Move the build and connector tags

git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc5.5.x/tags/TOMCAT_5_5_1@802189 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/build/.cvsignore b/build/.cvsignore
new file mode 100644
index 0000000..5aebb14
--- /dev/null
+++ b/build/.cvsignore
@@ -0,0 +1,7 @@
+build
+compat
+deployer
+embed
+dist
+release
+build.properties
\ No newline at end of file
diff --git a/build/BENCHMARKS.txt b/build/BENCHMARKS.txt
new file mode 100644
index 0000000..0b6f60e
--- /dev/null
+++ b/build/BENCHMARKS.txt
@@ -0,0 +1,38 @@
+
+Benchmarking Tomcat
+-------------------
+
+Tomcat benchmarks are more than welcome, and people who are interested are
+encouraged to post results.
+
+This guide is designed to be a FAQ on how to configure Tomcat for maximum
+performance.
+
+- Please upgrade to the latest available version of the Tomcat branch you
+  would like to benchmark. Small incremental performance improvements are often
+  made in each release.
+
+- Use the most recent JDK available whenever possible. Usually, results are
+  dependent on the JDK, so results obtained with different JDK should not be
+  compared directly.
+
+- When benchmarking Jasper, set it in production mode. To do that, add a
+  "development" init parameter (with value set to "false") in the Jasper
+  section of $CATALINA_HOME/conf/web.xml. This increases maximum throughtput
+  of JSPs by about 20%, as well as removing a synchronization point.
+
+- When benchmarking a web application which creates sessions on each request,
+  be careful about not exhausting memory by having too many active sessions. If
+  a max number of sessions is set in the manager to avoid memory problems, this
+  will lead to requests failing when session creation is denied. To avoid that,
+  either limit the total number of requests, or increase the JVM memory.
+
+- Do not enable access logging (unless that is required by the benchmark), 
+  or any debug log.
+
+- Please post the benchmark web application used along with the results, so
+  that people may attempt to reproduce the results.
+
+- Definition of a standard webapp workload may be desirable, and contibutors
+  interested in doing that are welcome to discuss it on the Tomcat developer
+  mailing list.
diff --git a/build/BUILDING.txt b/build/BUILDING.txt
new file mode 100644
index 0000000..e0d96de
--- /dev/null
+++ b/build/BUILDING.txt
@@ -0,0 +1,121 @@
+$Id$
+
+                 =============================================
+                 Building The Tomcat 5.5 Servlet/JSP Container
+                 =============================================
+
+This subproject contains the source code for Tomcat 5.5, a container that
+implements the Servlet 2.4 and JSP 2.0 specifications from the Java
+Community Process <http://www.jcp.org/>.  In order to build a binary
+distribution version of the container from a source distribution, 
+do the following:
+
+
+(0) Download and Install a Java Development Kit
+
+* If the JDK is already installed, skip to (1).
+
+* Download a Java Development Kit (JDK) release (version 1.4.x or later) from:
+
+    http://java.sun.com/j2se/
+
+* Install the JDK according to the instructions included with the release.
+
+* Set an environment variable JAVA_HOME to the pathname of the directory
+  into which you installed the JDK release.
+
+
+(1) Install Apache Ant 1.5.x on your computer
+
+* If Apache Ant 1.5.x is already installed on your computer, skip to (2).
+
+* Download a binary distribution of Ant 1.5.x from:
+
+    http://ant.apache.org/bindownload.cgi
+
+* Unpack the binary distribution into a convenient location so that the
+  Ant release resides in its own directory (conventionally named
+  "jakarta-ant-1.5.x").  For the purposes of the remainder of this document,
+  the symbolic name "${ant.home}" is used to refer to the full pathname of
+  the release directory.
+
+* Create an ANT_HOME environment variable to point the directory
+  ${ant.home}.
+
+* Modify the PATH environment variable to include the directory
+  ${ant.home}/bin in its list.  This makes the "ant" command line script
+  available, which will be used to actually perform the build.
+
+
+(2) Building Tomcat 5.5
+
+(2.1) Download main build script and build binary distribution
+
+* Download the main build.xml script from:
+  http://jakarta.apache.org/tomcat/tomcat-5.5-doc/build.xml
+
+* Create a new directory, and copy the newly download build.xml to it. This
+  directory will be referred to as the ${tomcat.source} directory in the rest
+  of this document
+
+(2.2) Building
+
+* Go to that directory, and do:
+
+    cd ${tomcat.source}
+    ant
+
+* NOTE: Users accessing the Internet through a proxy must use a properties
+  file to indicate to Ant the proxy configuration. Read below.
+
+* WARNING: Running this command will checkout the Tomcat 5 sources from CVS, as
+  well as download binaries to the /usr/share/java directory. Make sure this is
+  appropriate to do on your computer. On Windows, this usually corresponds
+  to the "C:\usr\share\java" directory, unless Cygwin is used. Read below to 
+  customize the directory used to download the binaries.
+
+* The build can be controlled by creating a ${tomcat.source}/build.properties
+  file, and adding the following content to it:
+
+    # ----- Proxy setup -----
+    # Uncomment if using a proxy server
+    #proxy.host=proxy.domain
+    #proxy.port=8080
+    #proxy.use=on
+
+    # ----- Default Base Path for Dependent Packages -----
+    # Replace this path with the directory path where dependencies binaries
+    # should be downloaded
+    base.path=/usr/share/java
+
+
+(3) Updating sources
+
+It is recommended that you regularly update the downloaded Tomcat 5 sources. 
+To do this, execute the following commands:
+
+    cd ${tomcat.source}
+    ant checkout
+
+
+(4) Rebuilds
+
+For a quick rebuild of only modified code you can use 
+   
+    cd ${tomcat.source}
+    ant build
+
+In addition, "ant build-depends" will build packages that 
+tomcat depends on ( commons-logging for now ), to ease fixes
+and debuging in those packages.
+
+(5) Building The "compat" Package
+
+Tomcat 5.5 is designed to run on J2SE 5.0, but will run on
+J2SE versions 1.3 and 1.4 as well as long as the compatability
+package is placed in the server classpath.  See "RUNNING.txt"
+in this directory for running instructions.  To build the
+compat package, do
+
+    cd ${tomcat.source}
+    ant build-compat
\ No newline at end of file
diff --git a/build/KEYS b/build/KEYS
new file mode 100644
index 0000000..ebe2d8d
--- /dev/null
+++ b/build/KEYS
@@ -0,0 +1,257 @@
+This file contains the PGP&GPG keys of various Apache developers.
+Please don't use them for email unless you have to. Their main
+purpose is code signing.
+
+Apache users: pgp < KEYS
+Apache developers: 
+        (pgpk -ll <your name> && pgpk -xa <your name>) >> this file.
+      or
+        (gpg --fingerprint --list-sigs <your name>
+             && gpg --armor --export <your name>) >> this file.
+
+Apache developers: please ensure that your key is also available via the
+PGP keyservers (such as pgpkeys.mit.edu).
+
+
+Type Bits/KeyID    Date       User ID
+pub  2048/F22C4FED 2001/07/02 Andy Armstrong <andy@tagish.com>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: PGPfreeware 7.0.3 for non-commercial use <http://www.pgp.com>
+
+mQGiBDtAWuURBADZ0KUEyUkSUiTA09e7tvEbX25STsjxrR+DNTainCls+XlkVOij
+gBv216lqge9tIsS0L6hCP4OQbFf/64qVtJssX4QXdyiZGb5wpmcj0Mz602Ew8r+N
+I0S5NvmogoYWW7BlP4r61jNxO5zrr03KaijM5r4ipJdLUxyOmM6P2jRPUwCg/5gm
+bpqiYl7pXX5FgDeB36tmD+UD/06iLqOnoiKO0vMbOk7URclhCObMNrHqxTxozMTS
+B9soYURbIeArei+plYo2n+1qB12ayybjhVu3uksXRdT9bEkyxMfslvLbIpDAG8Cz
+gNftTbKx/MVS7cQU0II8BKo2Akr+1FZah+sD4ovK8SfkMXUQUbTeefTntsAQKyyU
+9M9tA/9on9tBiHFl0qVJht6N4GiJ2G689v7rS2giLgKjetjiCduxBXEgvUSuyQID
+nF9ATrpXjITwsRlGKFmpZiFm5oCeCXihIVH0u6q066xNW2AXkLVoJ1l1Rs2Z0lsb
+0cq3xEAcwAmYLKQvCtgDV8CYgWKVmPi+49rSuQn7Lo9l02OUbLQgQW5keSBBcm1z
+dHJvbmcgPGFuZHlAdGFnaXNoLmNvbT6JAFgEEBECABgFAjtAWuUICwMJCAcCAQoC
+GQEFGwMAAAAACgkQajrT9PIsT+1plgCfXAovWnVL3MjrTfcGlFSKw7GHCSYAoJkz
+x+r2ANe8/0e+u5ZcYtSaSry+uQINBDtAWuUQCAD2Qle3CH8IF3KiutapQvMF6PlT
+ETlPtvFuuUs4INoBp1ajFOmPQFXz0AfGy0OplK33TGSGSfgMg71l6RfUodNQ+PVZ
+X9x2Uk89PY3bzpnhV5JZzf24rnRPxfx2vIPFRzBhznzJZv8V+bv9kV7HAarTW56N
+oKVyOtQa8L9GAFgr5fSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kj
+wEPwpVsYjY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obE
+AxnIByl6ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpMgs7AAIC
+B/0eHkYQ0Rv6s21TgpOzRBon+rQAv9ka0PlC7bj2eYWsCOBib8K7qO8hND0sW59p
+0uFQ01X7kC7L/4Ls1HTk0chEZMV0UrGAOKXHY1QFlxrNtFi5U3pTPITXDDfy+g/G
+6FTX3PLnGGvwXbtaiAq5UjQ6iXm03lh0BW6Q+kPtm8swPPfqfjYv0rrT+I8Ic3p2
+HplWKR2bpi3wqCSKB/AaTQJwTbh2x2+2cPVONPodgjZSJ9eQkErejkNSvqbumlTx
+dB81eoGa0Lo2xE7N+DNlCnILGE0X4hPMdj+N5fmyEbyx0WOB8crvCuODGGEQnXs/
+zbVO7FP+rj7YWjRh5pVD3bGiiQBMBBgRAgAMBQI7QFrlBRsMAAAAAAoJEGo60/Ty
+LE/tj/QAoOFNFa7rbAy+eT6mRNb7XztfcAbWAKD6Gd6S/7lEJU0k2TS5tozt4jMl
+vw==
+=/91Q
+-----END PGP PUBLIC KEY BLOCK-----
+
+Type Bits/KeyID    Date       User ID
+pub  1024D/86867BA6 2001-11-22 Jean-Frederic Clere (jfclere) <JFrederic.Clere@fjitsu-siemens.com>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.0.5 (GNU/Linux)
+Comment: For info see http://www.gnupg.org
+
+mQGiBDv9Gx8RBADclmKwDLcibNVipQnhYW+bFIpuQjQnRrqRwn3gXM+/luzzJYJ4
+bbWpw13zjX0EkrAJ8qH2A/d0EIU1eZ0zHrLgRvMUfLGFUX7FFFw18JKFLTVGhG4/
+8sSl3ydHSA2Kd1PF6xjBP7iM7sg5dJfEkyMzvK5H4F0ZpTqy3087wsg1wwCgitRy
+Zg4x3lWZSkOwBj472qaO9GkD/2q6kyWfAK6XFe3GuB5AAs3poMfN1eqW+duM4TA8
+zUiWK0Wxx4JXJbL7n0i4d+JdXJsrjSjF++KKfelcxsrSxoUIBegez25MUSvHe09D
+R3nqkY8CVO+viEtzRBqkSgCMbUjAtfkQ+vp2jDnWSmmkNfY0OYAzt+KRyJKcjUSJ
+gvOOA/45+DN9wuTELoFTvsXh1JgOL/QvW1fmQ2HrcQk94BkzIsfVGWClCiig5gNw
+LCxTbfgA5htpI8U7vPR9/5gH7U8Wy3HR6xQUZxcbttMeYit2VbDEJzF5r5S0pJvD
+vyk3n1kiKU7r49sjhxGgE8J/VvDpO6YcIsDs8LoULwuJTg0DTrRDSmVhbi1GcmVk
+ZXJpYyBDbGVyZSAoamZjbGVyZSkgPEpGcmVkZXJpYy5DbGVyZUBmdWppdHN1LXNp
+ZW1lbnMuY29tPohXBBMRAgAXBQI7/RsfBQsHCgMEAxUDAgMWAgECF4AACgkQ0+/m
+toaGe6amGQCeJU5VZ8QCi8+PY0QJHPA63e5uPyoAmgOWIwFm8A/xmW8qjEvVAWtb
+TjZxuQENBDv9GyMQBACCbFlSF+udW/Qz2oknDen8Hoql4Q1Q7CUQTbPjoQAcYgZg
+LrsR6hc9aCIf3Kt4qZBgQ1Oe9M/AemOFhU04UNp3dgHk91EYRvx80Rua992p/8V7
+QOhwIBVb2XE8as5nL2j8w6Jz7eSs/bivxm9yD0AH/I5H01RAJivRbOTsUgSkDwAD
+BQP5ARlW2Nqc0U17asQsmMYvT1UMiOiyBwUD/DIEG2Xy1hlEvdljg8WU26jcjpGq
+MrT69T4Z+eZ2oVyiRQTW4qMUBKc0Nbz89hL0qv9K41ExxxH+JgE1csRVvmwAT8Iy
+lnhof7TJLRBtvan3+p21Kxl1uQ7MbmLT875u+vc+J098fIiIRgQYEQIABgUCO/0b
+IwAKCRDT7+a2hoZ7pn9UAJ9f0TK0QQOtjQBvxAissopYhDKHGACePZg0k9sj69yw
+nVWrBS9fvFC9jcA=
+=BTiM
+-----END PGP PUBLIC KEY BLOCK-----
+
+Type Bits/KeyID    Date       User ID
+pub  1024D/E86E29AC 2002-02-13 kevin seguin <seguin@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.0.6 (MingW32)
+Comment: For info see http://www.gnupg.org
+
+mQGiBDxqtR8RBACbySxGrtf+flbowryS1Hj4z3zzEXD4CAEq6RjSGMtIraCDRJfp
+6Gexs+lQ6IhpdC4GfX70SUMjXXvT5suhXYeGOM4iJHqUsksgzEKjUqcfj1l3qmOs
+/doE8lcGGHcYbMplBcfuop+shZYiv9GEJ3gutwn/dNnhs/QA9bCdIj03lwCgvAcy
+QpT5JdTym2p2icd5e91mGIUEAJMw6JHTTcCiyoTRy7k8Cf65d8S7bTDLr6pqJVE2
+XU41CvW/pgL31akYAxpeZJJnsBaLaUiqh6K0qgfEMlDwDeC6gVogHBxWkEXdK1dr
+tGL4GIUcxQ1+ZvQhGg7dtjanmfMlylVgS+C48awJySkinRmaQDbQ0MKdFchLc/y1
+OR3IA/0VkIvlidehMPbZCalqhS9AEsDiFq5/u5AsQzDEp2nmTGlmBqjhc39kEnu4
+qKq08az1Gt6Q7sxXbjH/jYtDgd49FW5Yg4k5B3hpTgnbyRE6SGlKksu8qTmYkDve
+4rej6pvJRHwp6hDKxDG8qQoLWIgOfVC8960nurqx56QdV9YMsLQga2V2aW4gc2Vn
+dWluIDxzZWd1aW5AYXBhY2hlLm9yZz6IVwQTEQIAFwUCPGq1HwULBwoDBAMVAwID
+FgIBAheAAAoJEKy3f8Lobims3E0An0x3rrUMIijUMFoqnoT7muNGwmAzAJ990TWj
+dZO4ayh1M+cWhjaw9W+44bkBDQQ8arUkEAQApaMm5HUB1Yk2x5MavAs/O4zfWnOx
+YFOeXIPfGvhlhF2/Lrjs9icaa/tOM/CTCes19nDWP5Fc+pQxmgSPrgt3fsShwZJe
+p3iYodLbM76uXEgSvI4Wh6kwViHbN4V1GxJAd2ZPVb1v+lauGUCOgPFGw99UV9sO
+tTRXSbFS6AgqQzMAAwUD/jq6boxlnab/GUmKrILeLkv1X0G2/AEXEGRmG0nkhVdj
+OShoqtPr4y/UhMzJUOequs2CdvRlTIyAyZqN7A0Qp4mFfmsvp0dYYssTtE4bCzZe
+WxSKgjtBWBHXnH+Qzjb5R2Tz28kAxNY+dt7yxC+CkXWDZq/rsPgsXNbWXT49FnF8
+iEYEGBECAAYFAjxqtSQACgkQrLd/wuhuKazl7QCfQkz5t/3T6EtXZCcXz/hlswyI
+z30AoLr/7hwXgedEepBk/Gm9HUsbMnM8
+=S1mb
+-----END PGP PUBLIC KEY BLOCK-----
+
+
+Type bits      keyID      Date       User ID
+pub  1024D/307A10A5 2002-07-18 Henri Gomez *** RPM SIGNING KEY ***
+                                           <hgomez@users.sourceforge.net>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.0.6 (GNU/Linux)
+Comment: For info see http://www.gnupg.org
+
+mQGiBD02vbERBAC1v8fR6gjERpaz4UMfdy0hRVWCPSbOdF+Swm/IenjVzErco6zb
+MTa13umUNrDPBy/tTWiCCZrOnqi7fgDzWqPEqrXJjKAFVLEWE6MmKylPPEPG1/bm
+idkNGERSAZduvhKv777PzvEJJ/8eGe3wy/O8NbgIjCPtr4UklwCZS8cFuwCg8oMO
+UdT8qZRtzdxdAyu1m5fUb+MD/3IKJYWXsdtb6iBphCU4f/BoyjVC9EZJ1ywLuiVM
+siKbuaDUaXU9nWcbNKv+fx8uZ1NaadpfLokqqhnWcpnSiqw8HNR7SwsF1D33rkXK
+O4FSuVss/tIoqGdWFcJyPkP4yP5shxqR335narVw2vDa0+BiWkALbA2qVsSIdZDB
+LeFZA/47AMBS0U2BRk2rQT8LmMuFl7mR+wNBM4n7FUGdxsGn3TcYd4pXTNrEQPrV
+YNdooKlikgGk4hgFnIFX09Spmimqgq0goFue81rttVdZZ4uep8dTghY6gwmvcOxX
+jATbhWStBhdu9B35kzfHc+1QihD5Z94u4uyWIVBIzikcdiY8LbQqSGVucmkgR29t
+ZXogPGhnb21lekB1c2Vycy5zb3VyY2Vmb3JnZS5uZXQ+iFcEExECABcFAj02vbEF
+CwcKAwQDFQMCAxYCAQIXgAAKCRAZMdaEMHoQpYijAKCCP68ndU/kTXR9XAKLvibC
+3S8+1QCfUFQYte3Jo+MHKaWjsu9JGptRzo+5Ag0EPTa93RAIAKlsRJ5gOGTFsmaR
+W9k6MIh4c/MCy7J7HUxT5xTdHROa+3zUh+FAE/JaOx9ZtZtH863DFHA8cP4L+tpi
+PjBT6g2E94dwGcuH/OiSSCT4JSBukbGbOuLLdmFXqUl8+4gsL90Xal67FtNLwyLG
+1n7geLir0byD+OT7VLA5w+6G0NOpJEveV/FIa2qLgdRZ8vz73ybgMh18hBUrUmro
+jncp0rln2VU7VCH1C2aClKm7kK4mGAjIFIzKbguK+kM3b8NDHmXKpT6syyCtIM3h
+prkV1TUCAFqLI32aSdlTN79lpeA2zDga9k4/4X/RDHsFpRN2neRFGTNUtuUgYpQQ
+E5zWBmMAAwUH/RiGxyeBsad923IwE1+GAjxFl2tqF9xWk0J6yTnSK4nfhYAE9evV
+jwDEok9jRl4ILCcXx6YN/d/lWNuSbARKHz/3hLiTouPpwd3SSJ8is2x9PgpJz5JX
+cD0y1SkbPLvs3jH3ZmdcxZpuAmJeI/typqFKK5pWP44oXIH+XH/8nWDtmLEBkgKQ
+/ATQWenMTmZ6MIJ6aWKWGkO9QS6iYRz3PPPGQ1O8W02CeprM2wBtlb8J1Z3RxNhM
+rZcg/1Qi3V3D1HI4zw6tAFmDeBb8J4PaBQzqlhzx2EBTbfwNPhV8AlPvpxHEeGGn
+v+O1yhZr33SnyZdINNoNDn+owVMdmkobe9GIRgQYEQIABgUCPTa93QAKCRAZMdaE
+MHoQpRsTAJ4qst3MhLm48fBAEnzuzi/BIKr+AgCfYaCB/AvPoncQbHc8BcNGRimR
+P9A=
+=hQhz
+-----END PGP PUBLIC KEY BLOCK-----
+
+Type bits      keyID      Date       User ID
+DSS  3072/1024 0xA01422B5 2001/10/09 *** DEFAULT SIGNING KEY ***
+                                     Remy Maucherat <remm@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: PGP 6.5.8
+
+mQGiBDvDY+ERBADJ+GZ2SSlHMb0wRjfsHdJbb4zrRfHn4McEkYGUXyLtZJquAj3V
+8/JxrL3+wiBuXFQitf5qYXLDlRH+F1iFT17kqalFTvxZMA3Hg2kLB6ZUvIP8sTrD
+b8WJU18Lu2GwPMEUT1ito2eG3PDzgIVRwA0c5btLOhkwoRT+5bEl4ad8KwCg/0XV
+CEchMuZtOdJi6WOf8hfwF+cD/iTdfkuxGZEn6fK9erJb5GCr1v/0l83qKU36r8DX
+3kGpR6yac/2xv7+NEaKS1t9k/0seRsFQ/gWZA192HpsRhhe4C2lWmTh+YI7jS7mB
+Bw8dY304yhwPZuUYw86cLMpG5PXz9519kkDFGHlLG6MaxHnhfe0dXWzs/yv4uSsm
+35QfBACUx677GL4pEFJq2pP30aWhxc1but0ncLh5WDCael7wVuTGVSk8TorWoteM
+GcQkmDLo+my7xettXUTssijSn+NVRsFjcgMEpAWeJTBtfDRCyf312ECx7BM5G9oh
+HxtzMW4ZJYY4M1+3qklgpZaEYx1J8g8bIBv661vunbbcj1/OrLQgUmVteSBNYXVj
+aGVyYXQgPHJlbW1AYXBhY2hlLm9yZz6JAE4EEBECAA4FAjvDY+EECwMBAgIZAQAK
+CRB7QXnboBQitdfbAKD8zt0jeFHIlX6awuu9atxWyWaB3ACfb08JdEElpF7cTZhx
+yRpnKZDUvpa5Aw0EO8Nj5RAMAMwdd1ckOErixPDojhNnl06SE2H22+slDhf99pj3
+yHx5sHIdOHX79sFzxIMRJitDYMPj6NYK/aEoJguuqa6zZQ+iAFMBoHzWq6MSHvoP
+Ks4fdIRPyvMX86RA6dfSd7ZCLQI2wSbLaF6dfJgJCo1+Le3kXXn11JJPmxiO/Cqn
+S3wy9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV89AHxstD
+qZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50T8X8dryD
+xUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknbzSC0neSR
+BzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdXQ6MdGGze
+MyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbTCD1mpF1B
+n5x8vYlLIhkmuquiXsNV6UwybwACAgwAkwuQXCs1OPX398vydLxtt6AX9KreX4wm
+ykMG+hjgNUsUhYbi03fInYtqTd0TS9qtvSyFUP1aahoY7xKDECI8fyN20Xphb2ih
+fgipT6lBFH1tYX4cS1NHcQEdPU3b96Uat6cy7bma0/kUr3EZuxQlc6UVvsxD+lsT
+HsSqymY3om8Jx2yaDB58yK/VRUMs7/GPaqe/jQiHxbwyS7sWqhew7uNp+wJL5lEx
+x5PkbKj/eA2pKUpMrPKVJfK3E4nq5Q9feBgukhMCADxon2KYa4z4orU3EkM7I3sn
+wujj9dnCkoK2+P6/WvhNhR6q9fbdTHQqtDi4zSwip90m4CPWX/26RrEw2Lax1vC9
+LNJu5olQORxWvaDEoPFkU23kohGe9Qtxozqjn2Sya8VQoq1X5IMrQoCrf3MUWGrJ
++UzABENqSj4mM5XSGazkzTsx59hQMLEz/zKsFlQqTSaZ88VXNoRjj3Hm7b8KQeDE
+nTVtyjsrfoYjqPLSdhypI6HyNbUZ4mDJiQBGBBgRAgAGBQI7w2PlAAoJEHtBedug
+FCK1DkkAoJpMzhUJ2LrraHk7ALnqEeOlPnusAKCQ0vyLlJGktTxwzZ6yv2xwSj6O
+XQ==
+=1Jv0
+-----END PGP PUBLIC KEY BLOCK-----
+
+Type Bits/KeyID     Date       User ID
+pub  1024D/564C17A3 2003-01-11 Mladen Turk <mturk@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.0.6 (MingW32)
+
+mQGiBD4fwXIRBAC7WRS8PYxi2YH0T1mX4HCYsF8aHoqxBzMnyFR4J896m1s96vGM
+BTSAwH2NKbiVqtfLokTbQkUVxtrgrF2HMB5NfYBg/JzT7pZL/Q2ThWUS7SJQQA4f
+a7/DpiLiHalp6iX45om6JTdIWEyXv26csIVhmtlkGBEPRhNRX8X4//BM0wCg7wcA
+yQ7c5NmoOJLVs+uHsRrnHo0D/R/dMyuWt7/o0eGIEuRlDl2q+YL8xLuVyJMXQBnd
+jo7jKpQ+Q1zl93aVTzsJa7mP2zZ7jqaJ855sdz6rvwyhGF1/qYMtm6zrmgBy2XPm
+J+57sfwSZr0bhIeMpCWjIw98z9sObq0v2r2oA3+J9E3Na/BZsCVTZVb3ew7ILmEp
+F5D7A/4zvjY41dakCAJsD1Xo8TS6hSqJf4zq9vX3ayJVvUjeo8n4sHNOwcbEnnui
+9zZaUH3F0x+3cDo7mS1Y4pD8THuqCZoSbSkiHnlved6nLXsKbqvVrVo+esEhfZCn
+Iji3gp+2TVNwdHXGM+4BAzMJCLsdXjByO6SNzB9a+H8RsRlZKrQ8TWxhZGVuIFR1
+cmsgKCoqKiBERUZBVUxUIFNJR05JTkcgS0VZICoqKikgPG10dXJrQGFwYWNoZS5v
+cmc+iF0EExECAB0FAj4fwXIFCRLP94AFCwcKAwQDFQMCAxYCAQIXgAAKCRAcUGQH
+VkwXo0jxAKCgHzXPIB4IAgoD7GMAohPQfX7j2QCeL6pAsf4pPufmPvbrrpDp6rQH
+GOS5Ag0EPh/BhhAIAKWzq7+/+nNYGpc7sXGkDNo9xncxcx/KbbJVT0rBteuaonQ4
+vYar1ITjIhOPmF9yPmpUddNrqgQTSO+Or+ZrVOndn+qC1gdY3qpKIN3KTjXloW38
+0Y84ezwdRLznQNkhgXwNcB55l/Z9kLaW2MS8CJzOuYSQT1CYbXg7XP3684ZmV1KC
+cGgcUt9VkIGqwsa2RFDNGvMbySedSkJ/70Q+PJlkXN+W86f8hi3HTjw2MCkNa5NL
++Byg8FEAm95YWrO6kCY3qaJYV7NRt9oVd+2V/NNzwYp3Or/QoYofvfNerupfwBmU
+GEXPyZCqqNH6nDv6chscsWvEA9KzhsWnsdKhmHsAAwUH/R6LwfWgtpaO42dQI4ZS
+VRBmCeWrXCuyVk0d13Yz0xLi5Z5m4g3MON3d+cRVUiyNX+hbDGpi2mkbsnL559Ef
+iqmzDmSz5GQHDutolhOPtLxLrC537ODn2q7hnYQwIQYYIUtYD5sYlzfGYC8olGCB
+IcKIdlGRWcxxiFCIJm5CX/jnSBsyDRpanlSrdkxhzAGsifqj4NQ19ayoeNoZg2ZP
+9SLIY7vbmOxJeHEYkx8AG25xOY1PLotb/0buSXPB8e71zb/DCV1rAhhUxAr/2JOQ
+RqlZBq6PfcHKLRitXRCeVvfldRxuWBIzhuTLUfRPYR6phjP50EzZPlbJzIvGwsOI
+RheITAQYEQIADAUCPh/BhgUJEs/3gAAKCRAcUGQHVkwXoy0JAJ9WTfqfYzW/F6qi
+5MxmqDnU9/G+6ACfQVmhZNnGTSfcwQCttwCaW3CRhDY=
+=MWUr
+-----END PGP PUBLIC KEY BLOCK-----
+
+Type bits      keyID      Date       User ID
+DSS  3072/1024 0x7C037D42 2003/08/05 *** DEFAULT SIGNING KEY ***
+                                     Yoav Shapira <yoavs@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: PGP 6.5.8
+
+mQGiBD8u/mgRBAD5WKD5xF3CLrnABeS1DvQQhYH+tJjvAmyZgFkYwaQT7eiiOzLa
+PC5knbcBC4nuw+8OOPDFw0Ghb2MFogQzRxD6gpPH2t9eEUsrkPFax2Kw2vNTHRrQ
+RGAastmi+EYJsQAoktX2dPseTdrkeJBk240Bfj08ZUFg4uPuho9C45ND6QCg/6SO
+FMuan62QE+DwlUiMDo4ZcU0EAMDS8k6Dhb5m/0njO6w9OLTEyzohlsM9AP+4mfgB
+NOJYhrzfkFoElOcWSA/V3nmYn2VS0oIYDDtBnjXVWZidzTAWKsbT9/AepS3/P2tG
+KMhlXhas+uAiAbMpOglz8fdQ76ivQqyRdS99t4iy/cP2ZC3ShAqZQCacfWY5ZQ8Q
+kTILBADvp/eayw8fvtfWQXJ9EjBRbhO4THmP6z8J+4ypG6l0V/RBjDWZybrqibO9
+ejnOjQYJNCnfrfpzQ5l6dHyy86zLyg+bkFxeId4jp/IfDfJX90sGbuQahNYYwqTp
+SFiDMI3KN5ZhzhGnx+pKQh59pcux3HyKmcpPa4oB0CT828lWuLQfWW9hdiBTaGFw
+aXJhIDx5b2F2c0BhcGFjaGUub3JnPokATgQQEQIADgUCPy7+aAQLAwECAhkBAAoJ
+ECZhkcN8A31CGLkAoPRDGtLRwjkzS2F/OBPkRHKF9/atAKCIh3Fmcr2Cdn05P4qF
+kBe3QeWVt7kDDQQ/Lv5qEAwAzB13VyQ4SuLE8OiOE2eXTpITYfbb6yUOF/32mPfI
+fHmwch04dfv2wXPEgxEmK0Ngw+Po1gr9oSgmC66prrNlD6IAUwGgfNaroxIe+g8q
+zh90hE/K8xfzpEDp19J3tkItAjbBJstoXp18mAkKjX4t7eRdefXUkk+bGI78KqdL
+fDL2Qle3CH8IF3KiutapQvMF6PlTETlPtvFuuUs4INoBp1ajFOmPQFXz0AfGy0Op
+lK33TGSGSfgMg71l6RfUodNQ+PVZX9x2Uk89PY3bzpnhV5JZzf24rnRPxfx2vIPF
+RzBhznzJZv8V+bv9kV7HAarTW56NoKVyOtQa8L9GAFgr5fSI/VhOSdvNILSd5JEH
+NmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsYjY67VYy4XTjTNP18F1dDox0YbN4z
+ISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWakXUGf
+nHy9iUsiGSa6q6Jew1XpTDJvAAICC/4iJF383WNktP9/SxeGVIV74r7C7q5Cxr4a
+Liy7pEYs52DEft3JzTCLI5O4+NjOw+hEd3QiIytUJRW66V6zd50h4x9lBfK+eMYz
+GKNN7kd3aBmH/vXEsG9m9bK1ExwyWq4uyf76nRx1Ya9YthNWmxPUHQnSrOYNPU0/
+beA87ouZG4RL9tYqdu3NKJ4g/DYiaw+twvhSoCUkBEFHFfKLDlv8zyQvPTaPUSAM
+Ha5/G2Dj1D5RluMSCEMG1V8+YcYAFh63WEP7Afye0mR1LMJvmlba67ogh0ZSfR+I
+ju3lhJ9XOp/2W372F9ZbRJofgofVwHQV6INB5uX7KHAdXtPTss+l1nTmydLhsiPC
+5oh99ITPdOm8gRzrP10aFwCnwsqXvr+b7fX/CywpuCOQMIr4sbhbYTTClwDo6E0U
+TQ+Nb7PWE+8KuJuobTvMUqDQSQaQBnkpLcvRt3cPppANtkaADAeNf0RqKxxLlym4
+AltN8G8IMLtSJoH9xlQHTQA4tEUeKOeJAEYEGBECAAYFAj8u/moACgkQJmGRw3wD
+fUJh7ACdE7QuMkzSbxEzTXnbkS61AUPy06QAoI5b613vrWeqg5Gz9C7TzG+FEEoh
+=O17Z
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/build/LICENSE b/build/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/build/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/build/NOTICE b/build/NOTICE
new file mode 100644
index 0000000..3d55bef
--- /dev/null
+++ b/build/NOTICE
@@ -0,0 +1,18 @@
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
+Java Management Extensions (JMX) support is provided by
+the MX4J package, which is open source software.  The
+original software and related information is available
+at http://mx4j.sourceforge.net.
+
+The Windows Installer is built with the Nullsoft
+Scriptable Install Sysem (NSIS), which is
+open source software.  The original software and
+related information is available at
+http://nsis.sourceforge.net.
+
+Java compilation software for JSP pages is provided by Eclipse, 
+which is open source software.  The orginal software and 
+related infomation is available at
+http://www.eclipse.org.
\ No newline at end of file
diff --git a/build/RELEASE-NOTES b/build/RELEASE-NOTES
new file mode 100644
index 0000000..b9bc8ac
--- /dev/null
+++ b/build/RELEASE-NOTES
@@ -0,0 +1,189 @@
+
+
+                     Apache Tomcat Version @VERSION@
+                            Release Notes
+
+
+$Id$
+
+
+=============================
+KNOWN ISSUES IN THIS RELEASE:
+=============================
+
+* Dependency Changes
+* JNI Based Applications
+* Bundled APIs
+* Web application reloading and static fields in shared libraries
+* JAVAC leaking memory
+* Tomcat on Linux
+* Enabling SSI and CGI Support
+* Security manager URLs
+* Symlinking static resources
+* Enabling invoker servlet
+* Viewing the Tomcat Change Log
+* When all else fails
+
+
+===================
+Dependency Changes:
+===================
+Tomcat 5.5 is designed to run on J2SE 5.0 and later, and requires
+configuration to run on J2SE 1.4.  Make sure to read the "RUNNING.txt" 
+file in this directory if you are using J2SE 1.4.
+
+In addition, Tomcat 5.5 uses the Eclipse JDT Java compiler for compiling
+JSP pages.  This means you no longer need to have the complete
+Java Development Kit (JDK) to run Tomcat, but a Java Runtime Environment
+(JRE) is sufficient.  The Eclipse JDT Java compiler is bundled with the 
+binary Tomcat distributions.  Tomcat can also be configured to use the
+compiler from the JDK to compile JSPs, or any other Java compiler supported 
+by Apache Ant.
+
+
+=======================
+JNI Based Applications:
+=======================
+Applications that require native libraries must ensure that the libraries have
+been loaded prior to use.  Typically, this is done with a call like:
+
+  static {
+    System.loadLibrary("path-to-library-file");
+  }
+
+in some class.  However, the application must also ensure that the library is
+not loaded more than once.  If the above code were placed in a class inside
+the web application (i.e. under /WEB-INF/classes or /WEB-INF/lib), and the
+application were reloaded, the loadLibrary() call would be attempted a second
+time.
+
+To avoid this problem, place classes that load native libraries outside of the
+web application, and ensure that the loadLibrary() call is executed only once
+during the lifetime of a particular JVM.
+
+
+=============
+Bundled APIs:
+=============
+A standard installation of Tomcat 5.5 makes all of the following APIs available
+for use by web applications (by placing them in "common/lib" or "shared/lib"):
+* commons-el.jar (Commons Expression Language 1.0)
+* commons-logging-api.jar (Commons Logging API 1.0.3 or later)
+* jasper-compiler.jar (Jasper 2 Compiler)
+* jasper-compiler-jdt.jar (Eclipse JDT Java compiler)
+* jasper-runtime.jar (Jasper 2 Runtime)
+* jsp-api.jar (JSP 2.0 API)
+* commons-el.jar (JSP 2.0 Expression Language)
+* naming-common.jar (JNDI Context implementation)
+* naming-factory.jar (JNDI object factories for J2EE ENC support)
+* naming-factory-dbcp.jar (DataSource implementation based on commons-dbcp)
+* naming-resources.jar (JNDI DirContext implementations)
+* servlet-api.jar (Servlet 2.4 API)
+
+Installing the compatibility package will add the following to the list, which are
+needed when running on J2SE 1.4:
+* jmx.jar (Java Management Extensions API 1.2 or later)
+* xercesImpl.jar (Xerces XML Parser, version 2.6.2 or later)
+
+You can make additional APIs available to all of your web applications by
+putting unpacked classes into a "classes" directory (not created by default),
+or by placing them in JAR files in the "lib" directory.
+
+To override the XML parser implementation or interfaces, use the endorsed
+mechanism of the JVM. The default configuration defines JARs located in 
+"common/endorsed" as endorsed.
+
+
+================================================================
+Web application reloading and static fields in shared libraries:
+================================================================
+Some shared libraries (many are part of the JDK) keep references to objects
+instantiated by the web application. To avoid class loading related problems
+(ClassCastExceptions, messages indicating that the classloader
+is stopped, etc.), the shared libraries state should be reinitialized.
+
+Something which might help is to avoid putting classes which would be
+referenced by a shared static field in the web application classloader,
+and putting them in the shared classloader instead (JARs should be put in the
+"lib" folder, and classes should be put in the "classes" folder).
+
+
+=====================
+JAVAC leaking memory:
+=====================
+The Java compiler leaks memory each time a class is compiled. Web applications
+containing hundreds of JSP files may as a result trigger out of memory errors
+once a significant number of pages have been accessed. The memory can only be
+freed by stopping Tomcat and then restarting it.
+
+The JSP command line compiler (JSPC) can also be used to precompile the JSPs.
+
+Note: This issue has been fixed in Sun JDK 1.4.x.
+
+
+================
+Tomcat on Linux:
+================
+GLIBC 2.2 / Linux 2.4 users should define an environment variable:
+export LD_ASSUME_KERNEL=2.2.5
+
+Redhat Linux 9.0 users should use the following setting to avoid
+stability problems:
+export LD_ASSUME_KERNEL=2.4.1
+
+
+=============================
+Enabling SSI and CGI Support:
+=============================
+Because of the security risks associated with CGI and SSI available
+to web applications, these features are disabled by default.  
+
+To enable and configure CGI support, please see the cgi-howto.html page.
+
+To enable and configue SSI support, please see the ssi-howto.html page.
+
+
+======================
+Security manager URLs:
+======================
+In order to grant security permissions to JARs located inside the
+web application repository, use URLs of of the following format
+in your policy file:
+
+file:${catalina.home}/webapps/examples/WEB-INF/lib/driver.jar
+
+
+============================
+Symlinking static resources:
+============================
+By default, Unix symlinks will not work when used in a web application to link
+resources located outside the web application root directory.
+
+This behavior is optional, and the "allowLinking" flag may be used to disable
+the check.
+
+
+=========================
+Enabling invoker servlet:
+=========================
+Starting with Tomcat 4.1.12, the invoker servlet is no longer available by
+default in all webapps. Enabling it for all webapps is possible by editing
+$CATALINA_HOME/conf/web.xml to uncomment the "/servlet/*" servlet-mapping
+definition.
+
+Using the invoker servlet in a production environment is not recommended and
+is unsupported.  More details are available on the Tomcat FAQ at
+http://jakarta.apache.org/tomcat/faq/misc.html#invoker.
+
+
+==============================
+Viewing the Tomcat Change Log:
+==============================
+See changelog.html in this directory.
+
+
+====================
+When all else fails:
+====================
+See the FAQ
+http://jakarta.apache.org/tomcat/faq/
diff --git a/build/RELEASE-PLAN-5.0.txt b/build/RELEASE-PLAN-5.0.txt
new file mode 100644
index 0000000..490018c
--- /dev/null
+++ b/build/RELEASE-PLAN-5.0.txt
@@ -0,0 +1,70 @@
+$Id$
+
+                      Release Plan for Apache Tomcat 5.0
+                      ==================================
+
+
+Introduction:
+------------
+
+This document is a release plan for the final release of Apache Tomcat 5.0.
+
+The goal of the Apache Tomcat 5.0 final release is to provide a stable
+container that supports 100% of the mandatory requirements of the Servlet 2.4
+and JSP 2.0 specifications, as well as to improve and add many useful 
+additional features on top of the existing Apache Tomcat 4.1.x releases.
+
+Apache Tomcat 5.0 includes the following major new features over 
+Apache Tomcat 4.1:
+
+    * Performance optimizations and reduced garbage collection
+    * Refactored application deployer, with an optional standalone deployer 
+      allowing validation and compilation of a web application before putting 
+      it in production
+    * Complete server monitoring using JMX and the manager web application
+    * Scalability and reliability enhancements
+    * Improved Taglibs handling, including advanced pooling and tag plugins
+    * Improved platform integration, with native Windows and Unix wrappers
+    * Embedding of Tomcat using JMX
+    * Expanded documentation
+
+Apache Tomcat 5.0 will use the build numbering and release process first used 
+in the Apache HTTPd 2.0.x project.
+Milestone builds, numbered 5.0.x, will be released as needed and will 
+recieve a stability rating after a one week testing period. The rating can be
+either: Alpha, Beta, or Stable.
+
+This Release Plan proposes the following prospective target dates 
+for stability:
+
+  Middle of August, 2003
+  ----------------------
+
+Tomcat 5.0 should reach Beta status by this date.
+
+  Servlet 2.4 and JSP 2.0 Final Specification Releases (expected by 09/2003)
+  ----------------------------------------------------
+
+At least another Beta rated release should be made simultaneous to the release
+of the specification.
+
+  Two to four weeks after Final Specification Releases
+  ----------------------------------------------------
+
+Best effort should be made for Tomcat 5.0 to reach Stable status as soon as
+possible after the specification releases. However, it should be pointed out 
+that wide testing cannot occur before the specifications are released, so
+a Stable release should only be made after a period of time.
+
+In order to complete a first Stable release, all outstanding Bugzilla bug 
+reports against Tomcat 5.0 above NORMAL severity need to be fixed or deferred 
+for later releases.
+
+This Release Plan proposes the following classification of current outstanding
+bug reports in the bug tracking system, sorted by component and their ID
+numbers in our bug tracking system at:
+
+http://nagoya.apache.org/bugzilla/
+
+Please review the bug reports, and their severity accordingly. 
+
diff --git a/build/RELEASE-PLAN-5.5.txt b/build/RELEASE-PLAN-5.5.txt
new file mode 100644
index 0000000..2b2365b
--- /dev/null
+++ b/build/RELEASE-PLAN-5.5.txt
@@ -0,0 +1,59 @@
+$Id$
+
+                      Release Plan for Apache Tomcat 5.5
+                      ==================================
+
+
+Introduction:
+------------
+
+This document is a release plan for the final release of Apache Tomcat 5.5.
+
+The goal of the Apache Tomcat 5.5 final release is to provide a stable
+container that supports 100% of the mandatory requirements of the Servlet 2.4
+and JSP 2.0 specifications, as well as to improve and add many useful 
+additional features on top of the existing Apache Tomcat 5.0.x releases.
+
+Apache Tomcat 5.5 includes the following major new features over 
+Apache Tomcat 5.0:
+
+    * Performance optimizations and startup time improvements
+    * Refactored application deployer
+    * Faster JSP compiler with in memory loading of depedencies, powered by
+      Eclipse JDT
+    * Dependencies repackaging
+    * Streamlined configuration (datasources and web application defaults)
+    * Support for J2SE JRE 5.0
+    * New native connector for Apache 2, based on mod_proxy
+
+Apache Tomcat 5.5 will use the build numbering and release process first used 
+in the Apache HTTPd 2.0.x project.
+Milestone builds, numbered 5.5.x, will be released as needed and will 
+recieve a stability rating after a one week testing period. The rating can be
+either: Alpha, Beta, or Stable.
+
+This Release Plan proposes the following prospective target dates 
+for stability:
+
+  August 30, 2004
+  ---------------
+
+Tomcat 5.5.0 should be tagged by this date.
+
+  End of September, 2003
+  ----------------------
+
+Tomcat 5.5 should reach beta status by then.
+
+In order to complete a first Stable release, all outstanding Bugzilla bug 
+reports against Tomcat 5.5 above NORMAL severity need to be fixed or deferred 
+for later releases.
+
+This Release Plan proposes the following classification of current outstanding
+bug reports in the bug tracking system, sorted by component and their ID
+numbers in our bug tracking system at:
+
+http://nagoya.apache.org/bugzilla/
+
+Please review the bug reports, and their severity accordingly. 
+
diff --git a/build/RUNNING.txt b/build/RUNNING.txt
new file mode 100644
index 0000000..af75a39
--- /dev/null
+++ b/build/RUNNING.txt
@@ -0,0 +1,182 @@
+$Id$
+
+                 ============================================
+                 Running The Tomcat 5.5 Servlet/JSP Container
+                 ============================================
+
+Out of the box, Tomcat 5.5 requires the Java 2 Standard Edition Runtime
+Environment (JRE) version 5.0 or later.  However, you can also run Tomcat
+5.5 on earlier versions of the JRE, as detailed below.
+
+=============================
+Running With JRE 5.0 Or Later
+=============================
+
+(1) Download and Install the J2SE Runtime Environment (JRE)
+
+(1.1) Download the Java 2 Standard Edition Runtime Environment (JRE),
+      release version 5.0 or later, from http://java.sun.com/j2se.
+
+(1.2) Install the JRE according to the instructions included with the
+      release.
+
+(1.3) Set an environment variable named JAVA_HOME to the pathname of
+      the directory into which you installed the JRE, e.g. c:\j2sdk5.0
+      or /usr/local/java/j2sdk5.0.
+
+
+(2) Download and Install the Tomcat Binary Distribution
+
+NOTE:  As an alternative to downloading a binary distribution, you can create
+your own from the Tomcat source repository, as described in "BUILDING.txt".
+If you do this, the value to use for "${catalina.home}" will be the "dist"
+subdirectory of your source distribution.
+
+(2.1) Download a binary distribution of Tomcat from:
+
+      http://jakarta.apache.org/site/binindex.cgi
+
+(2.2) Unpack the binary distribution into a convenient location so that the
+      distribution resides in its own directory (conventionally named
+      "jakarta-tomcat-5").  For the purposes of the remainder of this document,
+      the symbolic name "$CATALINA_HOME" is used to refer to the full
+      pathname of the release directory.
+
+
+(3) Start Up Tomcat
+
+(3.1) Tomcat can be started by executing the following commands:
+
+      $CATALINA_HOME\bin\startup.bat          (Windows)
+
+      $CATALINA_HOME/bin/startup.sh           (Unix)
+
+(3.2) After startup, the default web applications included with Tomcat will be
+      available by visiting:
+
+      http://localhost:8080/
+
+(3.3) Further information about configuring and running Tomcat can be found in
+      the documentation included here, as well as on the Tomcat web site:
+
+      http://jakarta.apache.org/tomcat/
+
+
+(4) Shut Down Tomcat
+
+(4.1) Tomcat can be shut down by executing the following command:
+
+      $CATALINA_HOME\bin\shutdown            (Windows)
+
+      $CATALINA_HOME/bin/shutdown.sh         (Unix)
+
+
+
+===========================================
+Running Tomcat With J2SE Version 1.4 or 1.3
+===========================================
+
+(1) Obtain the compat package:
+
+(1.1) Download the compat package from the binary download site:
+      http://jakarta.apache.org/site/binindex.cgi
+
+      * Or build this package yourself from the source code: see 
+        "BUILDING.txt" in this directory.
+
+(2) Unzip the package in $CATALINA_HOME.  It will place the XML
+    parser APIs and Xerces implementation in the common/endorsed
+    directory, and the JMX API jar (jmx.jar from Sun) in the bin
+    directory.
+
+(3) Follow the same directions for starting and stopping the
+    server as if you were using J2SE 5.0.
+
+
+====================================================
+Advanced Configuration - Multiple Tomcat Instances
+====================================================
+
+In many circumstances, it is desirable to have a single copy of a Tomcat
+binary distribution shared among multiple users on the same server.  To make
+this possible, you can pass a "-Dcatalina.base=$CATALINA_BASE" argument when
+executing the startup command (see (2)). In this
+"-Dcatalina.base=$CATALINA_BASE" argument, replace $CATALINA_BASE with the
+directory that contains the files for your 'personal' Tomcat instance.
+
+When you use this "-Dcatalina.base=$CATALINA_BASE" argument, Tomcat will
+calculate all relative references for files in the following directories based
+on the value of $CATALINA_BASE instead of $CATALINA_HOME:
+
+* conf - Server configuration files (including server.xml)
+
+* logs - Log and output files
+
+* shared - For classes and resources that must be shared across all web
+           applications
+
+* webapps - Automatically loaded web applications
+
+* work - Temporary working directories for web applications
+
+* temp - Directory used by the JVM for temporary files (java.io.tmpdir)
+
+If you do not pass the "-Dcatalina.base=$CATALINA_BASE" argument to the
+startup command, $CATALINA_BASE will default to the same value as $CATALINA_HOME,
+ which means that the same directory is used for all relative path resolutions.
+
+The administration and manager web applications, which are defined in the
+$CATALINA_BASE/conf/Catalina/localhost/admin.xml
+and 
+$CATALINA_BASE/conf/Catalina/localhost/manager.xml files, will
+not run in that configuration, unless either:
+- The path specified in the docBase attribute of the Context element is made
+  absolute, and replaced respectively by $CATALINA_HOME/server/webapps/admin
+  and $CATALINA_HOME/server/webapps/manager
+- Both web applications are copied or moved to $CATALINA_BASE, 
+  and the path specified in the docBase attribute of the Context
+  element is modified appropriately.
+- Both web applications are disabled by removing
+  $CATALINA_BASE/conf/Catalina/localhost/admin.xml
+  and
+  $CATALINA_BASE/conf/Catalina/localhost/manager.xml.
+
+
+================
+Troubleshooting
+================
+
+There are only really 3 things likely to go wrong during the stand-alone
+Tomcat install:
+
+(1) The most common hiccup is when another web server (or any process for that
+    matter) has laid claim to port 8080.  This is the default HTTP port that
+    Tomcat attempts to bind to at startup.  To change this, open the file:
+
+       $CATALINA_HOME/conf/server.xml
+
+    and search for '8080'.  Change it to a port that isn't in use, and is
+    greater than 1024, as ports less than or equal to 1024 require superuser
+    access to bind under UNIX.
+
+   Restart Tomcat and you're in business.  Be sure that you replace the "8080"
+   in the URL you're using to access Tomcat.  For example, if you change the
+   port to 1977, you would request the URL http://localhost:1977/ in your browser.
+
+(2) An "out of environment space" error when running the batch files in
+    Windows 95, 98, or ME operating systems.
+
+    Right-click on the STARTUP.BAT and SHUTDOWN.BAT files.  Click on
+    "Properties", then on the "Memory" tab.  For the "Initial environment" field,
+    enter in something like 4096.
+
+    After you click apply, Windows will create shortcuts which you can use
+    to start and stop the container.
+
+(3) The 'localhost' machine isn't found.  This could happen if you're behind a
+    proxy.  If that's the case, make sure the proxy configuration for your
+    browser knows that you shouldn't be going through the proxy to access the
+    "localhost".
+
+    In Netscape, this is under Edit/Preferences -> Advanced/Proxies, and in
+    Internet Explorer, Tools -> Internet Options -> Connections -> LAN Settings.
diff --git a/build/build.properties.default b/build/build.properties.default
new file mode 100644
index 0000000..8df0761
--- /dev/null
+++ b/build/build.properties.default
@@ -0,0 +1,287 @@
+# -----------------------------------------------------------------------------
+# build.properties.sample
+#
+# This is an example "build.properties" file, used to customize building Tomcat
+# for your local environment.  It defines the location of all external
+# modules that Tomcat depends on.  Copy this file to "build.properties"
+# in the top-level source directory, and customize it as needed.
+#
+# $Id$
+# -----------------------------------------------------------------------------
+
+
+# ----- Compile Control Flags -----
+compile.debug=on
+compile.deprecation=off
+compile.optimize=off
+
+
+# ----- Build Control Flags
+
+#Build all components
+#full.dist=on
+
+#Hide configuration flags display
+#flags.hide=on
+
+
+# ----- Proxy setup -----
+#proxy.host=proxy.domain
+#proxy.port=8080
+#proxy.use=on
+
+
+# ----- CVS root for the jakarta repositories ------
+cvsroot=":pserver:anoncvs@cvs.apache.org:/home/cvspublic"
+
+# ----- Default Base Path for Dependent Packages -----
+base.path=/usr/share/java
+#base.path=../repository
+#base.path=/usr/local
+
+# ----- Jakarta files base location -----
+base-jakarta.loc=http://archive.apache.org/dist/jakarta
+
+# ----- XML files base location -----
+base-xml.loc=http://archive.apache.org/dist/xml
+
+# ----- Sourceforge files base location -----
+base-sf.loc=http://telia.dl.sourceforge.net/sourceforge
+
+# --------------------------------------------------
+#                REQUIRED LIBRARIES
+# --------------------------------------------------
+
+
+# ----- Commons Beanutils, version 1.4 or later -----
+commons-beanutils.home=${base.path}/commons-beanutils-1.7.0
+commons-beanutils.lib=${commons-beanutils.home}
+commons-beanutils.jar=${commons-beanutils.lib}/commons-beanutils.jar
+commons-beanutils.loc=${base-jakarta.loc}/commons/beanutils/binaries/commons-beanutils-1.7.0.tar.gz
+
+
+# ----- Commons Launcher, version 0.9 or later -----
+commons-launcher.home=${base.path}/commons-launcher-0.9
+commons-launcher.lib=${commons-launcher.home}
+commons-launcher.bin=${commons-launcher.home}/bin
+commons-launcher.jar=${commons-launcher.bin}/commons-launcher.jar
+commons-launcher.bootstrap.class=${commons-launcher.bin}/LauncherBootstrap.class
+commons-launcher.loc=${base-jakarta.loc}/commons/launcher/binaries/launcher-0.9.tar.gz
+
+
+# ----- Commons Daemon, version 1.0-Alpha or later -----
+commons-daemon.home=${base.path}/commons-daemon-1.0
+commons-daemon.lib=${commons-daemon.home}
+commons-daemon.jar=${commons-daemon.lib}/commons-daemon.jar
+commons-daemon.loc=${base-jakarta.loc}/commons/daemon/binaries/commons-daemon-1.0.tar.gz
+commons-daemon.jsvc.tar.gz=${commons-daemon.lib}/bin/jsvc.tar.gz
+
+
+# ----- Commons Digester, version 1.4 or later -----
+commons-digester.home=${base.path}/commons-digester-1.5
+commons-digester.lib=${commons-digester.home}
+commons-digester.jar=${commons-digester.lib}/commons-digester.jar
+commons-digester.loc=${base-jakarta.loc}/commons/digester/binaries/commons-digester-1.5.tar.gz
+
+
+# ----- Commons Expression Language (EL), version 1.0 or later -----
+commons-el.home=${base.path}/commons-el-1.0
+commons-el.lib=${commons-el.home}
+commons-el.jar=${commons-el.lib}/commons-el.jar
+commons-el.loc=${base-jakarta.loc}/commons/el/binaries/commons-el-1.0.tar.gz
+
+
+# ----- Commons Logging, version 1.0.1 or later -----
+commons-logging.home=${base.path}/commons-logging-1.0.4
+commons-logging.lib=${commons-logging.home}
+commons-logging-api.jar=${commons-logging.lib}/commons-logging-api.jar
+commons-logging.jar=${commons-logging.lib}/commons-logging.jar
+commons-logging.loc=${base-jakarta.loc}/commons/logging/binaries/commons-logging-1.0.4.tar.gz
+
+
+# ----- Commons Modeler, version 1.1 or later -----
+commons-modeler.home=${base.path}/commons-modeler-1.1
+#commons-modeler.lib=${commons-modeler.home}
+commons-modeler.lib=${commons-modeler.home}
+commons-modeler.jar=${commons-modeler.lib}/commons-modeler.jar
+commons-modeler.loc=${base-jakarta.loc}/commons/modeler/binaries/modeler-1.1.tar.gz
+
+
+# ----- Java Naming and Directory Interface (JNDI), version 1.2 or later -----
+# Note: Optional with JDK 1.3+
+jndi.home=${base.path}/jndi-1.2.1
+jndi.lib=${jndi.home}/lib
+jndi.jar=${jndi.lib}/jndi.jar
+ldap.jar=${jndi.lib}/ldap.jar
+jaas.jar=${jndi.lib}/jaas.jar
+
+
+# ----- Xerces XML Parser, version 2.6.2 -----
+xerces.home=${base.path}/xerces-2_6_2
+xerces.lib=${xerces.home}
+xercesImpl.jar=${xerces.lib}/xercesImpl.jar
+xml-apis.jar=${xerces.lib}/xml-apis.jar
+xerces.loc=${base-xml.loc}/xerces-j/binaries/Xerces-J-bin.2.6.2.tar.gz
+
+
+# ----- Eclipse JDT, version 3.0 or later -----
+jdt.home=${base.path}/eclipse/plugins/org.eclipse.jdt.core_3.0.0
+jdt.lib=${jdt.home}
+jdt.jar=${jdt.lib}/jdtcore.jar
+jdt.loc=http://sunsite.informatik.rwth-aachen.de/eclipse/downloads/drops/R-3.0-200406251208/eclipse-JDT-3.0.zip
+
+
+# --------------------------------------------------
+#              CORE OPTIONAL LIBRARIES
+# --------------------------------------------------
+
+
+# ----- Log4j, version 1.2 or later -----
+log4j.home=${base.path}/jakarta-log4j-1.2.8
+log4j.lib=${log4j.home}
+log4j.jar=${log4j.lib}/dist/lib/log4j-1.2.8.jar
+log4j.loc=${base-jakarta.loc}/log4j/binaries/jakarta-log4j-1.2.8.tar.gz
+
+
+# ----- Commons DBCP, version 1.1 or later -----
+commons-dbcp.version=1.2.1
+commons-dbcp.home=${base.path}/commons-dbcp-1.2.1
+commons-dbcp-src.loc=${base-jakarta.loc}/commons/dbcp/source/commons-dbcp-1.2.1-src.tar.gz
+
+
+# ----- Commons HttpClient, version 2.0 or later -----
+commons-httpclient.home=${base.path}/commons-httpclient-2.0
+commons-httpclient.lib=${commons-httpclient.home}
+commons-httpclient.jar=${commons-httpclient.lib}/commons-httpclient-2.0.jar
+commons-httpclient.loc=${base-jakarta.loc}/commons/httpclient/binary/commons-httpclient-2.0.tar.gz
+
+
+# ----- Commons Pool, version 1.1 or later -----
+commons-pool.home=${base.path}/commons-pool-1.2
+commons-pool-src.loc=${base-jakarta.loc}/commons/pool/source/commons-pool-1.2-src.tar.gz
+
+
+# ----- Commons Collections, version 2.0 or later -----
+commons-collections.home=${base.path}/commons-collections-3.1
+commons-collections.lib=${commons-collections.home}
+commons-collections.jar=${commons-collections.lib}/commons-collections-3.1.jar
+commons-collections.loc=${base-jakarta.loc}/commons/collections/binaries/commons-collections-3.1.tar.gz
+commons-collections-src.loc=${base-jakarta.loc}/commons/collections/source/commons-collections-3.1-src.tar.gz
+
+
+# ----- Commons FileUpload, version 1.0-20030531 or later -----
+commons-fileupload.home=${base.path}/commons-fileupload-1.0
+commons-fileupload.lib=${commons-fileupload.home}
+commons-fileupload.jar=${commons-fileupload.lib}/commons-fileupload-1.0.jar
+commons-fileupload.loc=${base-jakarta.loc}/commons/fileupload/binaries/commons-fileupload-1.0.tar.gz
+
+# ----- Java Management Extensions (JMX), JMX RI 1.2.1 or later or MX4J 2.0.1 or later -----
+jmx.home=${base.path}/mx4j-2.0.1
+jmx.lib=${jmx.home}/lib
+jmx.jar=${jmx.lib}/mx4j.jar
+jmx-tools.jar=${jmx.lib}/mx4j-tools.jar
+jmx.loc=${base-sf.loc}/mx4j/mx4j-2.0.1.zip
+
+
+# ----- JUnit Unit Test Suite, version 3.7 or later -----
+junit.home=${base.path}/junit3.8.1
+junit.lib=${junit.home}
+junit.jar=${junit.lib}/junit.jar
+junit.loc=${base-sf.loc}/junit/junit3.8.1.zip
+
+
+# ----- NSIS, version 2.0 or later -----
+nsis.home=${base.path}/nsis-2.0
+nsis.exe=${nsis.home}/makensis.exe
+nsis.installoptions.dll=${nsis.home}/Plugins/InstallOptions.dll
+nsis.nsexec.dll=${nsis.home}/Plugins/nsExec.dll
+nsis.loc=${base-sf.loc}/nsis/nsis20.exe
+
+
+# ----- Struts, version 1.1 or later -----
+struts.home=${base.path}/jakarta-struts-1.1
+struts.lib=${struts.home}/lib
+struts.jar=${struts.lib}/struts.jar
+struts.loc=${base-jakarta.loc}/struts/binaries/jakarta-struts-1.1.tar.gz
+
+
+# --------------------------------------------------
+#                OPTIONAL LIBRARIES
+# --------------------------------------------------
+
+
+# ----- Java Activation Framework (JAF), version 1.0.1 or later -----
+activation.home=${base.path}/jaf-1.0.1
+activation.lib=${activation.home}
+activation.jar=${activation.lib}/activation.jar
+
+
+# ----- Jaxen ( required by taglibs/standard required by jasper ) -----
+jaxen.home=${base.path}/jaxen-1.0-FCS
+jaxen.jar=${jaxen.home}/jaxen-full.jar
+jaxen.loc=${base-sf.loc}/jaxen/jaxen-1.0-FCS.tar.gz
+
+
+# ----- Saxpath ( required by taglibs/standard required by jasper ) -----
+saxpath.home=${base.path}/saxpath-1.0-FCS
+saxpath.jar=${saxpath.home}/saxpath.jar
+saxpath.loc=${base-sf.loc}/saxpath/saxpath-1.0.tar.gz
+
+
+# ----- Java Database Connectivity (JDBC) Optional Package, version 2.0 -----
+jdbc20ext.home=${base.path}/jdbc2_0-stdext
+jdbc20ext.lib=${jdbc20ext.home}
+jdbc20ext.jar=${jdbc20ext.lib}/jdbc2_0-stdext.jar
+
+
+# ----- Java Secure Sockets Extension (JSSE), version 1.0.3 or later -----
+jsse.home=${base.path}/jsse-1.0.3
+jsse.lib=${jsse.home}/lib
+jcert.jar=${jsse.lib}/jcert.jar
+jnet.jar=${jsse.lib}/jnet.jar
+jsse.jar=${jsse.lib}/jsse.jar
+
+
+# ----- Java Transaction API (JTA), version 1.0.1a or later -----
+jta.home=${base.path}/jta-1_0_1a
+jta.lib=${jta.home}
+jta.jar=${jta.lib}/jta.jar
+
+
+# ----- Java Mail, version 1.2 or later -----
+mail.home=${base.path}/javamail-1.3.1
+mail.lib=${mail.home}
+mail.jar=${mail.lib}/mail.jar
+
+# ----- Java Activation Framework, version 1.0.1 or later -----
+activation.home=${base.path}/jaf-1.0.2
+activation.lib=${activation.home}
+activation.jar=${activation.lib}/activation.jar
+
+# ----- PureTLS Extension, version 0.9 or later -----
+puretls.home=${base.path}/puretls-0.9b4
+puretls.lib=${puretls.home}/build
+puretls.jar=${puretls.lib}/puretls.jar
+
+
+# ----- Servlet 2.3, version 20020801 or later -----
+servlet23api.home=${base.path}/servletapi-4
+servlet23api.lib=${servlet23api.home}/lib
+servlet23api.jar=${servlet23api.lib}/servlet23api.jar
+servlet23api.loc=jakarta-servletapi-4
+
+servlet-api.home=${base.path}/servlet-api-2.4
+servlet-api.lib=${servlet-api.home}/lib
+servlet-api.jar=${servlet-api.lib}/servlet-api.jar
+
+jsp-api.home=${base.path}/jsp-api-2.0
+jsp-api.lib=${jsp-api.home}/lib
+jsp-api.jar=${jsp-api.lib}/jsp-api.jar
+
+
+# ----- Watchdog, version 20020801 or later -----
+watchdog.home=${base.path}/jakarta-watchdog-4.0
+watchdog.webapps=${watchdog.home}/dist/webapps
+watchdog.war=${watchdog.webapps}/servlet-tests.war
+watchdog.loc=jakarta-watchdog-4.0
+watchdog.target=all
diff --git a/build/build.xml b/build/build.xml
new file mode 100644
index 0000000..07c2646
--- /dev/null
+++ b/build/build.xml
@@ -0,0 +1,2115 @@
+<project name="Tomcat 5.5" default="deploy" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <!-- See "build.properties.sample" in the top level directory for all     -->
+  <!-- property values you must customize for successful building!!!        -->
+  <property file="${user.home}/build.properties"/>
+  <property file="build.properties"/>
+  
+  <property file="build.properties.default"/>
+
+  <!-- Project Properties -->
+  <property name="name"                  value="Apache Tomcat" />
+  <property name="year"                  value="2004" />
+  <property name="version.major"         value="5" />
+  <property name="version.minor"         value="5" />
+  <!-- When releasing set the 'version.build' to even number
+        and 'version.isdev' to zero.
+  -->
+  <property name="version.build"         value="1" />
+  <property name="version.patch"         value="0" />
+  <property name="version.isdev"         value="1" />
+
+  <condition property="isDevVersion"     value="-dev">
+    <not>
+      <equals arg1="${version.isdev}" arg2="0"/>
+    </not>
+  </condition>
+  <condition property="isPatchedVersion" value=".${version.patch}">
+    <not>
+      <equals arg1="${version.patch}" arg2="0"/>
+    </not>
+  </condition>
+  <!-- Second definition if not already conditionaly set -->
+  <property name="isDevVersion"          value="" />
+  <property name="isPatchedVersion"      value="" />
+  
+  <property name="version"               value="${version.major}.${version.minor}.${version.build}${isPatchedVersion}${isDevVersion}" />
+  <property name="version.number"        value="${version.major}.${version.minor}.${version.build}.${version.patch}" />
+
+  <property name="project"               value="jakarta-tomcat" />
+  <property name="final.name"            value="${project}-${version}" />
+  <property name="final-src.name"        value="${project}-${version}-src" />
+
+  <!-- Subprojects -->
+  <property name="api.project"           value="jakarta-servletapi-5" />
+  <property name="tomcat.project"        value="jakarta-tomcat-5" />
+  <property name="catalina.project"      value="jakarta-tomcat-catalina" />
+  <property name="jtc.project"           value="jakarta-tomcat-connectors" />
+  <property name="jasper.project"        value="jakarta-tomcat-jasper" />
+  <property name="ant.jar"               value="${ant.home}/lib/ant.jar"/>
+  <property name="ant-launcher.jar"      value="${ant.home}/lib/ant-launcher.jar"/>
+  <property name="cvstag"                value="" />
+  <property name="cvs.base"
+           value="${basedir}/.."/>
+
+  <!-- Source dependencies -->
+  <property name="api.home"
+           value="${basedir}/../${api.project}"/>
+  <property name="catalina.home" 
+           value="${basedir}/../${catalina.project}"/>
+  <property name="jasper.home"
+           value="${basedir}/../${jasper.project}/jasper2"/>
+  <property name="jtc.home"
+           value="${basedir}/../${jtc.project}"/>
+  <property name="tomcat.home"
+           value="${basedir}/../${tomcat.project}"/>
+
+  <!-- Build Defaults -->
+  <property name="catalina.build"   value="${catalina.home}/build"/>
+  <property name="jasper.build"     value="${jasper.home}/build"/>
+  <property name="tomcat.build"     value="${basedir}/build"/>
+  <property name="build.home"       value="${tomcat.build}"/>
+
+  <property name="tomcat.dist"      value="${basedir}/dist"/>
+  <property name="tomcat.embed"     value="${basedir}/embed"/>
+  <property name="tomcat.compat"    value="${basedir}/compat"/>
+  <property name="tomcat.deployer"  value="${basedir}/deployer"/>
+  <property name="tomcat.release"   value="${basedir}/release"/>
+  <property name="webapps.build"    value="${catalina.home}/webapps/build"/>
+  <property name="webapps.dist"     value="${catalina.home}/webapps/dist"/>
+  <property name="tomcat-dbcp.home" value="${base.path}/tomcat-deps" />
+  <property name="tomcat-dbcp.jar" 
+       value="${tomcat-dbcp.home}/naming-factory-dbcp.jar"/>
+  <property name="jasper-compiler-jdt.home" value="${base.path}/tomcat-deps" />
+  <property name="jasper-compiler-jdt.jar" 
+       value="${jasper-compiler-jdt.home}/jasper-compiler-jdt.jar"/>
+  
+  <!-- Some compilers will disable debugging if true. And it doesn't do anything 
+       in most cases -->
+  <property name="compile.optimize"     value="false"/>
+  <property name="compile.debug"        value="true" />
+  <property name="compile.deprecation"  value="false" />
+  <property name="compile.source"       value="1.4" />
+
+  <!-- =================== DETECT: Display configuration ================== -->
+  <target name="detect"
+   description="Display configuration and conditional compilation flags">
+
+    <echo message="--- ${name} ${version} Build ---"/>
+    <echo message="api.home=${api.home}"/>
+    <echo message="catalina.home=${catalina.home}"/>
+    <echo message="jasper.home=${jasper.home}"/>
+    <echo message="jtc.home=${jtc.home}"/>
+
+    <ant dir="${catalina.home}" target="flags.display"/>
+
+  </target>
+
+
+  <!-- ===================== DEPLOY: Create Directories =================== -->
+  <target name="init">
+    <mkdir dir="${tomcat.build}"/>
+    <mkdir dir="${tomcat.build}/classes" />
+    <mkdir dir="${tomcat.build}/server/lib" />
+    <mkdir dir="${tomcat.build}/common/lib" />
+
+    <uptodate property="servletapi.build.notrequired"
+              targetfile="${servlet-api.jar}">
+      <srcfiles dir="${api.home}/jsr154/src" includes="**" />
+    </uptodate>
+
+    <uptodate property="jspapi.build.notrequired"
+              targetfile="${jsp-api.jar}">
+      <srcfiles dir="${api.home}/jsr152/src" includes="**" />
+    </uptodate>
+
+    <uptodate property="tomcatutil.build.notrequired"
+              targetfile="${tomcat.build}/server/lib/tomcat-util.jar">
+      <srcfiles dir="${jtc.home}/util/java" includes="**" />
+    </uptodate>
+
+    <uptodate property="tomcatjk.build.notrequired"
+              targetfile="${tomcat.build}/server/lib/tomcat-ajp.jar">
+      <srcfiles dir="${jtc.home}/jk/java" includes="**" />
+    </uptodate>
+
+    <uptodate property="tomcatcoyote.build.notrequired"
+              targetfile="${tomcat.build}/server/lib/tomcat-coyote.jar">
+      <srcfiles dir="${jtc.home}/coyote/src" includes="**" />
+    </uptodate>
+
+    <uptodate property="tomcathttp11.build.notrequired"
+              targetfile="${tomcat.build}/server/lib/tomcat-http11.jar">
+      <srcfiles dir="${jtc.home}/http11/src" includes="**" />
+    </uptodate>
+
+    <uptodate property="admin.build.notrequired" 
+              targetfile="${tomcat.build}/server/webapps/admin/WEB-INF/web.xml">
+      <srcfiles dir="${catalina.home}/webapps/admin" includes="**" />
+    </uptodate>
+
+    <!--
+    <uptodate property="commons-daemon.build.notrequired"
+              targetfile="${commons-daemon.jar}">
+      <srcfiles dir="${cvs.base}/jakarta-commons/daemon" includes="**" />
+    </uptodate>
+    -->
+  </target>
+
+
+  <!-- ====================== DEPLOY: Copy Static Files =================== -->
+  <target name="deploy-static" depends="init">
+    <copy file="${jsp-api.jar}"  todir="${tomcat.build}/common/lib"/>
+    <copy file="${servlet-api.jar}" todir="${tomcat.build}/common/lib"/>
+
+    <copy file="${commons-el.jar}" 
+          todir="${tomcat.build}/common/lib"/>
+
+    <copy todir="${tomcat.build}/bin" 
+           file="${commons-logging-api.jar}" />
+
+    <copy todir="${tomcat.build}/server/lib" file="${commons-modeler.jar}" />
+
+    <copy todir="${tomcat.build}/bin" file="${commons-daemon.jar}" />
+    <copy todir="${tomcat.build}/bin" file="${commons-daemon.jsvc.tar.gz}" 
+            failonerror="false" />
+
+    <!-- <copy todir="${tomcat.build}/common/lib" file="${ant.jar}"/>
+    <copy todir="${tomcat.build}/common/lib" file="${ant-launcher.jar}"/> -->
+  	<copy todir="${tomcat.build}/common/lib" file="${jasper-compiler-jdt.jar}"/>
+  </target>
+
+  <!-- ====================== Build all components =================== -->
+  <target name="build-servletapi" unless="servletapi.build.notrequired" description="Build servlet API">
+    <echo>========== Building: ${servlet-api.jar}</echo>
+    <ant dir="${api.home}/jsr154" target="dist" >
+      <property name="servlet-api.dist" value="${servlet-api.home}" />
+    </ant>
+  </target>
+
+  <target name="build-jspapi" unless="jspapi.build.notrequired" description="Build JSP API">
+    <echo>========== Building: ${jsp-api.jar}</echo>
+    <ant dir="${api.home}/jsr152" target="dist">
+        <property name="jsp-api.dist" value="${jsp-api.home}" />
+    </ant>
+  </target>
+
+  <target name="build-tomcatutil" unless="tomcatutil.build.notrequired" description="Build j-t-c/util">
+    <echo>========== Building: tomcat-util to ${tomcat.build} </echo>
+
+    <ant dir="${jtc.home}/util" target="build-main">
+       <property name="jmx.jar" value="${jmx.jar}" />
+       <property name="puretls.jar" value="${puretls.jar}" />
+       <property name="jsse.lib" value="${jsse.lib}" />
+
+       <property name="tomcat-util.build" value="${tomcat.build}" />
+       <property name="tomcat-util.lib" value="${tomcat.build}/server/lib" />
+    </ant>
+  </target>
+
+  <target name="build-tomcatjk" unless="tomcatjk.build.notrequired" 
+          description="build j-t-c/jk" >
+    <echo>========== Building: tomcat-jk ${catalina.build} </echo>
+
+    <ant dir="${jtc.home}/jk" target="jkjava">
+      <property name="tomcat5.home" value="${catalina.build}"/>
+      <property name="tomcat5.detect" value="true"/>
+      <property name="commons-logging.jar" value="${commons-logging.jar}"/>
+      <property name="jmx.jar" value="${jmx.jar}"/>
+      <property name="jmx.detect" value="true"/>
+
+      <property name="jk.build" value="${tomcat.build}"/>
+
+        <!--
+      <property name="tomcat-coyote.jar" value="${tomcat.build}/server/lib/tomcat-coyote.jar" />
+      <property name="tomcat-jk2.jar" value="${tomcat.build}/server/lib/tomcat-ajp.jar" />
+      <property name="tomcat-jk.jar" value="${tomcat.build}/server/lib/tomcat-jk.jar" />
+      <property name="tomcat-jkconfig.jar" value="${tomcat.build}/server/lib/jkconfig.jar" />
+      <property name="tomcat-jkshm.jar" value="${tomcat.build}/server/lib/jkshm.jar" />
+      <property name="tomcat-jni.jar" value="${tomcat.build}/server/lib/tomcat-jni.jar" />
+      -->
+
+    </ant>
+
+    <copy file="${jtc.home}/jk/conf/jk2.properties" 
+        todir="${tomcat.build}/conf" />
+
+  	<!-- Protocol handlers - AJP -->
+	<jar jarfile="${tomcat.build}/server/lib/tomcat-ajp.jar" index="true">
+      <fileset dir="${tomcat.build}/classes">
+        <include name="org/apache/jk/**" />
+        <exclude name="org/apache/jk/ant/**" />
+        <!-- Javadoc and i18n exclusions -->
+        <exclude name="**/package.html" />
+        <exclude name="**/LocalStrings_*" />
+      </fileset>
+    </jar>
+
+  </target>
+
+  <target name="build-tomcatcoyote" 
+          unless="tomcatcoyote.build.notrequired" 
+          depends="init" description="Build j-t-c/coyote">
+    <echo>========== Building: tomcat-coyote </echo>
+
+    <ant dir="${jtc.home}/coyote" target="jar.tomcat5">
+      <property name="catalina.home" value="${tomcat.build}"/>
+      <property name="build.home" value="${tomcat.build}"/>
+      <property name="tomcat5.detect" value="true"/>
+      <!--
+      <property name="tomcat-coyote.jar" value="${tomcat.build}/server/lib/tomcat-coyote.jar" />
+      -->
+      <property name="tomcat-util.jar"
+                value="${tomcat.build}/server/lib/tomcat-util.jar"/>
+      <property name="servlet.jar"   value="${servlet-api.jar}"/>
+    </ant>
+
+  	<!-- Coyote API -->
+  	<delete file="${tomcat.build}/server/lib/tomcat-coyote.jar" />
+	<jar jarfile="${tomcat.build}/server/lib/tomcat-coyote.jar" index="true">
+      <fileset dir="${tomcat.build}/classes">
+        <include name="org/apache/coyote/*" />
+        <include name="org/apache/coyote/memory/*" />
+        <!-- Javadoc and i18n exclusions -->
+        <exclude name="**/package.html" />
+        <exclude name="**/LocalStrings_*" />
+      </fileset>
+    </jar>
+
+  </target>
+
+  <target name="build-tomcathttp11" 
+          unless="tomcathttp11.build.notrequired" 
+          depends="init" description="builds j-t-c/http11">
+    <echo>========== Building: tomcat-http11 </echo>
+
+    <ant dir="${jtc.home}/http11" target="compile-only">
+      <property name="build.home" value="${tomcat.build}"/>
+      <!--
+      <property name="tomcat-http11.jar" value="${tomcat.build}/server/lib/tomcat-http11.jar"/>
+      <property name="tomcat-coyote.jar" value="${tomcat.build}/server/lib/tomcat-coyote.jar" />
+      -->
+      <property name="commons-logging.jar" value="${commons-logging.jar}"/>
+    </ant>
+  	
+  	<!-- Protocol handlers - HTTP -->
+	<jar jarfile="${tomcat.build}/server/lib/tomcat-http.jar" index="true">
+      <fileset dir="${tomcat.build}/classes">
+        <include name="org/apache/coyote/http11/**" />
+        <!-- Javadoc and i18n exclusions -->
+        <exclude name="**/package.html" />
+        <exclude name="**/LocalStrings_*" />
+      </fileset>
+    </jar>
+
+  </target>
+
+  <target name="build-jasper" 
+          unless="jasper.build.notrequired" 
+          depends="init" description="build jasper">
+    <echo>========== Building: jasper </echo>
+
+    <ant dir="${jasper.home}"   target="build-only">
+      <property name="jasper.classes" value="${tomcat.build}/classes"/>
+      <property name="jasper.build" value="${tomcat.build}"/>
+      <property name="jasper-compiler.jar" value="${tomcat.build}/common/lib/jasper-compiler.jar"/>
+      <property name="jasper-runtime.jar" value="${tomcat.build}/common/lib/jasper-runtime.jar"/>
+      <property name="catalina.home" value="${tomcat.build}"/>
+      <property name="commons-el.jar" location="${commons-el.jar}" />
+    </ant>
+  </target>
+
+  <target name="build-admin" unless="admin.build.notrequired" 
+          depends="init" description="build admin" >
+
+    <echo>========== Building: admin </echo>
+    <ant dir="${catalina.home}/webapps/admin" target="build-main">
+        <property name="webapps.build" value="${tomcat.build}/server/webapps" />
+        <property name="classes.dir" value="${tomcat.build}/classes" />
+        <property name="tomcat-util.jar" value="${tomcat.build}/server/lib/tomcat-util.jar"/>
+        <property name="catalina.deploy" value="${tomcat.build}" />
+        <property name="flags.hide" value="true" />
+    </ant>
+
+    <touch file="${tomcat.build}/server/webapps/admin/WEB-INF/web.xml" />
+  </target>
+
+  <target name="build-webapps-precompile" 
+          depends="init" description="Precompile webapps" >
+
+    <!-- JSPC -->
+    <property name="admin.base" location="${tomcat.build}/server/webapps/admin" />
+    <property name="ROOT.base" location="${tomcat.build}/webapps/ROOT" />
+    <property name="jsp-examples.base" location="${tomcat.build}/webapps/jsp-examples" />
+
+    <mkdir dir="${admin.base}/WEB-INF/src/admin" />
+    <mkdir dir="${ROOT.base}/WEB-INF/src" />
+    <mkdir dir="${ROOT.base}/WEB-INF/classes" />
+    <mkdir dir="${jsp-examples.base}/WEB-INF/src" />
+
+    <path id="jspc.classpath">
+      <pathelement location="${java.home}/../lib/tools.jar"/>
+      <pathelement location="${commons-logging.jar}"/>
+      <pathelement location="${tomcat.build}/server/classes"/>
+      <fileset dir="${tomcat.build}/server/lib">
+        <include name="*.jar"/>
+      </fileset>
+      <pathelement location="${tomcat.build}/common/classes"/>
+      <fileset dir="${tomcat.build}/common/lib">
+        <include name="*.jar"/>
+      </fileset>
+      <pathelement location="${tomcat.build}/common/classes"/>
+      <fileset dir="${tomcat.build}/common/endorsed">
+        <include name="*.jar"/>
+      </fileset>
+      <fileset dir="${tomcat.build}/common/lib">
+        <include name="*.jar"/>
+      </fileset>
+    </path>
+
+    <taskdef classname="org.apache.jasper.JspC" name="jasper2" >
+      <classpath refid="jspc.classpath"/>
+    </taskdef>
+
+    <jasper2 
+             compile="false"
+             validateXml="false"
+             uriroot="${ROOT.base}"
+             webXmlFragment="${ROOT.base}/WEB-INF/generated_web.xml"
+             addWebXmlMappings="true"
+             outputDir="${ROOT.base}/WEB-INF/src" />
+
+    <jasper2 
+             compile="false"
+             validateXml="false"
+             uriroot="${jsp-examples.base}"
+	     webXmlFragment="${jsp-examples.base}/WEB-INF/generated_web.xml"
+             addWebXmlMappings="true"
+             outputDir="${jsp-examples.base}/WEB-INF/src" />
+
+    <jasper2 
+             package="admin"
+             compile="false"
+             validateXml="false"
+             uriroot="${admin.base}"
+             webXmlFragment="${admin.base}/WEB-INF/generated_web.xml"
+             addWebXmlMappings="true"
+             outputDir="${admin.base}/WEB-INF/src/admin" />
+
+    <javac destdir="${ROOT.base}/WEB-INF/classes"
+           optimize="off"
+           debug="${compile.debug}" 
+           deprecation="${compile.deprecation}"
+           source="${compile.source}"
+           failonerror="false"
+           srcdir="${ROOT.base}/WEB-INF/src" 
+	   excludes="**/*.smap">
+      <classpath>
+        <pathelement location="${java.home}/../lib/tools.jar"/>
+        <fileset dir="${tomcat.build}/server/lib">
+          <include name="*.jar"/>
+        </fileset>
+        <fileset dir="${tomcat.build}/common/lib">
+          <include name="*.jar"/>
+        </fileset>
+        <pathelement location="${tomcat.build}/classes"/>
+      </classpath>
+      <include name="**" />
+    </javac>
+
+    <mkdir dir="${jsp-examples.base}/WEB-INF/src/tags"/>
+    <copy todir="${jsp-examples.base}/WEB-INF/classes">
+      <fileset dir="${jsp-examples.base}/WEB-INF/src">
+        <include name="**/*.class" />
+      </fileset>
+    </copy>
+
+    <javac destdir="${jsp-examples.base}/WEB-INF/classes"
+           optimize="off"
+           debug="${compile.debug}"
+           deprecation="${compile.deprecation}"
+           source="${compile.source}"
+           failonerror="false"
+           srcdir="${jsp-examples.base}/WEB-INF/src" 
+	   excludes="**/*.smap">
+      <classpath>
+        <pathelement location="${java.home}/../lib/tools.jar"/>
+        <fileset dir="${tomcat.build}/server/lib">
+          <include name="*.jar"/>
+        </fileset>
+        <fileset dir="${jsp-examples.base}/WEB-INF/lib">
+          <include name="*.jar"/>
+        </fileset>
+        <fileset dir="${tomcat.build}/common/lib">
+          <include name="*.jar"/>
+        </fileset>
+        <pathelement location="${tomcat.build}/classes"/>
+        <pathelement location="${jsp-examples.base}/WEB-INF/classes"/>
+      </classpath>
+      <include name="**" />
+      <exclude name="tags/**" />
+    </javac>
+
+    <javac destdir="${admin.base}/WEB-INF/classes"
+           optimize="off"
+           debug="${compile.debug}"
+           deprecation="${compile.deprecation}"
+           source="${compile.source}"
+           failonerror="false"
+           srcdir="${admin.base}/WEB-INF/src" 
+	   excludes="**/*.smap">
+      <classpath>
+        <pathelement location="${java.home}/../lib/tools.jar"/>
+        <fileset dir="${tomcat.build}/server/lib">
+          <include name="*.jar"/>
+        </fileset>
+        <fileset dir="${admin.base}/WEB-INF/lib">
+          <include name="*.jar"/>
+        </fileset>
+        <fileset dir="${tomcat.build}/common/lib">
+          <include name="*.jar"/>
+        </fileset>
+        <pathelement location="${tomcat.build}/classes"/>
+      </classpath>
+      <include name="admin/**" />
+    </javac>
+  </target>
+
+  <target name="build-catalina" depends="init"
+          description="Builds catalina">
+    <echo>========== Building: catalina in ${tomcat.build}</echo>
+
+    <ant dir="${catalina.home}/catalina" target="build-static">
+        <property name="catalina.build" value="${tomcat.build}" />
+        <property name="classes.dir" value="${tomcat.build}/classes" />
+        <property name="tomcat-util.jar"
+                 value="${tomcat.build}/server/lib/tomcat-util.jar"/>
+        <property name="catalina.deploy" value="${tomcat.build}" />
+        <property name="flags.hide" value="true" />
+    </ant>
+
+    <ant dir="${catalina.home}/catalina" target="catalina-jars">
+        <!-- in-place building -->
+        <property name="tomcat-coyote.jar" value="${tomcat.build}/server/lib/tomcat-coyote.jar" />
+        <property name="catalina.build" value="${tomcat.build}" />
+        <property name="classes.dir" value="${tomcat.build}/classes" />
+        <property name="tomcat-util.jar"
+                 value="${tomcat.build}/server/lib/tomcat-util.jar"/>
+        <property name="catalina.deploy" value="${tomcat.build}" />
+        <property name="flags.hide" value="true" />
+    </ant>
+
+    <ant dir="${catalina.home}/catalina" target="deploy-static-only">
+        <property name="catalina.build" value="${tomcat.build}" />
+        <property name="classes.dir" value="${tomcat.build}/classes" />
+        <property name="tomcat-util.jar"
+                 value="${tomcat.build}/server/lib/tomcat-util.jar"/>
+        <property name="catalina.deploy" value="${tomcat.build}" />
+        <property name="flags.hide" value="false" />
+    </ant>
+
+    <ant dir="${catalina.home}/modules" target="build">
+        <property name="catalina.build" value="${tomcat.build}" />
+        <property name="cluster.dist" value="${tomcat.build}/server/lib" />
+        <property name="catalina.deploy" value="${tomcat.build}" />
+        <property name="flags.hide" value="true" />
+    </ant>
+
+  </target>
+
+  <target name="build" depends="init"
+          description="Builds all components">
+
+    <antcall target="build-tomcatutil"/>
+    <antcall target="build-tomcatcoyote"/>
+    <antcall target="build-catalina"/>
+
+    <antcall target="build-tomcatjk"/>
+
+    <antcall target="build-tomcathttp11"/>
+      
+    <antcall target="build-jasper"/>
+
+    <antcall target="build-i18n"/>
+
+    <!-- Correct permissions and line endings on "bin" scripts -->
+    <fixcrlf srcdir="${tomcat.build}/bin"   includes="*.sh"  eol="lf"/>
+    <fixcrlf srcdir="${tomcat.build}/bin"   includes="*.bat" eol="crlf"/>
+    <chmod      dir="${tomcat.build}/bin"   includes="*.sh"  perm="+x"/>
+
+  </target>
+
+  <target name="build-i18n">
+
+  	<!-- i18n JARs -->
+    <jar jarfile="${tomcat.build}/common/i18n/tomcat-i18n-es.jar">
+      <fileset dir="${tomcat.build}/classes">
+        <include name="**/LocalStrings_es.properties" />
+        <exclude name="**/tomcat4/**" />
+      </fileset>
+    </jar>
+    <jar jarfile="${tomcat.build}/common/i18n/tomcat-i18n-fr.jar">
+      <fileset dir="${tomcat.build}/classes">
+        <include name="**/LocalStrings_fr.properties" />
+        <exclude name="**/tomcat4/**" />
+      </fileset>
+    </jar>
+    <jar jarfile="${tomcat.build}/common/i18n/tomcat-i18n-ja.jar">
+      <fileset dir="${tomcat.build}/classes">
+        <include name="**/LocalStrings_ja.properties" />
+        <exclude name="**/tomcat4/**" />
+      </fileset>
+    </jar>
+
+  	<!-- Build baseline _en bundle to ease -->
+    <copy todir="${tomcat.build}/classes" includeemptydirs="false">
+      <fileset dir="${tomcat.build}/classes">
+        <include name="**/LocalStrings.properties"/>
+      </fileset>
+      <mapper type="glob" from="*.properties" to="*_en.properties"/>
+    </copy>
+    <jar jarfile="${tomcat.build}/common/i18n/tomcat-i18n-en.jar">
+      <fileset dir="${tomcat.build}/classes">
+        <include name="**/LocalStrings_en.properties" />
+        <exclude name="**/tomcat4/**" />
+      </fileset>
+    </jar>
+  	
+  </target>
+	
+  <!-- ====================== Build dependent code =================== -->
+
+<!--
+  <target name="build-commons-modeler" unless="commons-modeler.build.notrequired" description="build commons-modeler">
+    <echo>========== Building: commons-modeler </echo>
+
+    <ant dir="${cvs.base}/jakarta-commons/modeler" target="dist" >
+        <property name="dist.home" location="${commons-modeler.home}" />
+        <property name="commons-logging.jar" location="${commons-logging.jar}" />
+        <property name="jmx.jar" location="${jmx.jar}" />
+        <property name="commons-digester.jar" location="${commons-digester.jar}" />
+        <property name="build.home" value="${tomcat.build}" />
+    </ant>
+  </target>
+-->
+
+<!--
+  <target name="build-commons-daemon" unless="commons-daemon.build.notrequired" description="build commons-daemon" >
+    <echo>========== Building: commons-daemon </echo>
+
+    <ant dir="${cvs.base}/jakarta-commons/daemon" target="dist" >
+        <property name="compile.optimize" value="false" />
+        <property name="dist.home" value="${commons-daemon.home}" />
+    </ant>
+  </target>
+-->
+
+  <target name="build-all" depends="init,deploy-static,build,build-webapps,compat,deployer" 
+          description="build tomcat, webapps, embed, compat, deployer" />
+
+
+  <target name="build-depends" depends="init"
+          description="Builds various dependent components - APIs, commons-el, commons-modeler, daemon">
+    <antcall target="build-servletapi"/>
+    <antcall target="build-jspapi"/>
+
+    <!-- <antcall target="build-commons-modeler" /> -->
+    <!-- <antcall target="build-commons-daemon"  /> -->
+
+  	<antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-collections-src.loc}"/>
+      <param name="destfile" value="${tomcat-dbcp.jar}" />
+    </antcall>
+  	<antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-pool-src.loc}"/>
+      <param name="destfile" value="${tomcat-dbcp.jar}" />
+    </antcall>
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-dbcp-src.loc}"/>
+      <param name="destfile" value="${tomcat-dbcp.jar}" />
+    </antcall>
+
+    <antcall target="build-tomcat-dbcp" />
+
+    <antcall target="downloadzip">
+      <param name="sourcefile" value="${jdt.loc}"/>
+      <param name="destfile" value="${jdt.jar}"/>
+      <param name="destdir" value="${base.path}"/>
+    </antcall>
+    <antcall target="build-jasper-compiler-jdt" />
+  </target>
+
+  <target name="build-tomcat-dbcp">
+   <mkdir dir="${tomcat-dbcp.home}"/>
+   <antcall target="-build-tomcat-dbcp">
+      <param name="basedir" value="${tomcat-dbcp.home}" />
+   </antcall>
+  </target>
+
+  <target name="-build-tomcat-dbcp">
+    <copy todir="${tomcat-dbcp.home}">
+    	<fileset dir="${commons-collections.home}" >
+    		<include name="**/collections/CursorableLinkedList.java" />
+    		<include name="**/collections/KeyValue.java" />
+    		<include name="**/collections/LRUMap.java" />
+    		<include name="**/collections/SequencedHashMap.java" />
+    	</fileset>
+    	<fileset dir="${commons-pool.home}">
+    		<include name="**/*.java" />
+    		<exclude name="**/Stack*.java" />
+    		<exclude name="**/SoftReferenceObjectPool.java" />
+    		<exclude name="**/test/**" />
+    	</fileset>
+    	<fileset dir="${commons-dbcp.home}">
+    		<include name="**/*.java" />
+    		<exclude name="**/test/**" />
+       	</fileset>
+    </copy>
+  	<replace dir="${tomcat-dbcp.home}/src/java/org/apache/commons">
+  		<replacefilter token="return UnmodifiableList.decorate(l);"
+  			value="return l;" />
+  		<replacefilter token="import org.apache.commons.collections.list.UnmodifiableList;"
+  			value=" " />
+  	</replace>
+  	<replace dir="${tomcat-dbcp.home}/src/java/org/apache/commons" >
+  		<replacefilter token="org.apache.commons" 
+  			value="org.apache.tomcat.dbcp" />
+  	</replace>
+  	<mkdir dir="${tomcat-dbcp.home}/src/java/org/apache/tomcat/dbcp" />
+  	<move todir="${tomcat-dbcp.home}/src/java/org/apache/tomcat/dbcp">
+  		<fileset dir="${tomcat-dbcp.home}/src/java/org/apache/commons" />
+    </move>
+  	<mkdir dir="${tomcat-dbcp.home}/classes"/>
+    <javac destdir="classes"
+           optimize="off"
+           debug="${compile.debug}"
+           deprecation="${compile.deprecation}"
+           source="${compile.source}"
+           sourcepath="${tomcat-dbcp.home}/src/java"
+           srcdir="src/java" >
+      <include name="**" />
+    </javac>
+    <jar jarfile="${tomcat-dbcp.jar}"
+         index="true">
+       <fileset dir="${tomcat-dbcp.home}/classes">
+          <include name="**/*.class" />
+          <include name="**/*.properties" />
+       </fileset>
+    </jar>
+  </target>
+
+	  <target name="build-jasper-compiler-jdt">
+	   <mkdir dir="${jasper-compiler-jdt.home}"/>
+	   <antcall target="-build-jasper-compiler-jdt">
+	      <param name="basedir" value="${jasper-compiler-jdt.home}" />
+	   </antcall>
+	  </target>
+
+	  <target name="-build-jasper-compiler-jdt">
+	  	<unjar src="${jdt.jar}" dest="${jasper-compiler-jdt.home}" />
+	  	<jar destfile="${jasper-compiler-jdt.jar}" index="true">
+	  		<fileset dir="${jasper-compiler-jdt.home}">
+	  			<include name="org/eclipse/jdt/core/compiler/**"/>
+	  			<include name="org/eclipse/jdt/internal/compiler/**"/>
+	  			<include name="org/eclipse/jdt/internal/core/util/CommentRecorder*"/>
+	  		</fileset>
+	  	</jar>
+	  </target>
+	
+  <target name="build-webapps" depends="init"
+          description="build  webapps">
+
+    <echo>========== Building: webapps </echo>
+
+    <mkdir dir="${tomcat.build}/webapps" />
+    <mkdir dir="${tomcat.build}/server/webapps" />
+  
+    <!-- The build files are far too difficult to hack - just build it and copy -->
+    <ant dir="${api.home}/jsr154" target="dist">
+    </ant>
+    <ant dir="${api.home}/jsr152" target="dist">
+    </ant>
+
+    <mkdir dir="${tomcat.build}/webapps/servlets-examples"/>
+    <copy todir="${tomcat.build}/webapps/servlets-examples">
+      <fileset dir="${api.home}/jsr154/build/examples" includes="**"/>
+    </copy>
+
+    <mkdir dir="${tomcat.build}/webapps/jsp-examples"/>
+    <copy todir="${tomcat.build}/webapps/jsp-examples">
+      <fileset dir="${api.home}/jsr152/build/examples" includes="**"/>
+    </copy>
+
+    <ant dir="${catalina.home}/webapps/ROOT" target="build-main">
+        <property name="webapps.build" value="${tomcat.build}/webapps" />
+        <property name="classes.dir" value="${tomcat.build}/classes" />
+        <property name="tomcat-util.jar" value="${tomcat.build}/server/lib/tomcat-util.jar"/>
+        <property name="catalina.deploy" value="${tomcat.build}" />
+        <property name="flags.hide" value="true" />
+    </ant>
+
+    <ant dir="${catalina.home}/webapps/docs" target="build-main">
+        <property name="webapps.build" value="${tomcat.build}/webapps" />
+        <property name="classes.dir" value="${tomcat.build}/classes" />
+        <property name="tomcat-util.jar" value="${tomcat.build}/server/lib/tomcat-util.jar"/>
+        <property name="catalina.deploy" value="${tomcat.build}" />
+        <property name="flags.hide" value="true" />
+    </ant>
+
+    <antcall target="build-admin" />
+
+    <ant dir="${catalina.home}/webapps/manager" target="build-main">
+        <property name="webapps.build" value="${tomcat.build}/server/webapps" />
+        <property name="classes.dir" value="${tomcat.build}/classes" />
+        <property name="tomcat-util.jar" value="${tomcat.build}/server/lib/tomcat-util.jar"/>
+        <property name="catalina.deploy" value="${tomcat.build}" />
+        <property name="flags.hide" value="true" />
+    </ant>
+
+    <ant dir="${catalina.home}/webapps/balancer" target="build-main">
+        <property name="webapps.build" value="${tomcat.build}/webapps" />
+        <property name="classes.dir" value="${tomcat.build}/classes" />
+        <property name="tomcat-util.jar" value="${tomcat.build}/server/lib/tomcat-util.jar"/>
+        <property name="catalina.deploy" value="${tomcat.build}" />
+        <property name="flags.hide" value="true" />
+    </ant>
+
+    <ant dir="${catalina.home}/webapps/webdav" target="build-main">
+        <property name="webapps.build" value="${tomcat.build}/webapps" />
+    </ant>
+
+    <!-- Precompiling and fixing webapps -->
+    <antcall target="build-webapps-precompile" /> 
+    <antcall target="fix-webapps" />
+
+  </target>
+
+  <target name="fix-webapps" depends="init" >
+    <!-- Extra build steps for webapps -->
+
+    <filter token="VERSION" value="${version}"/>
+
+    <!-- Add release notes to the root webapp -->
+    <copy file="${basedir}/RELEASE-NOTES"
+        tofile="${tomcat.build}/webapps/ROOT/RELEASE-NOTES.txt"
+        filtering="true" />
+
+    <!-- Add documents to the tomcat-docs webapp -->
+    <copy file="${basedir}/resources/build.xml"
+        todir="${tomcat.build}/webapps/tomcat-docs" />
+    <copy file="./RELEASE-NOTES"
+        tofile="${tomcat.build}/webapps/tomcat-docs/RELEASE-NOTES.txt"
+        filtering="true" />
+
+    <!-- Build JARs for webapps classes -->
+    <mkdir dir="${tomcat.build}/server/webapps/admin/WEB-INF/lib" />
+    <jar jarfile="${tomcat.build}/server/webapps/admin/WEB-INF/lib/catalina-admin.jar"
+         index="true">
+       <fileset dir="${tomcat.build}/server/webapps/admin/WEB-INF/classes">
+          <include name="**/*.class" />
+          <include name="**/*.properties" />
+       </fileset>
+    </jar>
+    <mkdir dir="${tomcat.build}/server/webapps/manager/WEB-INF/lib" />
+    <jar jarfile="${tomcat.build}/server/webapps/manager/WEB-INF/lib/catalina-manager.jar"
+         index="true">
+       <fileset dir="${tomcat.build}/server/webapps/manager/WEB-INF/classes">
+          <include name="**/*.class" />
+          <include name="**/*.properties" />
+       </fileset>
+    </jar>
+    <mkdir dir="${tomcat.build}/webapps/balancer/WEB-INF/lib" />
+    <jar jarfile="${tomcat.build}/webapps/balancer/WEB-INF/lib/catalina-balancer.jar"
+         index="true">
+       <fileset dir="${tomcat.build}/webapps/balancer/WEB-INF/classes">
+          <include name="**/*.class" />
+          <include name="**/*.properties" />
+       </fileset>
+    </jar>
+    <mkdir dir="${tomcat.build}/webapps/ROOT/WEB-INF/lib" />
+    <jar jarfile="${tomcat.build}/webapps/ROOT/WEB-INF/lib/catalina-root.jar"
+         index="true">
+       <fileset dir="${tomcat.build}/webapps/ROOT/WEB-INF/classes">
+          <include name="**/*.class" />
+          <include name="**/*.properties" />
+       </fileset>
+    </jar>
+
+    <!-- Add XML declarations for admin, manager and balancer -->
+    <copy file="${tomcat.build}/server/webapps/manager/manager.xml"
+         todir="${tomcat.build}/conf/Catalina/localhost" />
+    <copy file="${tomcat.build}/server/webapps/admin/admin.xml"
+         todir="${tomcat.build}/conf/Catalina/localhost" />
+
+  </target>
+
+  <!-- ====================== Embed target =================== -->
+
+  <target name="embed" description="Create a set of jars for embedded tomcat" > 
+    <!-- Generic libraries ( share ?) -->
+    <copy todir="embed/lib" file="${commons-logging.jar}"/>
+    <copy todir="embed/lib" file="${commons-digester.jar}"/>
+    <copy todir="embed/lib" file="${commons-modeler.jar}"/>
+    <copy todir="embed/lib" file="${commons-beanutils.jar}"/>
+    <copy todir="embed/lib" file="${commons-collections.jar}"/>
+
+    <copy todir="embed/lib" file="${jasper-compiler-jdt.jar}"/>
+
+    <copy todir="embed/lib">
+      <fileset dir="build/server/lib">
+        <include name="tomcat-util.jar"/>
+      </fileset>
+    </copy>
+    
+    <!-- Connector -->
+    <copy todir="embed/lib">
+      <fileset dir="build/server/lib">
+        <include name="tomcat-coyote.jar"/>
+        <include name="tomcat-http11.jar"/>
+        <include name="tomcat-ajp.jar"/>
+      </fileset>
+    </copy>
+
+    <!-- Servlet API implementation -->
+    <copy todir="embed/lib">
+      <fileset dir="build/common/lib">
+        <include name="servlet-api.jar"/>
+        <include name="jsp-api.jar"/>
+        <include name="naming-resources.jar"/>
+        <include name="naming-common.jar"/>
+      </fileset>
+      <fileset dir="build/server/lib">
+        <include name="servlets-common.jar"/>
+        <include name="servlets-invoker.jar"/>
+        <include name="servlets-default.jar"/>
+        <include name="catalina.jar"/>
+        <include name="catalina-optional.jar"/>
+      </fileset>
+    </copy>
+
+    <!-- JNDI extra -->
+    <copy todir="embed/lib">
+      <fileset dir="build/common/lib">
+        <include name="naming-java.jar"/>
+        <include name="naming-factory.jar"/>
+      </fileset>
+    </copy>
+
+    <!-- JSP runtime -->
+    <copy todir="embed/lib">
+      <fileset dir="build/common/lib">
+        <include name="commons-el.jar"/>
+        <include name="jsp-api.jar"/>
+        <include name="jasper-runtime.jar"/>
+        <include name="jasper-compiler.jar"/>
+      </fileset>
+    </copy>
+
+    <!-- JSP compiler - not needed for an minimal server if it 
+         uses precompilation -->
+    <!--
+    <copy todir="embed/lib">
+      <fileset dir="build/common/lib">
+        <include name="ant.jar"/>
+        <include name="ant-launcher.jar" />
+      </fileset>
+      <fileset dir="build/common/lib">
+        <include name="jasper-compiler.jar"/>
+      </fileset>
+    </copy>
+    -->
+    <copy tofile="embed/build.xml" file="resources/mbeans/tomcat5-ant.xml" />
+    <!-- 
+    <copy tofile="embed/tomcat5-mbeans.xml" file="resources/mbeans/tomcat5-mbeans.xml" />
+    <copy tofile="embed/tomcat5-mlet.xml" file="resources/mbeans/tomcat5-mlet.xml" />
+    <copy tofile="embed/tomcat5-service.xml" file="resources/mbeans/tomcat5-service.xml" />
+    -->
+
+    <mkdir dir="embed/conf"/>
+    <copy todir="embed/conf">
+      <fileset dir="build/conf">
+         <include name="jk2.properties"/>
+         <include name="tomcat-users.xml"/>
+         <include name="web.xml"/>
+         <!-- no longer needed 
+            <include name="server.xml"/>
+         -->
+      </fileset>
+    </copy>
+
+    <mkdir dir="embed/webapps"/>
+    <copy todir="embed/webapps" >
+       <fileset dir="dist/webapps" includes="ROOT/**"/>
+    </copy>
+    <copy todir="embed/webapps" >
+       <fileset dir="dist/server/webapps" includes="manager/**"/>
+    </copy>
+    
+  </target>
+
+  <!-- ====================== Deployer target =================== -->
+
+  <target name="deployer" description="Create the Tomcat deployer binary" > 
+
+    <!-- JSP and Servlet runtime -->
+    <copy todir="${tomcat.deployer}/lib">
+      <fileset dir="${tomcat.build}/common/lib">
+        <include name="commons-el.jar"/>
+        <include name="jsp-api.jar"/>
+        <include name="jasper-runtime.jar"/>
+        <include name="jasper-compiler.jar"/>
+        <include name="servlet-api.jar"/>
+      </fileset>
+    </copy>
+
+    <!-- Digester and dependencies -->
+    <copy todir="${tomcat.deployer}/lib">
+      <fileset dir="${tomcat.build}/server/lib">
+        <include name="commons-digester.jar"/>
+        <include name="commons-beanutils.jar"/>
+      </fileset>
+    </copy>
+    <copy todir="${tomcat.deployer}/lib">
+      <fileset dir="${tomcat.build}/common/lib">
+        <include name="commons-collections.jar"/>
+      </fileset>
+    </copy>
+
+    <copy todir="${tomcat.deployer}/lib" 
+           file="${tomcat.build}/server/lib/catalina-ant.jar"/>
+    <copy todir="${tomcat.deployer}/lib" file="${commons-logging.jar}"/>
+
+    <jar jarfile="${tomcat.deployer}/lib/catalina-deployer.jar">
+       <fileset dir="${tomcat.build}/classes">
+          <include name="org/apache/catalina/startup/DigesterFactory.class" />
+          <include name="org/apache/catalina/util/SchemaResolver.class" />
+          <include name="org/apache/catalina/util/StringManager.class" />
+       </fileset>
+    </jar>
+
+  	<!--
+    <copy todir="${tomcat.deployer}/lib" file="${xercesImpl.jar}"/>
+    <copy todir="${tomcat.deployer}/lib" file="${xml-apis.jar}"/>
+    -->
+
+    <copy todir="${tomcat.deployer}">
+      <fileset dir="${basedir}/resources/deployer" />
+    </copy>
+
+    <!-- Copy deployer documentation -->
+    <copy todir="${tomcat.deployer}">
+      <fileset dir="${tomcat.build}/webapps/tomcat-docs">
+        <include name="images/jakarta-logo.gif" />
+        <include name="images/tomcat.gif" />
+      </fileset>
+    </copy>
+    <copy tofile="${tomcat.deployer}/docs/manual.html"
+      file="${tomcat.build}/webapps/tomcat-docs/printer/deployer-howto.html" />
+
+  </target>
+
+  <!-- ====================== Compat target =================== -->
+
+  <target name="compat" description="Create compatibility binaries for JREs before 1.5" >
+
+    <copy todir="${tomcat.compat}/common/endorsed" file="${xercesImpl.jar}"/>
+    <copy todir="${tomcat.compat}/common/endorsed" file="${xml-apis.jar}"/>
+    <copy tofile="${tomcat.compat}/bin/jmx.jar" file="${jmx.jar}"/>
+
+  </target>
+
+  <!-- ====================== DEPLOY: Deploy Components =================== -->
+
+  <target name="deploy" depends="deploy-static,build-all,build-webapps"
+          description="Build and deploy all components" />
+
+  <target name="deploy.old" depends="deploy-static">
+
+    <echo>Target: Modeler - Dist ...</echo>
+    <ant dir="${commons-modeler.home}" target="dist"/>
+
+    <echo>Target: Catalina - Deploy ...</echo>
+    <ant dir="${catalina.home}" target="deploy"/>
+    <!-- 
+    <antcall target="build-tomcat-coyote"/>
+    <antcall target="build-tomcat-jk"/>
+    <antcall target="build-tomcat-http11"/>
+    <ant dir="${catalina.home}" target="deploy-catalina"/>
+     -->
+    <copy todir="${tomcat.build}">
+      <fileset dir="${catalina.home}/build"/>
+    </copy>
+
+    <filter token="VERSION" value="${version}"/>
+
+    <antcall target="build-jasper"/>
+
+    <!-- Correct permissions and line endings on "bin" scripts -->
+    <fixcrlf srcdir="${tomcat.build}/bin"   includes="*.sh"  eol="lf"/>
+    <fixcrlf srcdir="${tomcat.build}/bin"   includes="*.bat" eol="crlf"/>
+    <chmod      dir="${tomcat.build}/bin"   includes="*.sh"  perm="+x"/>
+
+    <antcall target="fix-webapps"/>
+
+    <!-- Copy the examples webapps -->
+    <copy todir="${tomcat.build}/webapps/jsp-examples">
+      <fileset dir="${api.home}/jsr152/build/examples"/>
+    </copy>
+    <copy todir="${tomcat.build}/webapps/servlets-examples">
+      <fileset dir="${api.home}/jsr154/build/examples"/>
+    </copy>
+
+    <!-- Copy Unix JSVC from commons-daemon -->
+    <copy file="${commons-daemon.jsvc.tar.gz}" 
+        tofile="${tomcat.dist}/bin/jsvc.tar.gz" />
+    <copy todir="${tomcat.build}/bin" file="${commons-daemon.jar}" />
+
+    <echo>Target: Webapps precompilation ...</echo>
+
+    <antcall target="build-admin"/>
+
+    <ant dir="." target="build-webapps-precompile" />
+
+  </target>
+
+
+  <!-- ====================== COMBO: Clean All Directories ================ -->
+  <target name="clean"
+   description="Clean all components">
+
+    <delete dir="${tomcat.build}"/>
+
+    <delete dir="${tomcat.embed}"/>
+    <delete dir="${tomcat.compat}"/>
+    <delete dir="${tomcat.deployer}"/>
+
+    <echo>Target: Servlet API - Clean ...</echo>
+    <ant dir="${api.home}/jsr154" target="clean"/>
+
+    <echo>Target: JSP API - Clean ...</echo>
+    <ant dir="${api.home}/jsr152" target="clean"/>
+
+    <echo>Target: Catalina - Clean ...</echo>
+    <ant dir="${catalina.home}" target="clean"/>
+
+    <echo>Target: Jasper - Clean ...</echo>
+    <ant dir="${jasper.home}"   target="clean">
+      <property name="catalina.home" value="${tomcat.build}"/>
+    </ant>
+
+    <delete dir="${tomcat.dist}"/>
+
+  </target>
+
+
+  <!-- ======================= COMBO: Build All Components ================ -->
+  <target name="all"
+          description="Clean, build, and deploy all components">
+
+    <echo>Target: Servlet API - Dist ...</echo>
+    <ant dir="${api.home}/jsr154" target="dist"/>
+
+    <echo>Target: JSP API - Dist ...</echo>
+    <ant dir="${api.home}/jsr152" target="dist"/>
+
+    <echo>Target: Catalina - All ...</echo>
+    <ant dir="${catalina.home}" target="all"/>
+
+    <echo>Target: Jasper - All ...</echo>
+    <ant dir="${jasper.home}"   target="all">
+      <property name="catalina.home" value="${tomcat.build}"/>
+    </ant>
+  </target>
+
+
+  <!-- ======================= COMBO: Test All Components ================= -->
+  <target name="test"
+          description="Unit tests on all components">
+    <echo>Target: Catalina - Test ...</echo>
+    <ant dir="${catalina.home}" target="test"/>
+    <echo>Target: Jasper - Test ...</echo>
+    <ant dir="${jasper.home}"   target="test">
+      <property name="catalina.home" value="${tomcat.build}"/>
+    </ant>
+  </target>
+
+  <!-- ======================= TESTER: Run Catalina Tester Tests=========== -->
+  
+   <target name="dist-tester" 
+           description="Build the Catalina tester">
+
+    <ant dir="${catalina.home}/tester" target="dist">
+      <property name="tester.deploy" value="${tomcat.build}"/>
+    </ant>
+    <ant dir="${catalina.home}/tester" target="deploy">
+      <property name="tester.deploy" value="${tomcat.build}"/>
+    </ant>
+
+   </target>
+
+   <target name="clean-tester" 
+           description="Clean the Catalina tester">
+
+    <ant dir="${catalina.home}/tester" target="clean" />
+
+   </target>
+
+  <target name="run-tester"
+   description="Catalina Tests" depends="dist-tester">
+
+  	<!-- For Java 1.4 -->
+  	<copy file="${jmx.jar}" tofile="${tomcat.build}/bin/jmx.jar" />
+    <copy todir="${tomcat.build}/common/endorsed" file="${xercesImpl.jar}"/>
+    <copy todir="${tomcat.build}/common/endorsed" file="${xml-apis.jar}"/>
+  	
+    <parallel>
+
+        <java classname="LauncherBootstrap" fork="yes">
+            <arg value="-launchfile"/>
+            <arg value="catalina.xml"/>
+            <arg value="-verbose"/>
+            <arg value="catalina"/>
+            <arg value="start"/>
+            <classpath>
+                <pathelement path="${java.class.path}"/>
+                <pathelement path="${tomcat.build}/bin"/>
+            </classpath>
+        </java>
+
+        <sequential>
+            <!-- Let tomcat starts before starting Tester -->
+            <sleep seconds="15"/>
+
+            <ant dir="${catalina.home}/tester/dist/bin" antfile="tester.xml" 
+                 target="all">
+              <property name="catalina.home" value="${tomcat.build}"/>
+            </ant>
+
+            <java classname="LauncherBootstrap" fork="yes">
+                <arg value="-launchfile"/>
+                <arg value="catalina.xml"/>
+                <arg value="-verbose"/>
+                <arg value="catalina"/>
+                <arg value="stop"/>
+                <classpath>
+                    <pathelement path="${java.class.path}"/>
+                    <pathelement path="${tomcat.build}/bin"/>
+                </classpath>
+            </java>
+        </sequential>
+
+    </parallel>  
+    
+  </target>
+
+  <!-- ======================= WATCHDOG: Run Watchdog Tests================ -->
+  
+   <target name="dist-watchdog"  depends="proxyflags" 
+           description="Build watchdog">
+
+   <cvs cvsRoot="${cvsroot}"
+      package="${watchdog.loc}"
+      dest="${base.path}"
+    />
+    
+    <mkdir dir="${watchdog.home}/tmp"/>
+
+    <unjar dest="${watchdog.home}/tmp" src="${servlet-api.home}/lib/servlet-api.jar"/>
+    <unjar dest="${watchdog.home}/tmp" src="${jsp-api.home}/lib/jsp-api.jar"/>
+
+    <jar destfile="${watchdog.home}/servlet.jar"
+         basedir="${watchdog.home}/tmp"
+    />      
+
+    <replace file="${watchdog.home}/build.xml" 
+        token="$${servlet23api.home}/lib/servlet.jar" 
+        value="${watchdog.home}/servlet.jar"/>  
+
+    <replace file="${watchdog.home}/build.xml" 
+        token="$${servlet22api.home}/lib/servlet.jar" 
+        value="${watchdog.home}/servlet.jar"/>  
+
+    <ant dir="${watchdog.home}" target="dist" inheritAll="false"/>
+  </target>
+  
+  <target name="prepare-watchdog">
+    <copy todir="${tomcat.build}/webapps">
+      <fileset dir="${watchdog.home}/dist/webapps"/>
+    </copy>
+  </target>
+  
+  <target name="run-watchdog"
+   description="Watchdog Servlet Container Tests" depends="dist-watchdog,
+                                                           prepare-watchdog">
+    <parallel>
+        <java classname="LauncherBootstrap" fork="yes">
+            <arg value="-launchfile"/>
+            <arg value="catalina.xml"/>
+            <arg value="-verbose"/>
+            <arg value="catalina"/>
+            <arg value="start"/>
+            <classpath>
+                <pathelement path="${java.class.path}"/>
+                <pathelement path="${tomcat.build}/bin"/>
+            </classpath>
+        </java>
+
+        <sequential>
+            <!-- Let tomcat starts before starting Watchdog -->
+            <sleep seconds="15"/>
+
+            <ant dir="${watchdog.home}/dist" target="${watchdog.target}"/>
+
+            <java classname="LauncherBootstrap" fork="yes">
+                <arg value="-launchfile"/>
+                <arg value="catalina.xml"/>
+                <arg value="-verbose"/>
+                <arg value="catalina"/>
+                <arg value="stop"/>
+                <classpath>
+                    <pathelement path="${java.class.path}"/>
+                    <pathelement path="${tomcat.build}/bin"/>
+                </classpath>
+            </java>
+        </sequential>
+    </parallel>  
+    
+  </target>
+
+  <target name="run-watchdog-security"
+   description="Watchdog Servlet Container Tests" depends="dist-watchdog,
+                                                           prepare-watchdog" >
+ 
+    <parallel>
+       <java classname="LauncherBootstrap" fork="yes">
+            <arg value="-launchfile"/>
+            <arg value="catalina.xml"/>
+            <arg value="-verbose"/>
+            <arg value="catalina"/>
+            <arg value="start"/>
+            <classpath>
+                <pathelement path="${java.class.path}"/>
+                <pathelement path="${tomcat.build}/bin"/>
+            </classpath>
+        </java>
+
+        <sequential>
+            <!-- Let tomcat starts before starting Watchdog -->
+            <sleep seconds="60"/>
+
+            <ant dir="${watchdog.home}/dist" target="${watchdog.target}"/>
+
+            <java classname="LauncherBootstrap" fork="yes">
+                <arg value="-launchfile"/>
+                <arg value="catalina.xml"/>
+                <arg value="-verbose"/>
+                <arg value="catalina"/>
+                <arg value="stop"/>
+                <classpath>
+                    <pathelement path="${java.class.path}"/>
+                    <pathelement path="${tomcat.build}/bin"/>
+                </classpath>
+            </java>
+        </sequential>
+    </parallel>  
+    
+  </target>
+
+  <!-- ====================== DIST: Create Directories ==================== -->
+  <target name="dist-prepare">
+    <mkdir dir="${tomcat.dist}"/>
+    <mkdir dir="${tomcat.dist}/bin"/>
+    <mkdir dir="${tomcat.dist}/common"/>
+    <mkdir dir="${tomcat.dist}/common/classes"/>
+    <mkdir dir="${tomcat.dist}/common/endorsed"/>
+    <mkdir dir="${tomcat.dist}/common/lib"/>
+    <mkdir dir="${tomcat.dist}/conf"/>
+    <mkdir dir="${tomcat.dist}/logs"/>
+    <mkdir dir="${tomcat.dist}/server"/>
+    <mkdir dir="${tomcat.dist}/server/classes"/>
+    <mkdir dir="${tomcat.dist}/server/lib"/>
+    <mkdir dir="${tomcat.dist}/shared/classes"/>
+    <mkdir dir="${tomcat.dist}/shared/lib"/>
+    <mkdir dir="${tomcat.dist}/webapps"/>
+    <mkdir dir="${tomcat.dist}/work"/>
+    <mkdir dir="${tomcat.dist}/temp"/>
+  </target>
+
+
+  <!-- ====================== DIST: Copy Static Files ===================== -->
+  <target name="dist-static" depends="dist-prepare">
+
+    <!-- Copy the top-level documentation files -->
+    <copy todir="${tomcat.dist}">
+      <fileset dir=".">
+        <include name="INSTALLING.txt"/>
+        <include name="LICENSE"/>
+        <include name="NOTICE"/>
+        <include name="README.txt"/>
+        <include name="RELEASE*"/>
+        <include name="RUNNING.txt"/>
+      </fileset>
+    </copy>
+
+    <!-- Copy the contents of each "build" directory -->
+    <copy todir="${tomcat.dist}/bin">
+      <fileset dir="${tomcat.build}/bin">
+      	<exclude name="catalina.xml"/>
+      	<exclude name="commons-launcher.jar"/>
+      	<exclude name="*-using-launcher.*"/>
+      	<exclude name="LauncherBootstrap.class"/>
+      	<exclude name="launcher.properties"/>
+      </fileset>
+    </copy>
+    <copy todir="${tomcat.dist}/common/classes">
+      <fileset dir="${tomcat.build}/common/classes" />
+    </copy>
+    <copy todir="${tomcat.dist}/common/endorsed">
+      <fileset dir="${tomcat.build}/common/endorsed" />
+    </copy>
+    <copy todir="${tomcat.dist}/common/i18n">
+      <fileset dir="${tomcat.build}/common/i18n" />
+    </copy>
+    <copy todir="${tomcat.dist}/common/lib">
+      <fileset dir="${tomcat.build}/common/lib" />
+    </copy>
+    <copy todir="${tomcat.dist}/conf">
+      <fileset dir="${tomcat.build}/conf">
+        <exclude name="MANIFEST.MF" />
+      </fileset>
+    </copy>
+    <copy todir="${tomcat.dist}/server/lib">
+      <fileset dir="${tomcat.build}/server/lib" />
+    </copy>
+    <copy todir="${tomcat.dist}/server/webapps">
+      <fileset dir="${tomcat.build}/server/webapps">
+        <exclude name="admin/**/*.jsp" />
+        <exclude name="admin/**/*.jspf" />
+        <exclude name="admin/WEB-INF/classes/**" />
+        <exclude name="admin/WEB-INF/src/**" />
+        <exclude name="manager/WEB-INF/classes/**" />
+      </fileset>
+    </copy>
+<!--
+    <copy todir="${tomcat.dist}/shared/lib">
+      <fileset dir="${tomcat.build}/shared/lib" />
+    </copy>
+-->
+    <copy todir="${tomcat.dist}/webapps">
+      <fileset dir="${tomcat.build}/webapps">
+        <exclude name="**/balancer/WEB-INF/classes/**" />
+        <exclude name="**/WEB-INF/src/**" />
+      </fileset>
+    </copy>
+
+    <!-- Correct permissions and line endings on "bin" scripts -->
+    <fixcrlf srcdir="${tomcat.dist}/bin"   includes="*.sh"  eol="lf"/>
+    <fixcrlf srcdir="${tomcat.dist}/bin"   includes="*.bat" eol="crlf"/>
+    <chmod      dir="${tomcat.dist}/bin"   includes="*.sh"  perm="+x"/>
+
+  </target>
+
+
+  <!-- ====================== DIST: Create Javadoc ======================== -->
+  <target name="dist-javadoc">
+  	<!--
+    <ant dir="${catalina.home}" target="dist-javadoc"/>
+    <mkdir  dir="${tomcat.dist}/webapps/tomcat-docs/catalina/docs/api"/>
+    <copy todir="${tomcat.dist}/webapps/tomcat-docs/catalina/docs/api">
+      <fileset dir="${catalina.build}/javadoc" />
+    </copy>
+    <ant dir="${jasper.home}" target="javadoc"/>
+    <mkdir  dir="${tomcat.dist}/webapps/tomcat-docs/jasper/docs/api"/>
+    <copy todir="${tomcat.dist}/webapps/tomcat-docs/jasper/docs/api">
+      <fileset dir="${jasper.build}/javadoc" />
+    </copy>
+    -->
+    <mkdir  dir="${tomcat.dist}/webapps/tomcat-docs/servletapi"/>
+    <copy todir="${tomcat.dist}/webapps/tomcat-docs/servletapi">
+      <fileset dir="${api.home}/jsr154/dist/docs/api" />
+    </copy>
+    <mkdir  dir="${tomcat.dist}/webapps/tomcat-docs/jspapi"/>
+    <copy todir="${tomcat.dist}/webapps/tomcat-docs/jspapi">
+      <fileset dir="${api.home}/jsr152/dist/docs/api" />
+    </copy>
+  </target>
+
+
+  <!-- ====================== DIST: Create Sources ======================== -->
+  <target name="dist-source">
+
+    <mkdir  dir="${tomcat.dist}/src"/>
+    <mkdir  dir="${tomcat.dist}/src/${api.project}"/>
+    <mkdir  dir="${tomcat.dist}/src/${tomcat.project}"/>
+    <mkdir  dir="${tomcat.dist}/src/${catalina.project}"/>
+    <mkdir  dir="${tomcat.dist}/src/${jtc.project}"/>
+    <mkdir  dir="${tomcat.dist}/src/${jasper.project}"/>
+    <mkdir  dir="${tomcat.dist}/src/${jasper.project}/jasper2"/>
+
+    <!-- Main build file -->
+    <copy todir="${tomcat.dist}/src">
+      <fileset dir="${basedir}/resources">
+        <include name="build.xml" />
+      </fileset>
+    </copy>
+
+    <!-- jakarta-tomcat-5 source -->
+    <copy todir="${tomcat.dist}/src/${tomcat.project}">
+      <fileset dir="${basedir}">
+        <exclude name="build/**"/>
+        <exclude name="dist/**"/>
+        <exclude name="release/**"/>
+        <exclude name="compat/**"/>
+        <exclude name="deployer/**"/>
+        <exclude name="embed/**"/>
+        <exclude name="build.properties"/>
+      </fileset>
+    </copy>
+
+    <!-- jakarta-servletapi-5 source -->
+    <copy todir="${tomcat.dist}/src/${api.project}">
+      <fileset dir="${api.home}">
+        <include name="jsr154/**"/>
+        <include name="jsr152/**"/>
+        <exclude name="**/build/**"/>
+        <exclude name="**/dist/**"/>
+        <exclude name="build.properties"/>
+      </fileset>
+    </copy>
+
+    <!-- jakarta-tomcat-5 source -->
+    <copy todir="${tomcat.dist}/src/${catalina.project}">
+      <fileset dir="${catalina.home}">
+        <exclude name="**/build/**"/>
+        <exclude name="**/dist/**"/>
+      </fileset>
+    </copy>
+
+    <!-- jakarta-tomcat-jasper source -->
+    <copy todir="${tomcat.dist}/src/${jasper.project}/jasper2">
+      <fileset dir="${jasper.home}">
+        <exclude name="**/build/**"/>
+        <exclude name="**/dist/**"/>
+      </fileset>
+    </copy>
+
+    <!-- jakarta-tomcat-connectors source -->
+    <copy todir="${tomcat.dist}/src/${jtc.project}">
+      <fileset dir="${jtc.home}">
+        <exclude name="coyote/build/**"/>
+        <exclude name="http11/build/**"/>
+        <exclude name="jk/build/**"/>
+        <exclude name="util/build/**"/>
+        <exclude name="webapp/**"/>
+        <exclude name="lib/**"/>
+        <exclude name="build.properties"/>
+      </fileset>
+    </copy>
+  </target>
+
+
+  <!-- ====================== DIST: Create Archives ======================= -->
+  <target name="dist" depends="deploy,dist-static,dist-javadoc,embed"
+   description="Create binary distribution">
+  </target>
+
+
+  <!-- ================= DIST: Create Windows Installer =================== -->
+  <target name="installer"
+   description="Create Windows installer" if="execute.installer">
+    <echo message="Builds a Windows installer based on Nullsoft Installer"/>
+    <echo message="NSIS must be installed in the default directory"/>
+    <copy todir="${tomcat.dist}">
+      <fileset dir="resources" />
+    </copy>
+    <copy file="${nsis.installoptions.dll}" todir="${tomcat.dist}" />
+    <copy file="${nsis.nsexec.dll}" todir="${tomcat.dist}" />
+    <copy file="${jtc.home}/procrun/bin/tomcat5.exe" 
+        tofile="${tomcat.dist}/bin/tomcat5.exe" />
+    <copy file="${jtc.home}/procrun/bin/tomcat5w.exe" 
+        tofile="${tomcat.dist}/bin/tomcat5w.exe" />
+    <filter token="VERSION" value="${version}"/>
+    <filter token="VERSION_NUMBER" value="${version.number}"/>
+    <copy file="tomcat.nsi" tofile="${tomcat.dist}/tomcat.nsi" 
+     filtering="true"/>
+    <exec dir="${tomcat.dist}" executable="${nsis.exe}">
+      <arg value="/DNSISDIR=${nsis.home}" />
+      <arg value="tomcat.nsi" />
+    </exec>
+    <move file="${tomcat.dist}/tomcat-installer.exe" 
+     tofile="${tomcat.release}/v${version}/bin/${final.name}.exe" />
+  </target>
+
+
+  <!-- ==================== RELEASE: Create Release ======================= -->
+  <target name="release" depends="clean,dist,dist-source,prepare-release,installer,package-zip,package-tgz,package-embed-zip,package-embed-tgz,package-deployer-zip,package-deployer-tgz,package-compat-zip,package-compat-tgz,package-admin-zip,package-admin-tgz,package-src-zip,package-src-tgz,package-docs-tgz,clean-tester,run-tester"
+   description="Create a Tomcat 5 packaged distribution">
+
+    <filter token="VERSION" value="${version}"/>
+    <copy file="KEYS" 
+         todir="${tomcat.release}/v${version}"/>
+    <copy file="RELEASE-NOTES" 
+         todir="${tomcat.release}/v${version}"
+     filtering="true"/>
+    <copy file="resources/welcome.main.html" 
+        tofile="${tomcat.release}/v${version}/README.html"
+     filtering="true"/>
+    <copy file="resources/welcome.bin.html" 
+        tofile="${tomcat.release}/v${version}/bin/README.html"
+     filtering="true"/>
+
+  </target>
+
+  <target name="package-zip">
+    <zip zipfile="${tomcat.release}/v${version}/bin/${final.name}.zip">
+      <zipfileset dir="${tomcat.dist}" prefix="${final.name}">
+      	<include name="bin/**"/>
+     	<include name="common/**"/>
+     	<include name="conf/**"/>
+     	<include name="logs/**"/>
+     	<include name="server/**"/>
+     	<include name="shared/**"/>
+     	<include name="webapps/**"/>
+     	<include name="work/**"/>
+     	<include name="temp/**"/>
+     	<include name="LICENSE"/>
+     	<include name="NOTICE"/>
+     	<include name="README.txt"/>
+     	<include name="RELEASE-NOTES"/>
+     	<include name="RUNNING.txt"/>
+     	<include name="BENCHMARKS.txt"/>
+      	<exclude name="server/webapps/admin/**"/>
+      	<exclude name="conf/Catalina/localhost/admin.xml"/>
+      </zipfileset>
+    </zip>
+  </target>
+
+  <target name="package-embed-zip">
+    <zip zipfile="${tomcat.release}/v${version}/bin/${final.name}-embed.zip">
+      <zipfileset dir="${tomcat.embed}" prefix="${final.name}-embed" 
+                  includes="**" />
+      <zipfileset dir="${tomcat.dist}" prefix="${final.name}-embed" 
+       includes="LICENSE" />
+      <zipfileset dir="${tomcat.dist}" prefix="${final.name}-embed" 
+       includes="NOTICE" />
+      <zipfileset dir="${tomcat.dist}" prefix="${final.name}-embed" 
+       includes="README.txt" />
+      <zipfileset dir="${tomcat.dist}" prefix="${final.name}-embed" 
+       includes="RELEASE-NOTES" />
+    </zip>
+  </target>
+
+  <target name="package-deployer-zip">
+    <zip zipfile="${tomcat.release}/v${version}/bin/${final.name}-deployer.zip">
+      <zipfileset dir="${tomcat.deployer}" prefix="${final.name}-deployer" 
+                  includes="**" />
+      <zipfileset dir="${tomcat.dist}" prefix="${final.name}-deployer" 
+       includes="LICENSE" />
+      <zipfileset dir="${tomcat.dist}" prefix="${final.name}-deployer" 
+       includes="NOTICE" />
+      <zipfileset dir="${tomcat.dist}" prefix="${final.name}-deployer" 
+       includes="README.txt" />
+      <zipfileset dir="${tomcat.dist}" prefix="${final.name}-deployer" 
+       includes="RELEASE-NOTES" />
+    </zip>
+  </target>
+
+	  <target name="package-compat-zip">
+	    <zip zipfile="${tomcat.release}/v${version}/bin/${final.name}-compat.zip">
+	      <zipfileset dir="${tomcat.compat}" prefix="${final.name}" 
+	                  includes="**" />
+	      <zipfileset dir="${tomcat.dist}" prefix="${final.name}" 
+	       includes="LICENSE" />
+	      <zipfileset dir="${tomcat.dist}" prefix="${final.name}" 
+	       includes="NOTICE" />
+	      <zipfileset dir="${tomcat.dist}" prefix="${final.name}" 
+	       includes="README.txt" />
+	      <zipfileset dir="${tomcat.dist}" prefix="${final.name}" 
+	       includes="RELEASE-NOTES" />
+	    </zip>
+	  </target>
+
+	  <target name="package-admin-zip">
+	    <zip zipfile="${tomcat.release}/v${version}/bin/${final.name}-admin.zip">
+	      <zipfileset dir="${tomcat.dist}" prefix="${final.name}">
+	      	<include name="server/webapps/admin/**"/>
+	      	<include name="conf/Catalina/localhost/admin.xml"/>
+	        <exclude name="*.jsp" />
+	      </zipfileset>
+	      <zipfileset dir="${tomcat.dist}" prefix="${final.name}" 
+	       includes="LICENSE" />
+	      <zipfileset dir="${tomcat.dist}" prefix="${final.name}" 
+	       includes="NOTICE" />
+	      <zipfileset dir="${tomcat.dist}" prefix="${final.name}" 
+	       includes="README.txt" />
+	      <zipfileset dir="${tomcat.dist}" prefix="${final.name}" 
+	       includes="RELEASE-NOTES" />
+	    </zip>
+	  </target>
+
+  <target name="prepare-release">
+
+    <mkdir dir="${tomcat.release}"/>
+    <mkdir dir="${tomcat.release}/v${version}"/>
+    <mkdir dir="${tomcat.release}/v${version}/bin"/>
+    <mkdir dir="${tomcat.release}/v${version}/src"/>
+
+    <condition property="execute.installer">
+      <and>
+        <os family="windows" />
+        <available file="${nsis.exe}" />
+        <available file="${nsis.installoptions.dll}" />
+        <available file="${nsis.nsexec.dll}" />
+      </and>
+    </condition>
+
+  </target>
+
+  <target name="package-tgz">
+    <fixcrlf srcdir="${tomcat.dist}" 
+     includes="*.txt,LICENSE,NOTICE" eol="lf"/>
+    <fixcrlf srcdir="${tomcat.dist}/conf" eol="lf"/>
+    <tar longfile="gnu" compression="gzip"
+          tarfile="${tomcat.release}/v${version}/bin/${final.name}.tar.gz">
+      <tarfileset dir="${tomcat.dist}" mode="755" prefix="${final.name}">
+        <include name="bin/catalina.sh" />
+        <include name="bin/digest.sh" />
+        <include name="bin/jasper.sh" />
+        <include name="bin/jspc.sh" />
+        <include name="bin/setclasspath.sh" />
+        <include name="bin/startup.sh" />
+        <include name="bin/shutdown.sh" />
+        <include name="bin/tool-wrapper.sh" />
+        <include name="bin/tool-wrapper-using-launcher.sh" />
+        <include name="bin/shutdown-using-launcher.sh" />
+        <include name="bin/startup-using-launcher.sh" />
+      </tarfileset>
+      <tarfileset dir="${tomcat.dist}" mode="600" prefix="${final.name}">
+        <include name="conf/**" />
+        <exclude name="conf/Catalina/localhost/admin.xml" />
+      </tarfileset>
+      <tarfileset dir="${tomcat.dist}" prefix="${final.name}">
+        <include name="bin/**" />
+        <include name="common/**" />
+        <include name="logs/**" />
+        <include name="server/**" />
+        <include name="shared/**" />
+        <include name="temp/**" />
+        <include name="webapps/**" />
+        <include name="work/**" />
+        <include name="LICENSE" />
+        <include name="NOTICE" />
+        <include name="README.txt" />
+        <include name="RELEASE-NOTES" />
+        <include name="RUNNING.txt" />
+        <include name="BENCHMARKS.txt" />
+        <exclude name="bin/catalina.sh" />
+        <exclude name="bin/digest.sh" />
+        <exclude name="bin/jasper.sh" />
+        <exclude name="bin/jspc.sh" />
+        <exclude name="bin/setclasspath.sh" />
+        <exclude name="bin/startup.sh" />
+        <exclude name="bin/shutdown.sh" />
+        <exclude name="bin/tool-wrapper.sh" />
+        <exclude name="bin/tool-wrapper-using-launcher.sh" />
+        <exclude name="bin/shutdown-using-launcher.sh" />
+        <exclude name="bin/startup-using-launcher.sh" />
+        <exclude name="conf/**" />
+        <exclude name="src/**" />
+        <exclude name="server/webapps/admin/**" />
+      </tarfileset>
+    </tar>
+  </target>
+
+  <target name="package-embed-tgz">
+    <fixcrlf srcdir="${tomcat.dist}" 
+     includes="*.txt,LICENSE,NOTICE" eol="lf"/>
+    <fixcrlf srcdir="${tomcat.embed}" includes="*.xml" eol="lf"/>
+    <tar longfile="gnu" compression="gzip"
+          tarfile="${tomcat.release}/v${version}/bin/${final.name}-embed.tar.gz">
+      <tarfileset dir="${tomcat.dist}" prefix="${final.name}-embed">
+        <include name="LICENSE" />
+        <include name="NOTICE" />
+        <include name="README.txt" />
+        <include name="RELEASE-NOTES" />
+      </tarfileset>
+      <tarfileset dir="${tomcat.embed}" prefix="${final.name}-embed">
+        <include name="**" />
+      </tarfileset>
+    </tar>
+  </target>
+
+  <target name="package-deployer-tgz">
+    <fixcrlf srcdir="${tomcat.dist}" 
+     includes="*.txt,LICENSE,NOTICE" eol="lf"/>
+    <fixcrlf srcdir="${tomcat.deployer}" includes="*.xml" eol="lf"/>
+    <tar longfile="gnu" compression="gzip"
+          tarfile="${tomcat.release}/v${version}/bin/${final.name}-deployer.tar.gz">
+      <tarfileset dir="${tomcat.dist}" prefix="${final.name}-deployer">
+        <include name="LICENSE" />
+        <include name="NOTICE" />
+        <include name="README.txt" />
+        <include name="RELEASE-NOTES" />
+      </tarfileset>
+      <tarfileset dir="${tomcat.deployer}" prefix="${final.name}-deployer">
+        <include name="**" />
+      </tarfileset>
+    </tar>
+  </target>
+
+	  <target name="package-compat-tgz">
+	    <fixcrlf srcdir="${tomcat.dist}" 
+	     includes="*.txt,LICENSE,NOTICE" eol="lf"/>
+	    <tar longfile="gnu" compression="gzip"
+	          tarfile="${tomcat.release}/v${version}/bin/${final.name}-compat.tar.gz">
+	      <tarfileset dir="${tomcat.dist}" prefix="${final.name}">
+	        <include name="LICENSE" />
+	        <include name="NOTICE" />
+	        <include name="README.txt" />
+	        <include name="RELEASE-NOTES" />
+	      </tarfileset>
+	      <tarfileset dir="${tomcat.compat}" prefix="${final.name}">
+	        <include name="**" />
+	      </tarfileset>
+	    </tar>
+	  </target>
+
+	  <target name="package-admin-tgz">
+	    <fixcrlf srcdir="${tomcat.dist}" 
+	     includes="*.txt,LICENSE,NOTICE" eol="lf"/>
+	    <tar longfile="gnu" compression="gzip"
+	          tarfile="${tomcat.release}/v${version}/bin/${final.name}-admin.tar.gz">
+	      <tarfileset dir="${tomcat.dist}" prefix="${final.name}">
+	        <include name="LICENSE" />
+	        <include name="NOTICE" />
+	        <include name="README.txt" />
+	        <include name="RELEASE-NOTES" />
+	      </tarfileset>
+	      <tarfileset dir="${tomcat.dist}" prefix="${final.name}">
+	        <include name="server/webapps/admin/**" />
+	        <exclude name="*.jsp" />
+	      </tarfileset>
+	        <tarfileset dir="${tomcat.dist}" mode="600" prefix="${final.name}">
+	          <include name="conf/Catalina/localhost/admin.xml" />
+	        </tarfileset>
+	    </tar>
+	  </target>
+
+  <target name="package-docs-tgz">
+    <ant dir="${catalina.home}" target="dist-javadoc"/>
+    <mkdir  dir="${tomcat.dist}/webapps/tomcat-docs/catalina/docs/api"/>
+    <copy todir="${tomcat.dist}/webapps/tomcat-docs/catalina/docs/api">
+      <fileset dir="${catalina.build}/javadoc" />
+    </copy>
+    <ant dir="${jasper.home}" target="javadoc"/>
+    <mkdir  dir="${tomcat.dist}/webapps/tomcat-docs/jasper/docs/api"/>
+    <copy todir="${tomcat.dist}/webapps/tomcat-docs/jasper/docs/api">
+      <fileset dir="${jasper.build}/javadoc" />
+    </copy>
+    <fixcrlf srcdir="${tomcat.dist}" 
+     includes="*.txt,LICENSE,NOTICE" eol="lf"/>
+    <tar longfile="gnu" compression="gzip"
+          tarfile="${tomcat.release}/v${version}/bin/${final.name}-fulldocs.tar.gz">
+      <tarfileset dir="${tomcat.dist}" prefix="tomcat-5.5-doc">
+        <include name="LICENSE" />
+        <include name="NOTICE" />
+        <include name="README.txt" />
+      </tarfileset>
+      <tarfileset dir="${tomcat.dist}/webapps/tomcat-docs" prefix="tomcat-5.5-doc">
+        <include name="**" />
+      </tarfileset>
+    </tar>
+  </target>
+
+ <target name="package-src-zip">
+    <!-- Package Tomcat Source -->
+    <zip zipfile="${tomcat.release}/v${version}/src/${final-src.name}.zip">
+      <zipfileset dir="${tomcat.dist}/src" prefix="${final-src.name}" />
+    </zip>
+  </target>
+
+  <target name="package-src-tgz">
+    <!-- Package Tomcat Source -->
+    <fixcrlf srcdir="${tomcat.dist}" 
+     excludes="**/*.jar,**/*.gif,**/*.bmp,**/*.jpg,**/*.ico" eol="lf"/>
+    <tar longfile="gnu" compression="gzip" 
+          tarfile="${tomcat.release}/v${version}/src/${final-src.name}.tar.gz">
+      <tarfileset dir="${tomcat.dist}/src" mode="755" prefix="${final-src.name}">
+        <include name="${jtc.project}/jk/native/buildconf.sh" />
+        <include name="${jtc.project}/jk/native/apache-1.3/build-hpux-cc.sh" />
+        <include name="${jtc.project}/jk/native/apache-1.3/build-solaris.sh" />
+        <include name="${jtc.project}/jk/native/apache-1.3/build-unix.sh" />
+        <include name="${jtc.project}/jk/native/apache-2.0/build-unix.sh" />
+        <include name="${jtc.project}/jk/native/apache-2.0/install-unix.sh" />
+        <include name="${jtc.project}/jk/native/domino/mkini.sh" />
+      </tarfileset>
+      <tarfileset dir="${tomcat.dist}/src" prefix="${final-src.name}">
+        <exclude name="${jtc.project}/jk/native/buildconf.sh" />
+        <exclude name="${jtc.project}/jk/native/apache-1.3/build-hpux-cc.sh" />
+        <exclude name="${jtc.project}/jk/native/apache-1.3/build-solaris.sh" />
+        <exclude name="${jtc.project}/jk/native/apache-1.3/build-unix.sh" />
+        <exclude name="${jtc.project}/jk/native/apache-2.0/build-unix.sh" />
+        <exclude name="${jtc.project}/jk/native/apache-2.0/install-unix.sh" />
+        <exclude name="${jtc.project}/jk/native/domino/mkini.sh" />
+      </tarfileset>
+    </tar>
+  </target>
+
+  <!-- ==================== Download or build the required binary packages ==================== -->
+
+  <target name="download" depends="proxyflags" 
+          description="Download binary packages" >
+    <mkdir dir="${base.path}" />
+
+    <!-- commons-digester needs ../LICENSE -->
+    <!-- That is ugly XXX needs a review -->
+    <copy file="LICENSE" tofile="../LICENSE"/>
+    <copy file="LICENSE" tofile="${base.path}/LICENSE"/>
+
+    <!-- Downdown any sub package or tools needed. -->
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-beanutils.loc}"/>
+      <param name="destfile" value="${commons-beanutils.jar}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-collections.loc}"/>
+      <param name="destfile" value="${commons-collections.jar}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-el.loc}"/>
+      <param name="destfile" value="${commons-el.jar}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-logging.loc}"/>
+      <param name="destfile" value="${commons-logging.jar}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-modeler.loc}"/>
+      <param name="destfile" value="${commons-modeler.jar}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${log4j.loc}"/>
+      <param name="destfile" value="${log4j.jar}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-digester.loc}"/>
+      <param name="destfile" value="${commons-digester.jar}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-fileupload.loc}"/>
+      <param name="destfile" value="${commons-fileupload.jar}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <!-- xerces2 brings 2 files, test for one of them -->
+      <param name="sourcefile" value="${xerces.loc}"/>
+      <param name="destfile" value="${xml-apis.jar}"/>
+    </antcall>
+
+    <antcall target="downloadzip">
+      <param name="sourcefile" value="${jmx.loc}"/>
+      <param name="destfile" value="${jmx.jar}"/>
+      <param name="destdir" value="${jmx.home}"/>
+    </antcall>
+
+    <antcall target="downloadzip">
+      <param name="sourcefile" value="${junit.loc}"/>
+      <param name="destfile" value="${junit.jar}"/>
+      <param name="destdir" value="${base.path}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-launcher.loc}"/>
+      <param name="destfile" value="${commons-launcher.jar}"/>
+    </antcall>
+
+  	<!--
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-pool.loc}"/>
+      <param name="destfile" value="${commons-pool.jar}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-dbcp.loc}"/>
+      <param name="destfile" value="${commons-dbcp.jar}"/>
+      <param name="destdir" value="${base.path}"/>
+    </antcall>
+    -->
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-httpclient.loc}"/>
+      <param name="destfile" value="${commons-httpclient.jar}"/>
+    </antcall>
+
+    <antcall target="downloadfile">
+      <param name="sourcefile" value="${nsis.loc}"/>
+      <param name="destfile" value="${nsis.exe}"/>
+      <param name="destdir" value="${nsis.home}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${struts.loc}"/>
+      <param name="destfile" value="${struts.jar}"/>
+    </antcall>
+    
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-daemon.loc}"/>
+      <param name="destfile" value="${commons-daemon.jar}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${jaxen.loc}"/>
+      <param name="destfile" value="${jaxen.jar}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${saxpath.loc}"/>
+      <param name="destfile" value="${saxpath.jar}"/>
+    </antcall>
+
+    <!-- Build the dependencies that are not yet released -->
+    <antcall target="build-depends"/>
+
+  </target>
+
+  <target name="proxyflags">
+    <!-- check proxy parameters. -->
+    <condition property="useproxy">
+      <equals arg1="${proxy.use}" arg2="on" />
+    </condition>
+  </target>
+
+  <target name="setproxy"  if="useproxy">
+    <taskdef name="setproxy"
+      classname="org.apache.tools.ant.taskdefs.optional.net.SetProxy" />
+    <setproxy proxyhost="${proxy.host}" proxyport="${proxy.port}"/> 
+    <echo message="Using ${proxy.host}:${proxy.port} to download ${sourcefile}"/>
+  </target>
+
+  <target name="testexist">
+    <echo message="Testing  for ${destfile}"/>
+    <available file="${destfile}" property="exist"/>
+  </target>
+
+  <target name="testsrc">
+    <echo message="Testing  for ${destfile} versus ${cvs.base}/${location}/src"/>
+    <uptodate property="exist"
+              targetfile="${destfile}">
+      <srcfiles dir="${cvs.base}/${location}/src" includes="**" />
+    </uptodate>
+  </target>
+
+  <target name="downloadgz" unless="exist" depends="setproxy,testexist">
+    <!-- Download and extract the package -->
+    <get src="${sourcefile}" dest="${base.path}/file.tar.gz" />
+    <gunzip src="${base.path}/file.tar.gz" dest="${base.path}/file.tar"/>
+    <untar src="${base.path}/file.tar" dest="${base.path}"/>
+    <delete file="${base.path}/file.tar"/>
+    <delete file="${base.path}/file.tar.gz"/>
+  </target>
+
+  <target name="downloadzip" unless="exist" depends="setproxy,testexist">
+    <!-- Download and extract the package -->
+    <get src="${sourcefile}" dest="${base.path}/file.zip" />
+    <mkdir dir="${destdir}" />
+    <unzip src="${base.path}/file.zip" dest="${destdir}"/>
+    <delete file="${base.path}/file.zip"/>
+  </target>
+
+  <target name="downloadfile" unless="exist" depends="setproxy,testexist">
+    <!-- Download extract the file -->
+    <mkdir dir="${destdir}" />
+    <get src="${sourcefile}" dest="${destfile}" />
+  </target>
+
+  <target name="cvsbuild" unless="exist" depends="testsrc">
+    <!-- cvs checkout for all source should be done in "update" -->
+    <!-- build the distribution -->
+    <ant dir="${cvs.base}/${location}" target="dist">
+    </ant>
+    <mkdir dir="${subdir}" />
+    <copy todir="${subdir}" >
+      <fileset dir="${cvs.base}/${location}/dist" includes="**" />
+    </copy>
+  </target>
+
+  <target name="cvsbuild.old" unless="exist" depends="testexist">
+    <!-- cvs checkout and ant dist + copy of jar file -->
+    <echo message="cvs co ${location} ${cvstag} and ant dist in ${subdir}"/>
+    <mkdir dir="${base.path}/tmp"/>
+    <cvs cvsRoot="${cvsroot}"
+      tag="${cvstag}"
+      package="${location}"
+      dest="${base.path}/tmp"
+    />
+    <!-- the software is checked out in ${{base.path}/tmp/${location} -->
+    <move todir="${subdir}">
+      <fileset dir="${base.path}/tmp/${location}/"/>
+    </move>
+    <!-- now build the distribution -->
+    <ant dir="${subdir}" target="dist"/>
+  </target>
+
+  <target name="update"  depends="checkout" />
+
+  <target name="checkout" 
+          description="Update or checkout required sources from CVS">
+    <cvs cvsroot="${cvsroot}" quiet="true"
+         command="checkout -P ${cvstag} jakarta-tomcat-catalina" 
+         dest="${cvs.base}"/>
+    <cvs cvsroot="${cvsroot}" quiet="true"
+         command="checkout -P ${cvstag} jakarta-tomcat-jasper" 
+         dest="${cvs.base}"/>
+    <cvs cvsroot="${cvsroot}" quiet="true"
+         command="checkout -P ${cvstag} jakarta-tomcat-connectors" 
+         dest="${cvs.base}"/>
+    <cvs cvsroot="${cvsroot}" quiet="true"
+         command="checkout -P ${cvstag} jakarta-servletapi-5" 
+         dest="${cvs.base}"/>
+<!-- Use released daemon instead
+    <cvs cvsroot="${cvsroot}" quiet="true"
+         command="checkout -P ${cvstag} ${commons-daemon.cvs.loc}"
+         dest="${cvs.base}"/>
+-->
+<!--
+    <cvs cvsroot="${cvsroot}" quiet="true"
+         command="checkout -P ${cvstag} ${commons-modeler.cvs.loc}" 
+         dest="${cvs.base}"/>
+-->
+  </target>
+
+
+  <!-- ==================== Modeler tricks ==================== 
+  -->
+  <target name="convert-mbeans" description="Hack: regenerate the ser form of the descriptors">
+    <path id="tomcatCP" >
+      <fileset dir="${tomcat.build}/common/lib" includes="*.jar"/>
+      <fileset dir="${tomcat.build}/server/lib" includes="*.jar" />
+    </path>
+    <taskdef resource="org/apache/commons/modeler/ant/ant.properties"
+             classpathref="tomcatCP" />
+
+    <mbeans-descriptors file="${catalina.home}/catalina/src/share/org/apache/catalina/mbeans/mbeans-descriptors.xml"
+              out="build/classes/org/apache/catalina/mbeans/mbeans-descriptors.xml.ser" />
+    <mbeans-descriptors file="${catalina.home}/catalina/src/share/org/apache/catalina/core/mbeans-descriptors.xml"
+              out="build/classes/org/apache/catalina/core/mbeans-descriptors.xml.ser" />
+    <mbeans-descriptors file="${catalina.home}/catalina/src/share/org/apache/catalina/valves/mbeans-descriptors.xml"
+              out="build/classes/org/apache/catalina/valves/mbeans-descriptors.xml.ser" />
+    <mbeans-descriptors file="${jtc.home}/coyote/src/java/org/apache/coyote/tomcat5/mbeans-descriptors.xml"
+              out="build/classes/org/apache/coyote/tomcat5/mbeans-descriptors.xml.ser" />
+  </target>
+
+  <target name="gumpy-build" description="HACK: to stop gumpy from nagging"
+       depends="prepare-release,dist,dist-source,installer,package-zip,package-tgz,package-src-zip,package-src-tgz" />
+
+</project>
diff --git a/build/resources/INSTALLLICENSE b/build/resources/INSTALLLICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/build/resources/INSTALLLICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/build/resources/License.rtf b/build/resources/License.rtf
new file mode 100644
index 0000000..a0fc985
--- /dev/null
+++ b/build/resources/License.rtf
Binary files differ
diff --git a/build/resources/build.xml b/build/resources/build.xml
new file mode 100644
index 0000000..fc910c1
--- /dev/null
+++ b/build/resources/build.xml
@@ -0,0 +1,118 @@
+<project name="Tomcat 5.0 Netbuild" default="build" basedir=".">
+
+
+  <!-- ===================== Initialize Property Values =================== -->
+
+  <!-- See "build.properties.sample" in the top level directory for all     -->
+  <!-- property values you must customize for successful building!!!        -->
+  <property file="${user.home}/build.properties"/>
+  <property file="build.properties"/>
+  <property file="build.properties.default"/>
+
+  <!-- Project Properties -->
+  <property name="name"                  value="Apache Tomcat" />
+  <property name="year"                  value="2003" />
+  <property name="version"               value="5.0" />
+  <property name="project"               value="jakarta-tomcat" />
+  <property name="final.name"            value="${project}-${version}" />
+  <property name="final-src.name"        value="${project}-${version}-src" />
+
+  <!-- CVSROOT -->
+  <property name="cvsroot" 
+           value=":pserver:anoncvs@cvs.apache.org:/home/cvspublic" />
+
+  <!-- Subprojects -->
+  <property name="api.project"           value="jakarta-servletapi-5" />
+  <property name="tomcat.project"        value="jakarta-tomcat-5" />
+  <property name="catalina.project"      value="jakarta-tomcat-catalina" />
+  <property name="jtc.project"           value="jakarta-tomcat-connectors" />
+  <property name="jasper.project"        value="jakarta-tomcat-jasper" />
+
+  <!-- Dependent projects -->
+  <property name="commons-daemon.project"  value="jakarta-commons/daemon" />
+
+  <!-- Source dependencies -->
+  <property name="api.home"
+           value="${basedir}/${api.project}"/>
+  <property name="catalina.home" 
+           value="${basedir}/${catalina.project}"/>
+  <property name="jasper.home"
+           value="${basedir}/${jasper.project}/jasper2"/>
+  <property name="jtc.home"
+           value="${basedir}/${jtc.project}"/>
+  <property name="tomcat.home"
+           value="${basedir}/${tomcat.project}"/>
+
+  <target name="build" depends="check.source,check.source.depends,get.source,get.source.depends"
+   description="Builds all components">
+
+    <ant dir="${tomcat.home}" target="download" />
+    <ant dir="${tomcat.home}" target="deploy" />
+
+  </target>
+
+  <target name="checkout"
+          description="Update or checkout required sources from CVS">
+
+    <echo level="info"
+        message="If the checkout fails, run `cvs -d ${cvsroot} login` and try again. The password for the anonymous CVS access is `anoncvs`" />
+
+    <cvs cvsroot="${cvsroot}" quiet="true"
+         command="checkout -P ${tomcat.project}" 
+         dest="${basedir}" compression="true" />
+    <cvs cvsroot="${cvsroot}" quiet="true"
+         command="checkout -P ${catalina.project}" 
+         dest="${basedir}" compression="true" />
+    <cvs cvsroot="${cvsroot}" quiet="true"
+         command="checkout -P ${jasper.project}" 
+         dest="${basedir}" compression="true" />
+    <cvs cvsroot="${cvsroot}" quiet="true"
+         command="checkout -P ${jtc.project}" 
+         dest="${basedir}" compression="true" />
+    <cvs cvsroot="${cvsroot}" quiet="true"
+         command="checkout -P ${api.project}" 
+         dest="${basedir}" compression="true" />
+
+  </target>
+
+  <target name="checkout.depends"
+          description="Update or checkout dependent sources from CVS">
+
+    <cvs cvsroot="${cvsroot}" quiet="true"
+         command="checkout -P jakarta-commons/LICENSE" 
+         dest="${basedir}" compression="true" />
+    <cvs cvsroot="${cvsroot}" quiet="true"
+         command="checkout -P ${commons-daemon.project}" 
+         dest="${basedir}" compression="true" />
+
+  </target>
+
+  <!-- *************** UTILITY TARGETS *************** -->
+
+  <target name="check.source">
+
+    <available property="source.exists"
+                   file="${basedir}/${tomcat.project}" type="dir" />
+
+  </target>
+
+  <target name="check.source.depends">
+
+    <available property="source.depends.exists" 
+                   file="${basedir}/${commons-daemon.project}" type="dir" />
+
+  </target>
+
+  <target name="get.source" unless="source.exists">
+
+    <antcall target="checkout" />
+
+  </target>
+
+  <target name="get.source.depends" unless="source.depends.exists">
+
+    <antcall target="checkout.depends" />
+
+  </target>
+
+</project>
diff --git a/build/resources/catalina-main.manifest b/build/resources/catalina-main.manifest
new file mode 100644
index 0000000..62f0768
--- /dev/null
+++ b/build/resources/catalina-main.manifest
@@ -0,0 +1,2 @@
+Main-Class: org.apache.catalina.startup.Catalina
+Class-Path: classes/ ../classes/ log4j.jar 
diff --git a/build/resources/config.ini b/build/resources/config.ini
new file mode 100644
index 0000000..1abdad2
--- /dev/null
+++ b/build/resources/config.ini
@@ -0,0 +1,59 @@
+[Settings]
+NumFields=7
+
+[Field 1]
+Type=Label
+Text=HTTP/1.1 Connector Port
+left=0
+right=150
+top=5
+bottom=20
+
+[Field 2]
+Type=Text
+State=8080
+MaxLen=5
+left=150
+right=200
+top=5
+bottom=18
+
+[Field 3]
+Type=Label
+text=Administrator Login
+left=0
+right=300
+top=30
+bottom=45
+
+[Field 4]
+Type=Label
+Text=User Name
+left=0
+right=150
+top=50
+bottom=65
+
+[Field 5]
+Type=Text
+State=admin
+left=150
+right=250
+top=50
+bottom=63
+
+[Field 6]
+Type=Label
+Text=Password
+left=0
+right=150
+top=70
+bottom=85
+
+[Field 7]
+Type=Password
+left=150
+right=250
+top=70
+bottom=83
+
diff --git a/build/resources/confinstall/server_1.xml b/build/resources/confinstall/server_1.xml
new file mode 100644
index 0000000..664a4fb
--- /dev/null
+++ b/build/resources/confinstall/server_1.xml
@@ -0,0 +1,83 @@
+<!-- Example Server Configuration File -->
+<!-- Note that component elements are nested corresponding to their
+     parent-child relationships with each other -->
+
+<!-- A "Server" is a singleton element that represents the entire JVM,
+     which may contain one or more "Service" instances.  The Server
+     listens for a shutdown command on the indicated port.
+
+     Note:  A "Server" is not itself a "Container", so you may not
+     define subcomponents such as "Valves" or "Loggers" at this level.
+ -->
+
+<Server port="8005" shutdown="SHUTDOWN" debug="0">
+
+
+  <!-- Comment these entries out to disable JMX MBeans support -->
+  <!-- You may also configure custom components (e.g. Valves/Realms) by 
+       including your own mbean-descriptor file(s), and setting the 
+       "descriptors" attribute to point to a ';' seperated list of paths
+       (in the ClassLoader sense) of files to add to the default list.
+       e.g. descriptors="/com/myfirm/mypackage/mbean-descriptor.xml"
+  -->
+  <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener"
+            debug="0"/>
+  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"
+            debug="0"/>
+
+  <!-- Global JNDI resources -->
+  <GlobalNamingResources>
+
+    <!-- Test entry for demonstration purposes -->
+    <Environment name="simpleValue" type="java.lang.Integer" value="30"/>
+
+    <!-- Editable user database that can also be used by
+         UserDatabaseRealm to authenticate users -->
+    <Resource name="UserDatabase" auth="Container"
+              type="org.apache.catalina.UserDatabase"
+       description="User database that can be updated and saved"
+           factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
+          pathname="conf/tomcat-users.xml" />
+
+  </GlobalNamingResources>
+
+  <!-- A "Service" is a collection of one or more "Connectors" that share
+       a single "Container" (and therefore the web applications visible
+       within that Container).  Normally, that Container is an "Engine",
+       but this is not required.
+
+       Note:  A "Service" is not itself a "Container", so you may not
+       define subcomponents such as "Valves" or "Loggers" at this level.
+   -->
+
+  <!-- Define the Tomcat Stand-Alone Service -->
+  <Service name="Catalina">
+
+    <!-- A "Connector" represents an endpoint by which requests are received
+         and responses are returned.  Each Connector passes requests on to the
+         associated "Container" (normally an Engine) for processing.
+
+         By default, a non-SSL HTTP/1.1 Connector is established on port 8080.
+         You can also enable an SSL HTTP/1.1 Connector on port 8443 by
+         following the instructions below and uncommenting the second Connector
+         entry.  SSL support requires the following steps (see the SSL Config
+         HOWTO in the Tomcat 5 documentation bundle for more detailed
+         instructions):
+         * If your JDK version 1.3 or prior, download and install JSSE 1.0.2 or
+           later, and put the JAR files into "$JAVA_HOME/jre/lib/ext".
+         * Execute:
+             %JAVA_HOME%\bin\keytool -genkey -alias tomcat -keyalg RSA (Windows)
+             $JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA  (Unix)
+           with a password value of "changeit" for both the certificate and
+           the keystore itself.
+
+         By default, DNS lookups are enabled when a web application calls
+         request.getRemoteHost().  This can have an adverse impact on
+         performance, so you can disable it by setting the
+         "enableLookups" attribute to "false".  When DNS lookups are disabled,
+         request.getRemoteHost() will return the String version of the
+         IP address of the remote client.
+    -->
+
+    <!-- Define a non-SSL HTTP/1.1 Connector on port 8080 -->
+    <Connector 
diff --git a/build/resources/confinstall/server_2.xml b/build/resources/confinstall/server_2.xml
new file mode 100644
index 0000000..c74c2ba
--- /dev/null
+++ b/build/resources/confinstall/server_2.xml
@@ -0,0 +1,276 @@
+               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
+               enableLookups="false" redirectPort="8443" acceptCount="100"
+               debug="0" connectionTimeout="20000" 
+               disableUploadTimeout="true" />
+    <!-- Note : To disable connection timeouts, set connectionTimeout value
+     to 0 -->
+	
+	<!-- Note : To use gzip compression you could set the following properties :
+	
+			   compression="on" 
+			   compressionMinSize="2048" 
+			   noCompressionUserAgents="gozilla, traviata" 
+			   compressableMimeType="text/html,text/xml"
+	-->
+
+    <!-- Define a SSL HTTP/1.1 Connector on port 8443 -->
+    <!--
+    <Connector port="8443" 
+               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
+               enableLookups="false" disableUploadTimeout="true"
+               acceptCount="100" debug="0" scheme="https" secure="true"
+               clientAuth="false" sslProtocol="TLS" />
+    -->
+
+    <!-- Define an AJP 1.3 Connector on port 8009 -->
+    <Connector port="8009" 
+               enableLookups="false" redirectPort="8443" debug="0"
+               protocol="AJP/1.3" />
+
+    <!-- Define a Proxied HTTP/1.1 Connector on port 8082 -->
+    <!-- See proxy documentation for more information about using this. -->
+    <!--
+    <Connector port="8082" 
+               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
+               enableLookups="false"
+               acceptCount="100" debug="0" connectionTimeout="20000"
+               proxyPort="80" disableUploadTimeout="true" />
+    -->
+
+    <!-- An Engine represents the entry point (within Catalina) that processes
+         every request.  The Engine implementation for Tomcat stand alone
+         analyzes the HTTP headers included with the request, and passes them
+         on to the appropriate Host (virtual host). -->
+
+    <!-- You should set jvmRoute to support load-balancing via AJP ie :
+    <Engine name="Standalone" defaultHost="localhost" debug="0" jvmRoute="jvm1">         
+    --> 
+         
+    <!-- Define the top level container in our container hierarchy -->
+    <Engine name="Catalina" defaultHost="localhost" debug="0">
+
+      <!-- The request dumper valve dumps useful debugging information about
+           the request headers and cookies that were received, and the response
+           headers and cookies that were sent, for all requests received by
+           this instance of Tomcat.  If you care only about requests to a
+           particular virtual host, or a particular application, nest this
+           element inside the corresponding <Host> or <Context> entry instead.
+
+           For a similar mechanism that is portable to all Servlet 2.4
+           containers, check out the "RequestDumperFilter" Filter in the
+           example application (the source for this filter may be found in
+           "$CATALINA_HOME/webapps/examples/WEB-INF/classes/filters").
+
+           Request dumping is disabled by default.  Uncomment the following
+           element to enable it. -->
+      <!--
+      <Valve className="org.apache.catalina.valves.RequestDumperValve"/>
+      -->
+
+      <!-- Because this Realm is here, an instance will be shared globally -->
+
+      <!-- This Realm uses the UserDatabase configured in the global JNDI
+           resources under the key "UserDatabase".  Any edits
+           that are performed against this UserDatabase are immediately
+           available for use by the Realm.  -->
+      <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
+                 debug="0" resourceName="UserDatabase"/>
+
+      <!-- Comment out the old realm but leave here for now in case we
+           need to go back quickly -->
+      <!--
+      <Realm className="org.apache.catalina.realm.MemoryRealm" />
+      -->
+
+      <!-- Replace the above Realm with one of the following to get a Realm
+           stored in a database and accessed via JDBC -->
+
+      <!--
+      <Realm  className="org.apache.catalina.realm.JDBCRealm" debug="99"
+             driverName="org.gjt.mm.mysql.Driver"
+          connectionURL="jdbc:mysql://localhost/authority"
+         connectionName="test" connectionPassword="test"
+              userTable="users" userNameCol="user_name" userCredCol="user_pass"
+          userRoleTable="user_roles" roleNameCol="role_name" />
+      -->
+
+      <!--
+      <Realm  className="org.apache.catalina.realm.JDBCRealm" debug="99"
+             driverName="oracle.jdbc.driver.OracleDriver"
+          connectionURL="jdbc:oracle:thin:@ntserver:1521:ORCL"
+         connectionName="scott" connectionPassword="tiger"
+              userTable="users" userNameCol="user_name" userCredCol="user_pass"
+          userRoleTable="user_roles" roleNameCol="role_name" />
+      -->
+
+      <!--
+      <Realm  className="org.apache.catalina.realm.JDBCRealm" debug="99"
+             driverName="sun.jdbc.odbc.JdbcOdbcDriver"
+          connectionURL="jdbc:odbc:CATALINA"
+              userTable="users" userNameCol="user_name" userCredCol="user_pass"
+          userRoleTable="user_roles" roleNameCol="role_name" />
+      -->
+
+      <!-- Define the default virtual host
+           Note: XML Schema validation will not work with Xerces 2.2.
+       -->
+      <Host name="localhost" debug="0" appBase="webapps"
+       unpackWARs="true" autoDeploy="true"
+       xmlValidation="false" xmlNamespaceAware="false">
+
+        <!-- Defines a cluster for this node,
+             By defining this element, means that every manager will be changed.
+             So when running a cluster, only make sure that you have webapps in there
+             that need to be clustered and remove the other ones.
+             A cluster has the following parameters:
+
+             className = the fully qualified name of the cluster class
+
+             name = a descriptive name for your cluster, can be anything
+
+             debug = the debug level, higher means more output
+
+             mcastAddr = the multicast address, has to be the same for all the nodes
+
+             mcastPort = the multicast port, has to be the same for all the nodes
+             
+             mcastBindAddr = bind the multicast socket to a specific address
+             
+             mcastTTL = the multicast TTL if you want to limit your broadcast
+             
+             mcastSoTimeout = the multicast readtimeout 
+
+             mcastFrequency = the number of milliseconds in between sending a "I'm alive" heartbeat
+
+             mcastDropTime = the number a milliseconds before a node is considered "dead" if no heartbeat is received
+
+             tcpThreadCount = the number of threads to handle incoming replication requests, optimal would be the same amount of threads as nodes 
+
+             tcpListenAddress = the listen address (bind address) for TCP cluster request on this host, 
+                                in case of multiple ethernet cards.
+                                auto means that address becomes
+                                InetAddress.getLocalHost().getHostAddress()
+
+             tcpListenPort = the tcp listen port
+
+             tcpSelectorTimeout = the timeout (ms) for the Selector.select() method in case the OS
+                                  has a wakup bug in java.nio. Set to 0 for no timeout
+
+             printToScreen = true means that managers will also print to std.out
+
+             expireSessionsOnShutdown = true means that 
+
+             useDirtyFlag = true means that we only replicate a session after setAttribute,removeAttribute has been called.
+                            false means to replicate the session after each request.
+                            false means that replication would work for the following piece of code:
+                            <%
+                            HashMap map = (HashMap)session.getAttribute("map");
+                            map.put("key","value");
+                            %>
+             replicationMode = can be either 'pooled', 'synchronous' or 'asynchronous'.
+                               * Pooled means that the replication happens using several sockets in a synchronous way. Ie, the data gets replicated, then the request return. This is the same as the 'synchronous' setting except it uses a pool of sockets, hence it is multithreaded. This is the fastest and safest configuration. To use this, also increase the nr of tcp threads that you have dealing with replication.
+                               * Synchronous means that the thread that executes the request, is also the
+                               thread the replicates the data to the other nodes, and will not return until all
+                               nodes have received the information.
+                               * Asynchronous means that there is a specific 'sender' thread for each cluster node,
+                               so the request thread will queue the replication request into a "smart" queue,
+                               and then return to the client.
+                               The "smart" queue is a queue where when a session is added to the queue, and the same session
+                               already exists in the queue from a previous request, that session will be replaced
+                               in the queue instead of replicating two requests. This almost never happens, unless there is a 
+                               large network delay.
+        -->             
+        <!--
+            When configuring for clustering, you also add in a valve to catch all the requests
+            coming in, at the end of the request, the session may or may not be replicated.
+            A session is replicated if and only if all the conditions are met:
+            1. useDirtyFlag is true or setAttribute or removeAttribute has been called AND
+            2. a session exists (has been created)
+            3. the request is not trapped by the "filter" attribute
+
+            The filter attribute is to filter out requests that could not modify the session,
+            hence we don't replicate the session after the end of this request.
+            The filter is negative, ie, anything you put in the filter, you mean to filter out,
+            ie, no replication will be done on requests that match one of the filters.
+            The filter attribute is delimited by ;, so you can't escape out ; even if you wanted to.
+
+            filter=".*\.gif;.*\.js;" means that we will not replicate the session after requests with the URI
+            ending with .gif and .js are intercepted.
+            
+            The deployer element can be used to deploy apps cluster wide.
+            Currently the deployment only deploys/undeploys to working members in the cluster
+            so no WARs are copied upons startup of a broken node.
+            The deployer watches a directory (watchDir) for WAR files when watchEnabled="true"
+            When a new war file is added the war gets deployed to the local instance,
+            and then deployed to the other instances in the cluster.
+            When a war file is deleted from the watchDir the war is undeployed locally 
+            and cluster wide
+        -->
+        
+        <!--
+        <Cluster className="org.apache.catalina.cluster.tcp.SimpleTcpCluster"
+                 managerClassName="org.apache.catalina.cluster.session.DeltaManager"
+                 expireSessionsOnShutdown="false"
+                 useDirtyFlag="true">
+
+            <Membership 
+                className="org.apache.catalina.cluster.mcast.McastService"
+                mcastAddr="228.0.0.4"
+                mcastPort="45564"
+                mcastFrequency="500"
+                mcastDropTime="3000"/>
+
+            <Receiver 
+                className="org.apache.catalina.cluster.tcp.ReplicationListener"
+                tcpListenAddress="auto"
+                tcpListenPort="4001"
+                tcpSelectorTimeout="100"
+                tcpThreadCount="6"/>
+
+            <Sender
+                className="org.apache.catalina.cluster.tcp.ReplicationTransmitter"
+                replicationMode="pooled"/>
+
+            <Valve className="org.apache.catalina.cluster.tcp.ReplicationValve"
+                   filter=".*\.gif;.*\.js;.*\.jpg;.*\.htm;.*\.html;.*\.txt;"/>
+                   
+            <Deployer className="org.apache.catalina.cluster.deploy.FarmWarDeployer"
+                      tempDir="/tmp/war-temp/"
+                      deployDir="/tmp/war-deploy/"
+                      watchDir="/tmp/war-listen/"
+                      watchEnabled="false"/>
+        </Cluster>
+        -->        
+
+
+
+        <!-- Normally, users must authenticate themselves to each web app
+             individually.  Uncomment the following entry if you would like
+             a user to be authenticated the first time they encounter a
+             resource protected by a security constraint, and then have that
+             user identity maintained across *all* web applications contained
+             in this virtual host. -->
+        <!--
+        <Valve className="org.apache.catalina.authenticator.SingleSignOn"
+                   debug="0"/>
+        -->
+
+        <!-- Access log processes all requests for this virtual host.  By
+             default, log files are created in the "logs" directory relative to
+             $CATALINA_HOME.  If you wish, you can specify a different
+             directory with the "directory" attribute.  Specify either a relative
+             (to $CATALINA_HOME) or absolute path to the desired directory.
+        -->
+        <!--
+        <Valve className="org.apache.catalina.valves.AccessLogValve"
+                 directory="logs"  prefix="localhost_access_log." suffix=".txt"
+                 pattern="common" resolveHosts="false"/>
+        -->
+
+      </Host>
+
+    </Engine>
+
+  </Service>
+
+</Server>
diff --git a/build/resources/confinstall/tomcat-users_1.xml b/build/resources/confinstall/tomcat-users_1.xml
new file mode 100644
index 0000000..8ea182c
--- /dev/null
+++ b/build/resources/confinstall/tomcat-users_1.xml
@@ -0,0 +1,6 @@
+<!--
+  NOTE:  By default, no user is included in the "manager" role required
+  to operate the "/manager" web application.  If you wish to use this app,
+  you must define such a user - the username and password are arbitrary.
+-->
+<tomcat-users>
diff --git a/build/resources/confinstall/tomcat-users_2.xml b/build/resources/confinstall/tomcat-users_2.xml
new file mode 100644
index 0000000..83bc06c
--- /dev/null
+++ b/build/resources/confinstall/tomcat-users_2.xml
@@ -0,0 +1,4 @@
+  <user name="tomcat" password="tomcat" roles="tomcat" />
+  <user name="role1"  password="tomcat" roles="role1"  />
+  <user name="both"   password="tomcat" roles="tomcat,role1" />
+</tomcat-users>
diff --git a/build/resources/deployer/build.xml b/build/resources/deployer/build.xml
new file mode 100644
index 0000000..8425651
--- /dev/null
+++ b/build/resources/deployer/build.xml
@@ -0,0 +1,101 @@
+<project name="Deployer" default="compile" basedir=".">
+
+  <property file="deployer.properties"/>
+
+  <!-- Configure the directory into which the web application is built -->
+  <property name="build"    value="${basedir}/build"/>
+
+  <!-- Configure the folder and context path for this application -->
+  <property name="webapp"   value="myapp"/>
+  <property name="path"     value="/myapp"/>
+
+  <!-- Configure properties to access the Manager application -->
+  <property name="url"      value="http://localhost:8080/manager"/>
+  <property name="username" value="tomcat"/>
+  <property name="password" value="tomcat"/>
+
+  <property name="webapp.path"     value="${build}/webapp${path}"/>
+
+  <path id="deployer.classpath">
+    <fileset dir="${basedir}/lib">
+      <include name="*.jar"/>
+    </fileset>
+  </path>
+
+  <!-- Configure the custom Ant tasks for the Manager application -->
+  <taskdef resource="org/apache/catalina/ant/catalina.tasks"
+           classpathref="deployer.classpath"/>
+
+  <!-- Executable Targets -->
+  <target name="clean" description="Removes build directory">
+    <delete dir="${build}" />
+  </target>
+
+  <target name="compile" description="Compile web application"
+          depends="clean">
+
+    <copy todir="${webapp.path}">
+      <fileset dir="${webapp}" />
+    </copy>
+
+    <jasper2 validateXml="false" 
+             uriroot="${webapp.path}" 
+             webXmlFragment="${webapp.path}/WEB-INF/generated_web.xml"
+             addWebXmlMappings="true"
+             outputDir="${webapp.path}/WEB-INF/classes" /> 
+
+    <validator path="${webapp.path}" />
+
+    <mkdir dir="${webapp.path}/WEB-INF/classes"/>
+    <mkdir dir="${webapp.path}/WEB-INF/lib"/>
+
+    <javac destdir="${webapp.path}/WEB-INF/classes"
+           optimize="off"
+           debug="${compile.debug}"
+           deprecation="${compile.deprecation}"
+           failonerror="false"
+           srcdir="${webapp.path}/WEB-INF/classes"
+           encoding="UTF-8"
+	   excludes="**/*.smap">
+      <classpath>
+        <fileset dir="${webapp.path}/WEB-INF/lib">
+          <include name="*.jar"/>
+        </fileset>
+        <fileset dir="${basedir}/lib">
+          <include name="*.jar"/>
+        </fileset>
+      </classpath>
+      <include name="**" />
+      <exclude name="tags/**" />
+    </javac>
+
+    <jar destfile="${webapp.path}.war"
+         basedir="${webapp.path}" />
+
+  </target>
+
+  <target name="deploy" description="Deploy web application">
+    <deploy url="${url}" username="${username}" password="${password}"
+            path="${path}" war="${webapp.path}.war" update="true" />
+  </target>
+
+  <target name="undeploy" description="Undeploy web application">
+    <undeploy url="${url}" username="${username}" password="${password}"
+              path="${path}"/>
+  </target>
+
+  <!-- Webapp lifecycle control -->
+  <target name="start" description="Start web application">
+    <start url="${url}" username="${username}" password="${password}"
+           path="${path}"/>
+  </target>
+  <target name="reload" description="Reload web application">
+    <reload url="${url}" username="${username}" password="${password}"
+            path="${path}"/>
+  </target>
+  <target name="stop" description="Stop web application">
+    <stop url="${url}" username="${username}" password="${password}"
+          path="${path}"/>
+  </target>
+
+</project>
diff --git a/build/resources/header.bmp b/build/resources/header.bmp
new file mode 100644
index 0000000..b19e056
--- /dev/null
+++ b/build/resources/header.bmp
Binary files differ
diff --git a/build/resources/jvm.ini b/build/resources/jvm.ini
new file mode 100644
index 0000000..f3e57e5
--- /dev/null
+++ b/build/resources/jvm.ini
@@ -0,0 +1,18 @@
+[Settings]
+NumFields=2
+
+[Field 1]
+Type=Label
+Text=Please select the path of a J2SE 5.0 JRE installed on your system:
+left=0
+right=300
+top=5
+bottom=20
+
+[Field 2]
+Type=DirRequest
+Left=0
+Right=-1
+Top=65
+Bottom=78
+
diff --git a/build/resources/log4j.properties b/build/resources/log4j.properties
new file mode 100644
index 0000000..d0f8331
--- /dev/null
+++ b/build/resources/log4j.properties
@@ -0,0 +1,9 @@
+log4j.rootCategory=info, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout.ConversionPattern=%r [%t] %-5p\: %-15c{2} - %m%n
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+
+#log4j.category.org.apache.catalina=info
+
+#log4j.category.org.apache.jasper.compiler.Compiler=debug
diff --git a/build/resources/main.ico b/build/resources/main.ico
new file mode 100644
index 0000000..dbf6e72
--- /dev/null
+++ b/build/resources/main.ico
Binary files differ
diff --git a/build/resources/mbeans/tomcat5-ant.xml b/build/resources/mbeans/tomcat5-ant.xml
new file mode 100644
index 0000000..fbd68a4
--- /dev/null
+++ b/build/resources/mbeans/tomcat5-ant.xml
@@ -0,0 +1,300 @@
+<project name="tomcat-embed" default="start" basedir=".">
+
+  <property file="${user.home}/build.properties"/>
+  <property file="build.properties"/>
+
+  <property name="tomcat.home" location="." />
+  <!-- This matches the build struct with build and embed at the same level -->
+  <property name="tomcat.webapps" location="${tomcat.home}/webapps" />
+
+  <path id="tomcatCP-extra" /> 
+
+  <target name="init" unless="init.done">
+
+    <path id="tomcatCP" >
+      <path refid="tomcatCP-extra"/>
+
+      <!-- Just include everything for now -->
+      <fileset dir="${tomcat.home}/lib" includes="*.jar"/>
+      <pathelement  path="${tomcat.home}/conf"/>
+      <!-- my log4j.properties -->
+      <pathelement  path="/ws/log4j"/>
+    </path>
+
+    <taskdef resource="org/apache/commons/modeler/ant/ant.properties"
+             classpathref="tomcatCP" />
+    <property name="init.done" value="true"/>
+
+    <available classname="com.sun.jdmk.comm.HtmlAdaptorServer"
+               property="jmxritools-available" 
+               classpathref="tomcatCP" />
+
+    <available classname="mx4j.adaptor.http.XSLTProcessor"
+               property="mx4jtools-available" 
+               classpathref="tomcatCP" />
+  </target>
+
+
+  <!-- ==================== Console - for debugging. ==================== 
+       Call this target if you want the console added.
+  -->
+
+  <target name="jmx-console" depends="jmx-console-ri,jmx-console-mx4j" />
+
+  <target name="jmx-console-ri" 
+          depends="init" 
+          description="Enable JMX-RI console ( web interface )" 
+          if="jmxritools-available">
+
+    <jmx-service>
+      <mbean code="com.sun.jdmk.comm.HtmlAdaptorServer"
+             name="jmx-console:type=HtmlAdaptorServer,port=9988">
+         <attribute name="Port" value="9988"/>
+     </mbean>
+    </jmx-service>    
+
+  </target>
+
+  <target name="jmx-console-mx4j" 
+          depends="init" 
+          description="Enable JMX console ( mx4j )" 
+          if="mx4jtools-available">
+
+    <jmx-service>
+      <mbean code="mx4j.adaptor.http.XSLTProcessor"
+             name="Http:name=XSLTProcessor">
+      </mbean>
+      <mbean code="mx4j.adaptor.http.HttpAdaptor"
+             name="Http:name=HttpAdaptor">
+         <attribute name="Port" value="9998"/>
+         <attribute name="ProcessorName" value="Http:name=XSLTProcessor"/>
+     </mbean>
+    </jmx-service>    
+
+  </target>
+
+  <!-- ======================= Proxy test ================ -->
+  <target name="proxy-run" depends="init,jmx-console,proxy" />
+
+  <target name="proxy" depends="init"
+        description="Start a JMX proxy service">
+    <property name="localDomain" value="catalina.proxy" />
+    <property name="remoteDomain" value="catalina" />
+
+    <jmx-service>
+       <mbean code="org.apache.commons.modeler.mbeans.SimpleRemoteConnector"
+              name="modjk:type=jmxProxy,name=apache80" 
+              modeler="true" >
+         <attribute name="webServerPort"  value="80"/>
+         <attribute name="statusPath"  value="/jkstatus"/>
+         <attribute name="filter"  value="*"/>
+         <attribute name="user"  value="tomcat"/>
+         <attribute name="pass"  value="tomcat"/>
+       </mbean>
+    </jmx-service>
+   
+    <jmx-service>
+       <mbean code="org.apache.commons.modeler.mbeans.SimpleRemoteConnector"
+              name="modjk:type=jmxProxy,name=apache8003" 
+              modeler="true" >
+         <attribute name="webServerPort"  value="8003"/>
+         <attribute name="statusPath"  value="/jkstatus"/>
+         <attribute name="filter"  value="*"/>
+         <attribute name="user"  value="tomcat"/>
+         <attribute name="pass"  value="tomcat"/>
+       </mbean>
+    </jmx-service>
+
+    <jmx-service>
+       <mbean code="org.apache.commons.modeler.mbeans.SimpleRemoteConnector"
+              name="Catalina9080:type=jmxProxy,name=tomcat9080" 
+              modeler="true" >
+         <attribute name="webServerPort"  value="9080"/>
+         <attribute name="statusPath"  value="/manager/jmxproxy"/>
+         <attribute name="domain"  value="Catalina"/>
+         <attribute name="user"  value="tomcat"/>
+         <attribute name="pass"  value="tomcat"/>
+       </mbean>
+    </jmx-service>
+
+    <jmx-service>
+       <mbean code="org.apache.commons.modeler.mbeans.SimpleRemoteConnector"
+              name="Catalina:type=jmxProxy,name=tomcat8080" 
+              modeler="true" >
+         <attribute name="webServerPort"  value="8080"/>
+         <attribute name="statusPath"  value="/manager/jmxproxy"/>
+         <attribute name="domain"  value="Catalina"/>
+         <attribute name="user"  value="tomcat"/>
+         <attribute name="pass"  value="tomcat"/>
+       </mbean>
+    </jmx-service>
+
+
+    <echo message="Proxy running"/>
+    <sleep hours="1"/>
+  </target>
+
+
+  <!-- ======================= Server.xml based - backward compat ================ -->
+
+  <property name="domain" value="Catalina" />
+  <property name="jsr77Domain" value="Catalina" />
+
+  <!-- ======================= JMX only  ================ -->
+
+  <target name="run" depends="init,jmx-console"
+        description="Start tomcat as an mbean, no server.xml">
+
+    <modelerRegistry resource="org/apache/catalina/mbeans/mbeans-descriptors.xml" />
+    <modelerRegistry resource="org/apache/catalina/loader/mbeans-descriptors.xml" />
+    <mkdir dir="${tomcat.home}/work/${domain}/" />
+
+    <jmx-service>
+<!--
+      Should be optional - but the name is used in several places.
+      The whole name should disapear - use domain instead
+-->
+       <mbean name="${domain}:type=Server" 
+              code="org.apache.catalina.core.StandardServer"
+              modeler="true">
+         <attribute name="port" value="9005"/>
+       </mbean>
+
+       <mbean name="${domain}:type=Service" 
+              code="org.apache.catalina.core.StandardService"
+              modeler="true">
+         <attribute name="name" value="Tomcat-Standalone"/>
+       </mbean>
+
+
+       <mbean name="${domain}:type=Engine" 
+              code="org.apache.catalina.core.StandardEngine"
+              modeler="true">
+         <attribute name="name" value="Tomcat-Standalone"/>
+         <attribute name="baseDir" value="${tomcat.home}"/>
+         <attribute name="defaultHost" value="localhost"/>
+       </mbean>
+
+<!--
+       <mbean name="${domain}:type=Realm" 
+              code="org.apache.catalina.realm.JAASRealm" modeler="true">
+       </mbean>
+-->
+       <mbean name="${domain}:type=Realm" 
+              code="org.apache.catalina.realm.MemoryRealm" modeler="true">
+         <attribute name="pathname"
+                    value="${tomcat.home}/conf/tomcat-users.xml" />
+       </mbean>
+
+<!--
+       <mbean name="${domain}:type=Connector,port=9009" 
+              code="org.apache.coyote.tomcat5.CoyoteConnector"
+              modeler="true">
+         <attribute name="protocolHandlerClassName"
+                    value="org.apache.jk.server.JkCoyoteHandler" />
+       </mbean>
+-->
+
+       <mbean name="${domain}:type=Channel,name=ChannelUn" 
+              code="org.apache.jk.common.ChannelUn"
+              modeler="true">
+          <attribute name="file" value="${tomcat.home}/work/${domain}/jk2.channel" />
+       </mbean>
+
+
+       <mbean name="${domain}:type=Connector,port=9080" 
+              code="org.apache.coyote.tomcat5.CoyoteConnector"
+              modeler="true">
+          <attribute name="port" value="9080" />
+       </mbean>
+
+
+<!--
+       <mbean name="${domain}:type=DefaultContext,host=localhost,service=Tomcat-Standalone" 
+              code="org.apache.catalina.core.StandardDefaultContext"
+              modeler="true">
+       </mbean>
+-->
+
+
+<!-- Optional: when the context is created it'll create a host if none is found.
+-->
+       <mbean name="${domain}:type=Host,host=localhost" 
+              code="org.apache.catalina.core.StandardHost" modeler="true">
+         <attribute name="name" value="localhost"/>
+       </mbean>
+
+
+       <mbean name="${jsr77Domain}:j2eeType=WebModule,name=//localhost/,J2EEApplication=none,J2EEServer=none" 
+              code="org.apache.catalina.core.StandardContext"  modeler="true">
+         <attribute name="docBase" value="${tomcat.webapps}/ROOT" />
+         <!-- Required for now -->
+         <attribute name="privileged" value="true" />
+         <attribute name="engineName" value="${domain}" />
+       </mbean>
+
+<!-- 
+       <mbean name="${jsr77Domain}:j2eeType=WebModule,name=//localhost/servlets-examples,J2EEApplication=none,J2EEServer=none" 
+              code="org.apache.catalina.core.StandardContext"  modeler="true">
+         <attribute name="docBase" value="${tomcat.webapps}/servlets-examples" />
+         <attribute name="privileged" value="true" />
+         <attribute name="engineName" value="${domain}" />
+       </mbean>
+
+       <mbean name="${jsr77Domain}:j2eeType=WebModule,name=//localhost/jsp-examples,J2EEApplication=none,J2EEServer=none" 
+              code="org.apache.catalina.core.StandardContext"  modeler="true">
+         <attribute name="docBase" value="${tomcat.webapps}/jsp-examples" />
+         <attribute name="privileged" value="true" />
+         <attribute name="engineName" value="${domain}" />
+       </mbean>
+
+       <mbean name="${jsr77Domain}:j2eeType=WebModule,name=//localhost/tomcat-docs,J2EEApplication=none,J2EEServer=none" 
+              code="org.apache.catalina.core.StandardContext"  modeler="true">
+         <attribute name="docBase" value="${tomcat.webapps}/tomcat-docs" />
+         <attribute name="privileged" value="true" />
+         <attribute name="engineName" value="${domain}" />
+       </mbean>
+-->
+
+       <mbean name="${jsr77Domain}:j2eeType=WebModule,name=//localhost/manager,J2EEApplication=none,J2EEServer=none" 
+              code="org.apache.catalina.core.StandardContext"  modeler="true">
+         <attribute name="docBase" value="${tomcat.webapps}/manager" />
+         <attribute name="engineName" value="${domain}" />
+         <attribute name="privileged" value="true" />
+       </mbean>
+
+       <!-- The realm must be declared after the web module (for now). -->
+       <mbean name="${domain}:type=Realm,host=localhost,path=manager" 
+              code="org.apache.catalina.realm.MemoryRealm" modeler="true">
+         <attribute name="pathname"
+                    value="${tomcat.home}/conf/tomcat-users.xml" />
+       </mbean>
+
+
+<!--
+       <mbean name="${domain}:j2eeType=WebModule,name=//localhost/admin,J2EEApplication=none,J2EEServer=none" 
+              code="org.apache.catalina.core.StandardContext"  modeler="true">
+         <attribute name="docBase" value="${tomcat.webapps}/admin" />
+         <attribute name="privileged" value="true" />
+       </mbean>
+-->
+
+    </jmx-service>
+  </target>
+
+
+  <!-- ==================== Await ==================== 
+       Call this target if you want the build file to hang in "await". Tomcat stop or ^C will stop the ant execution.
+    -->
+  <target name="await" depends="init"
+        description="Wait for tomcat stop. Call this target after run">
+    <jmx objectName="${domain}:type=Server"
+         operation="await" />
+    <sleep hours="1"/>
+  </target>
+
+  <target name="start"
+              depends="init,run,await" 
+  			  description="Start tomcat, wait for stop message"/>
+
+</project>
diff --git a/build/resources/side_left.bmp b/build/resources/side_left.bmp
new file mode 100644
index 0000000..cccb01f
--- /dev/null
+++ b/build/resources/side_left.bmp
Binary files differ
diff --git a/build/resources/tickno.bmp b/build/resources/tickno.bmp
new file mode 100644
index 0000000..64a6749
--- /dev/null
+++ b/build/resources/tickno.bmp
Binary files differ
diff --git a/build/resources/tickyes.bmp b/build/resources/tickyes.bmp
new file mode 100644
index 0000000..7b5d420
--- /dev/null
+++ b/build/resources/tickyes.bmp
Binary files differ
diff --git a/build/resources/tomcat.ico b/build/resources/tomcat.ico
new file mode 100644
index 0000000..6c5bd2c
--- /dev/null
+++ b/build/resources/tomcat.ico
Binary files differ
diff --git a/build/resources/tomcat.spec b/build/resources/tomcat.spec
new file mode 100644
index 0000000..3939b9e
--- /dev/null
+++ b/build/resources/tomcat.spec
@@ -0,0 +1,89 @@
+%define version 5.0.25
+%define base /opt/jakarta-tomcat-%{version}
+
+Summary: Apache Jakarta Servlet/JSP server 
+Name: jakarta-tomcat
+Version: %{version}
+Release: 1asf
+Vendor: Apache Software Foundation
+Group: System Environment/Daemons
+Copyright: Apache License
+BuildArch: noarch
+URL: http://jakarta.apache.org/tomcat
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot 
+Provides: jakarta-tomcat tomcat tomcat5
+
+# No requires - Java should be installed, but it can be installed for Sun's site as binary ( no RPM ) or 
+# in some other form. Adding dependency would require the user to install some dummy java RPM  he doesn't want
+# 
+
+# This is a complete distribution - using exactly the same jar files that we used when testing tomcat.
+# While this may create duplication with other RPMs distributing Xerces or MX4J - our goal is to 
+# have the most stable tomcat, even if you waste few megs of  hard drive space. 
+
+# TODO: decide over /opt or /usr/local
+Prefix: /opt
+
+# We build from the official binary distribution - it should produce identical result as on all other OSes and linux variants.
+Source: http://www.apache.org/dist/jakarta/tomcat-5/v%{version}/bin/jakarta-tomcat-%{version}.tar.gz
+
+%description
+Servlet container for developing Web applications in Java. Used as basis for the Reference Implementation 
+of the Servlet and JSP specificiations. This RPM mirrors the layout used by official Tomcat distributions
+on all other OSes and linux variants. 
+
+If you want a FHS-based package - be warned that tomcat may not work very well. ( TODO: add more warnings !).
+
+If you still want FHS-based package - please use jpackage.org or an RPM that is using the same exact layout.
+
+
+%prep
+%setup -q -n jakarta-tomcat-%{version} -T -b 0
+
+%build
+
+
+%install
+# cd jakarta-tomcat-%{version}
+
+install -d %{buildroot}/opt/jakarta-tomcat-%{version}
+cp -a * ${RPM_BUILD_ROOT}/opt/jakarta-tomcat-%{version}
+
+%clean
+
+%pre
+grep '^tomcat:' /etc/group > /dev/null
+if [ $? eq 1 ] ; then 
+  %{_sbindir}/groupadd tomcat
+fi
+
+grep '^tomcat:' /etc/passwd > /dev/null
+
+if [ $? eq 1 ] ; then 
+  %{_sbindir}/useradd -c "Tomcat Server" -d /opt/jakarta-tomcat-4 -g tomcat tomcat 
+fi
+
+
+%post
+ln -s /opt/jakarta-tomcat-%{version}/bin/catalina.sh /etc/init.d
+
+%preun
+
+%files
+%defattr(-,tomcat,tomcat,0755)
+%config(noreplace) %{base}/conf
+%{base}/NOTICE
+%{base}/LICENSE
+%{base}/RELEASE-NOTES
+%{base}/*.txt
+%{base}/bin
+%{base}/server
+%{base}/common
+%{base}/temp
+%{base}/webapps
+
+
+%changelog
+* Mon May 17 2004 Costin Manolache <costin@apache.org> 0:4.1.30-1asf
+- Initial version
+  
diff --git a/build/resources/uninst.ico b/build/resources/uninst.ico
new file mode 100644
index 0000000..ada4df8
--- /dev/null
+++ b/build/resources/uninst.ico
Binary files differ
diff --git a/build/resources/welcome.bin.html b/build/resources/welcome.bin.html
new file mode 100644
index 0000000..bb19b40
--- /dev/null
+++ b/build/resources/welcome.bin.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML><HEAD><TITLE>Apache Tomcat @VERSION@</TITLE>
+<META http-equiv=Content-Type content="text/html; charset=windows-1252">
+</HEAD>
+<BODY>
+<P>
+<H3>Apache Tomcat @VERSION@</H3>
+<p></p>
+<p>This version of Tomcat can use the AJP 1.3 protocol or JNI to connect to 
+native webservers. Among the native connectors available from the Jakarta 
+Tomcat Connectors subproject, using the JK 2 native connector 
+is recommended. Binaries can be downloaded 
+<a href="http://jakarta.apache.org/builds/jakarta-tomcat-connectors/jk2/release">here</a>.</p>
+<p><b>NOTE: The tar files in this distribution use GNU tar extensions, 
+and must be untarred with a GNU compatible version of tar. The version 
+of <CODE>tar</CODE> on Solaris and Mac OS X will not work with 
+these files.</b></P>
+<P></P></BODY></HTML>
diff --git a/build/resources/welcome.main.html b/build/resources/welcome.main.html
new file mode 100644
index 0000000..5b1a968
--- /dev/null
+++ b/build/resources/welcome.main.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML><HEAD><TITLE>Apache Tomcat @VERSION@</TITLE>
+<META http-equiv=Content-Type content="text/html">
+</HEAD>
+<BODY>
+<P>
+<H3>Apache Tomcat @VERSION@</H3>
+<P></P>
+<p>Useful references:
+<ul>
+<li><a href="RELEASE-NOTES">Release notes</a>, with important information 
+about known issues</li>
+<li><a href="http://jakarta.apache.org/tomcat/tomcat-5.0-doc/changelog.html">Changelog</a></li>
+<li><a href="http://jakarta.apache.org/tomcat/tomcat-5.0-doc/status.html">Status</a></li>
+</ul>
+</p>
+<p><b>NOTE: The tar files in this distribution use GNU tar extensions, 
+and must be untarred with a GNU compatible version of tar. The version 
+of <CODE>tar</CODE> on Solaris and Mac OS X will not work with 
+these files.</b></P>
+<P>Thank you for using <A href="http://jakarta.apache.org/tomcat">Tomcat</A>!. 
+</P>
+<P><B>The Apache Jakarta Project</B> <BR><A 
+href="http://jakarta.apache.org/">http://jakarta.apache.org/</A> </P>
+<P>
+<P></P></BODY></HTML>
diff --git a/build/tomcat.nsi b/build/tomcat.nsi
new file mode 100644
index 0000000..4c93050
--- /dev/null
+++ b/build/tomcat.nsi
@@ -0,0 +1,609 @@
+
+; Tomcat script for Nullsoft Installer
+; $Id$
+
+  ;Compression options
+  CRCCheck on
+  SetCompress force
+  SetCompressor lzma
+  SetDatablockOptimize on
+
+  Name "Apache Tomcat"
+
+  ;Product information
+  VIAddVersionKey ProductName "Apache Tomcat"
+  VIAddVersionKey CompanyName "Apache Software Foundation"
+  VIAddVersionKey LegalCopyright "Copyright (c) 1999-2004 The Apache Software Foundation"
+  VIAddVersionKey FileDescription "Apache Tomcat Installer"
+  VIAddVersionKey FileVersion "2.0"
+  VIAddVersionKey ProductVersion "@VERSION@"
+  VIAddVersionKey Comments "jakarta.apache.org/tomcat"
+  VIAddVersionKey InternalName "jakarta-tomcat-@VERSION@.exe"
+  VIProductVersion @VERSION_NUMBER@
+
+!include "MUI.nsh"
+!include "StrFunc.nsh"
+${StrRep}
+  Var "JavaHome"
+
+
+
+;--------------------------------
+;Configuration
+
+  !define MUI_HEADERIMAGE
+  !define MUI_HEADERIMAGE_RIGHT
+  !define MUI_HEADERIMAGE_BITMAP header.bmp
+  !define MUI_WELCOMEFINISHPAGE_BITMAP side_left.bmp 
+  !define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\webapps\ROOT\RELEASE-NOTES.txt"
+  !define MUI_FINISHPAGE_RUN $INSTDIR\bin\tomcat5w.exe
+  !define MUI_FINISHPAGE_RUN_PARAMETERS //MS//Tomcat5
+  !define MUI_FINISHPAGE_NOREBOOTSUPPORT
+
+  !define MUI_ABORTWARNING
+
+  !define TEMP1 $R0
+  !define TEMP2 $R1
+
+  !define MUI_ICON tomcat.ico
+  !define MUI_UNICON tomcat.ico
+
+  ;General
+  OutFile tomcat-installer.exe
+
+  ;Install Options pages
+  LangString TEXT_JVM_TITLE ${LANG_ENGLISH} "Java Virtual Machine"
+  LangString TEXT_JVM_SUBTITLE ${LANG_ENGLISH} "Java Virtual Machine path selection."
+  LangString TEXT_JVM_PAGETITLE ${LANG_ENGLISH} ": Java Virtual Machine path selection"
+
+  LangString TEXT_CONF_TITLE ${LANG_ENGLISH} "Configuration"
+  LangString TEXT_CONF_SUBTITLE ${LANG_ENGLISH} "Tomcat basic configuration."
+  LangString TEXT_CONF_PAGETITLE ${LANG_ENGLISH} ": Configuration Options"
+
+  ;Install Page order
+  !insertmacro MUI_PAGE_WELCOME
+  !insertmacro MUI_PAGE_LICENSE INSTALLLICENSE
+  !insertmacro MUI_PAGE_COMPONENTS
+  !insertmacro MUI_PAGE_DIRECTORY
+  Page custom SetConfiguration Void "$(TEXT_CONF_PAGETITLE)"
+  Page custom SetChooseJVM Void "$(TEXT_JVM_PAGETITLE)"
+  !insertmacro MUI_PAGE_INSTFILES
+  !insertmacro MUI_PAGE_FINISH
+
+  ;Uninstall Page order
+  !insertmacro MUI_UNPAGE_CONFIRM
+  !insertmacro MUI_UNPAGE_INSTFILES
+
+  ;License dialog
+  LicenseData License.rtf
+
+  ;Component-selection page
+    ;Descriptions
+    LangString DESC_SecTomcat ${LANG_ENGLISH} "Install the Tomcat Servlet container."
+    LangString DESC_SecTomcatCore ${LANG_ENGLISH} "Install the Tomcat Servlet container core."
+    LangString DESC_SecTomcatService ${LANG_ENGLISH} "Automatically start Tomcat when the computer is started. This requires Windows NT 4.0, Windows 2000 or Windows XP."
+    LangString DESC_SecTomcatSource ${LANG_ENGLISH} "Install the Tomcat source code."
+    LangString DESC_SecMenu ${LANG_ENGLISH} "Create a Start Menu program group for Tomcat."
+    LangString DESC_SecDocs ${LANG_ENGLISH} "Install the Tomcat documentation bundle. This include documentation on the servlet container and its configuration options, on the Jasper JSP page compiler, as well as on the native webserver connectors."
+    LangString DESC_SecExamples ${LANG_ENGLISH} "Installs some examples web applications."
+    LangString DESC_SecAdmin ${LANG_ENGLISH} "Installs the administration web application."
+    LangString DESC_SecWebapps ${LANG_ENGLISH} "Installs other utility web applications (WebDAV, balancer, etc)."
+
+  ;Language
+  !insertmacro MUI_LANGUAGE English
+
+  ;Folder-select dialog
+  InstallDir "$PROGRAMFILES\Apache Software Foundation\Tomcat 5.5"
+
+  ;Install types
+  InstType Normal
+  InstType Minimum
+  InstType Full
+
+  ; Main registry key
+  InstallDirRegKey HKLM "SOFTWARE\Apache Software Foundation\Tomcat\5.5" ""
+
+  !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS
+  ReserveFile "jvm.ini"
+  ReserveFile "config.ini"
+
+;--------------------------------
+;Installer Sections
+
+SubSection "Tomcat" SecTomcat
+
+Section "Core" SecTomcatCore
+
+  SectionIn 1 2 3
+
+  IfSilent +2 0
+  Call checkJvm
+
+  SetOutPath $INSTDIR
+  File tomcat.ico
+  File LICENSE
+  File /r common
+  File /nonfatal /r shared
+  File /nonfatal /r logs
+  File /nonfatal /r work
+  File /nonfatal /r temp
+  SetOutPath $INSTDIR\bin
+  File bin\bootstrap.jar
+  File bin\commons-logging-api.jar
+  File bin\*.exe
+  SetOutPath $INSTDIR\conf
+  File conf\*.*
+  SetOutPath $INSTDIR\server
+  File /r server\lib
+  File /nonfatal /r server\classes
+  SetOutPath $INSTDIR\server\webapps
+  File /r server\webapps\manager
+  SetOutPath $INSTDIR\webapps
+  File /r webapps\ROOT
+  SetOutPath $INSTDIR\conf\Catalina\localhost
+  File conf\Catalina\localhost\manager.xml
+
+  Call configure
+  Call findJavaPath
+  Pop $2
+
+  IfSilent +2 0
+  !insertmacro MUI_INSTALLOPTIONS_READ $2 "jvm.ini" "Field 2" "State"
+
+  StrCpy "$JavaHome" $2
+  Push $2
+  Call findJVMPath
+  Pop $2
+
+  DetailPrint "Using Jvm: $2"
+
+  nsExec::ExecToLog '"$INSTDIR\bin\tomcat5.exe" //IS//Tomcat5 --DisplayName "Apache Tomcat" --Description "Apache Tomcat @VERSION@ Server - http://jakarta.apache.org/tomcat/" --LogPath "$INSTDIR\logs" --Install "$INSTDIR\bin\tomcat5.exe" --Jvm "$2"'
+  ClearErrors
+
+SectionEnd
+
+Section "Service" SecTomcatService
+
+  SectionIn 3
+
+  IfSilent 0 +3
+  Call findJavaPath
+  Pop $2
+
+  IfSilent +2 0
+  !insertmacro MUI_INSTALLOPTIONS_READ $2 "jvm.ini" "Field 2" "State"
+
+  StrCpy "$JavaHome" $2
+  Push $2
+  Call findJVMPath
+  Pop $2
+
+  nsExec::ExecToLog '"$INSTDIR\bin\tomcat5.exe" //US//Tomcat5 --Startup auto'
+
+  ClearErrors
+
+SectionEnd
+
+Section "Source Code" SecTomcatSource
+
+  SectionIn 3
+  SetOutPath $INSTDIR
+  File /r src
+
+SectionEnd
+
+SubSectionEnd
+
+Section "Start Menu Items" SecMenu
+
+  SectionIn 1 2 3
+
+  !insertmacro MUI_INSTALLOPTIONS_READ $2 "jvm.ini" "Field 2" "State"
+
+  SetOutPath "$SMPROGRAMS\Apache Tomcat 5.5"
+
+  CreateShortCut "$SMPROGRAMS\Apache Tomcat 5.5\Tomcat Home Page.lnk" \
+                 "http://jakarta.apache.org/tomcat"
+
+  CreateShortCut "$SMPROGRAMS\Apache Tomcat 5.5\Welcome.lnk" \
+                 "http://127.0.0.1:$R0/"
+
+  CreateShortCut "$SMPROGRAMS\Apache Tomcat 5.5\Tomcat Administration.lnk" \
+                 "http://127.0.0.1:$R0/admin/"
+
+  IfFileExists "$INSTDIR\webapps\webapps\tomcat-docs" 0 NoDocumentaion
+
+  CreateShortCut "$SMPROGRAMS\Apache Tomcat 5.5\Tomcat Documentation.lnk" \
+                 "$INSTDIR\webapps\tomcat-docs\index.html"
+
+NoDocumentaion:
+
+  CreateShortCut "$SMPROGRAMS\Apache Tomcat 5.5\Uninstall Tomcat 5.5.lnk" \
+                 "$INSTDIR\Uninstall.exe"
+
+  CreateShortCut "$SMPROGRAMS\Apache Tomcat 5.5\Tomcat 5.5 Program Directory.lnk" \
+                 "$INSTDIR"
+
+  CreateShortCut "$SMPROGRAMS\Apache Tomcat 5.5\Monitor Tomcat.lnk" \
+                 "$INSTDIR\bin\tomcat5w.exe" \
+                 '//MS//Tomcat5' \
+                 "$INSTDIR\tomcat.ico" 0 SW_SHOWNORMAL
+
+  CreateShortCut "$SMPROGRAMS\Apache Tomcat 5.5\Configure Tomcat.lnk" \
+                 "$INSTDIR\bin\tomcat5w.exe" \
+                 '//ES//Tomcat5' \
+                 "$INSTDIR\tomcat.ico" 0 SW_SHOWNORMAL
+
+SectionEnd
+
+Section "Documentation" SecDocs
+
+  SectionIn 1 3
+  SetOutPath $INSTDIR\webapps
+  File /r webapps\tomcat-docs
+
+SectionEnd
+
+Section "Examples" SecExamples
+
+  SectionIn 3
+
+  SetOverwrite on
+  SetOutPath $INSTDIR\webapps
+  File /r webapps\jsp-examples
+  File /r webapps\servlets-examples
+
+SectionEnd
+
+Section "Administration" SecAdmin
+
+  SectionIn 3
+
+  SetOutPath $INSTDIR\server\webapps
+  File /r server\webapps\admin
+  SetOutPath $INSTDIR\conf\Catalina\localhost
+  File conf\Catalina\localhost\admin.xml
+
+SectionEnd
+
+Section "Webapps" SecWebapps
+
+  SectionIn 3
+
+  SetOutPath $INSTDIR\webapps
+  File /r webapps\balancer
+  File /r webapps\webdav
+
+SectionEnd
+
+Section -post
+  nsExec::ExecToLog '"$INSTDIR\bin\tomcat5.exe" //US//Tomcat5 --Classpath "$INSTDIR\bin\bootstrap.jar" --StartClass org.apache.catalina.startup.Bootstrap --StopClass org.apache.catalina.startup.Bootstrap --StartParams start --StopParams stop  --StartMode jvm --StopMode jvm'
+  nsExec::ExecToLog '"$INSTDIR\bin\tomcat5.exe" //US//Tomcat5 --JvmOptions "-Dcatalina.home=$INSTDIR#-Djava.endorsed.dirs=$INSTDIR\common\endorsed#-Djava.io.tmpdir=$INSTDIR\temp" --StdOutput "$INSTDIR\logs\stdout.log" --StdError "$INSTDIR\logs\stderr.log"'
+
+  WriteUninstaller "$INSTDIR\Uninstall.exe"
+
+  WriteRegStr HKLM "SOFTWARE\Apache Software Foundation\Tomcat\5.5" "InstallPath" $INSTDIR
+  WriteRegStr HKLM "SOFTWARE\Apache Software Foundation\Tomcat\5.5" "Version" @VERSION@
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Apache Tomcat 5.5" \
+                   "DisplayName" "Apache Tomcat 5.5 (remove only)"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Apache Tomcat 5.5" \
+                   "UninstallString" '"$INSTDIR\Uninstall.exe"'
+
+SectionEnd
+
+Function .onInit
+
+  ;Extract Install Options INI Files
+  !insertmacro MUI_INSTALLOPTIONS_EXTRACT "config.ini"
+  !insertmacro MUI_INSTALLOPTIONS_EXTRACT "jvm.ini"
+
+FunctionEnd
+
+Function SetChooseJVM
+  !insertmacro MUI_HEADER_TEXT "$(TEXT_JVM_TITLE)" "$(TEXT_JVM_SUBTITLE)"
+  Call findJavaPath
+  Pop $3
+  !insertmacro MUI_INSTALLOPTIONS_WRITE "jvm.ini" "Field 2" "State" $3
+  !insertmacro MUI_INSTALLOPTIONS_DISPLAY "jvm.ini"
+FunctionEnd
+
+Function SetConfiguration
+  !insertmacro MUI_HEADER_TEXT "$(TEXT_CONF_TITLE)" "$(TEXT_CONF_SUBTITLE)"
+  !insertmacro MUI_INSTALLOPTIONS_DISPLAY "config.ini"
+FunctionEnd
+
+Function Void
+FunctionEnd
+
+;--------------------------------
+;Descriptions
+
+!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecTomcat} $(DESC_SecTomcat)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecTomcatCore} $(DESC_SecTomcatCore)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecTomcatService} $(DESC_SecTomcatService)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecTomcatSource} $(DESC_SecTomcatSource)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecMenu} $(DESC_SecMenu)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecDocs} $(DESC_SecDocs)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecExamples} $(DESC_SecExamples)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecAdmin} $(DESC_SecAdmin)
+  !insertmacro MUI_DESCRIPTION_TEXT ${SecWebapps} $(DESC_SecWebapps)
+!insertmacro MUI_FUNCTION_DESCRIPTION_END
+
+
+; =====================
+; FindJavaPath Function
+; =====================
+;
+; Find the JAVA_HOME used on the system, and put the result on the top of the
+; stack
+; Will return an empty string if the path cannot be determined
+;
+Function findJavaPath
+
+  ClearErrors
+
+  ReadEnvStr $1 JAVA_HOME
+
+  IfErrors 0 FoundJDK
+
+  ClearErrors
+
+  ReadRegStr $2 HKLM "SOFTWARE\JavaSoft\Java Runtime Environment" "CurrentVersion"
+  ReadRegStr $1 HKLM "SOFTWARE\JavaSoft\Java Runtime Environment\$2" "JavaHome"
+  ReadRegStr $3 HKLM "SOFTWARE\JavaSoft\Java Runtime Environment\$2" "RuntimeLib"
+
+  FoundJDK:
+
+  IfErrors 0 NoErrors
+  StrCpy $1 ""
+
+NoErrors:
+
+  ClearErrors
+
+  ; Put the result in the stack
+  Push $1
+
+FunctionEnd
+
+
+; ====================
+; FindJVMPath Function
+; ====================
+;
+; Find the full JVM path, and put the result on top of the stack
+; Argument: JVM base path (result of findJavaPath)
+; Will return an empty string if the path cannot be determined
+;
+Function findJVMPath
+
+  Pop $1
+
+  IfFileExists "$1\jre\bin\hotspot\jvm.dll" 0 TryJDK14
+    StrCpy $2 "$1\jre\bin\hotspot\jvm.dll"
+    Goto EndIfFileExists
+  TryJDK14:
+  IfFileExists "$1\jre\bin\server\jvm.dll" 0 TryClassic
+    StrCpy $2 "$1\jre\bin\server\jvm.dll"
+    Goto EndIfFileExists
+  TryClassic:
+  IfFileExists "$1\jre\bin\classic\jvm.dll" 0 JDKNotFound
+    StrCpy $2 "$1\jre\bin\classic\jvm.dll"
+    Goto EndIfFileExists
+  JDKNotFound:
+    SetErrors
+  EndIfFileExists:
+
+  IfErrors 0 FoundJVMPath
+
+  ClearErrors
+
+  ReadRegStr $1 HKLM "SOFTWARE\JavaSoft\Java Runtime Environment" "CurrentVersion"
+  ReadRegStr $2 HKLM "SOFTWARE\JavaSoft\Java Runtime Environment\$1" "RuntimeLib"
+  
+  FoundJVMPath:
+
+  IfErrors 0 NoErrors
+  StrCpy $2 ""
+
+NoErrors:
+
+  ClearErrors
+
+  ; Put the result in the stack
+  Push $2
+
+FunctionEnd
+
+
+; ====================
+; CheckJvm Function
+; ====================
+;
+Function checkJvm
+
+  !insertmacro MUI_INSTALLOPTIONS_READ $3 "jvm.ini" "Field 2" "State"
+  IfFileExists "$3\bin\java.exe" NoErrors1
+  MessageBox MB_OK "No Java Virtual Machine found."
+  Quit
+NoErrors1:
+  StrCpy "$JavaHome" $3
+  Push $3
+  Call findJVMPath
+  Pop $4
+  StrCmp $4 "" 0 NoErrors2
+  MessageBox MB_OK "No Java Virtual Machine found."
+  Quit
+NoErrors2:
+
+FunctionEnd
+
+; ==================
+; Configure Function
+; ==================
+;
+; Display the configuration dialog boxes, read the values entered by the user,
+; and build the configuration files
+;
+Function configure
+
+  !insertmacro MUI_INSTALLOPTIONS_READ $R0 "config.ini" "Field 2" "State"
+  !insertmacro MUI_INSTALLOPTIONS_READ $R1 "config.ini" "Field 5" "State"
+  !insertmacro MUI_INSTALLOPTIONS_READ $R2 "config.ini" "Field 7" "State"
+
+  IfSilent 0 +2
+  StrCpy $R4 'port="8080"'
+
+  IfSilent +2 0
+  StrCpy $R4 'port="$R0"'
+
+  IfSilent 0 +2
+  StrCpy $R5 ''
+
+  IfSilent Silent 0
+
+  ; Escape XML
+  Push $R1
+  Call xmlEscape
+  Pop $R1
+  Push $R2
+  Call xmlEscape
+  Pop $R2
+  
+  StrCpy $R5 '<user name="$R1" password="$R2" roles="admin,manager" />'
+
+Silent:
+  DetailPrint 'HTTP/1.1 Connector configured on port "$R0"'
+  DetailPrint 'Admin user added: "$R1"'
+
+  SetOutPath $TEMP
+  File /r confinstall
+
+  ; Build final server.xml
+  Delete "$INSTDIR\conf\server.xml"
+  FileOpen $R9 "$INSTDIR\conf\server.xml" w
+
+  Push "$TEMP\confinstall\server_1.xml"
+  Call copyFile
+  FileWrite $R9 $R4
+  Push "$TEMP\confinstall\server_2.xml"
+  Call copyFile
+
+  FileClose $R9
+
+  DetailPrint "server.xml written"
+
+  ; Build final tomcat-users.xml
+  
+  Delete "$INSTDIR\conf\tomcat-users.xml"
+  FileOpen $R9 "$INSTDIR\conf\tomcat-users.xml" w
+
+  Push "$TEMP\confinstall\tomcat-users_1.xml"
+  Call copyFile
+  FileWrite $R9 $R5
+  Push "$TEMP\confinstall\tomcat-users_2.xml"
+  Call copyFile
+
+  FileClose $R9
+
+  DetailPrint "tomcat-users.xml written"
+
+  RMDir /r "$TEMP\confinstall"
+
+FunctionEnd
+
+
+Function xmlEscape
+  Pop $0
+  ${StrRep} $0 $0 "&" "&amp;"
+  ${StrRep} $0 $0 "$\"" "&quot;"
+  ${StrRep} $0 $0 "<" "&lt;"
+  ${StrRep} $0 $0 ">" "&gt;"
+  Push $0
+FunctionEnd
+
+
+; =================
+; CopyFile Function
+; =================
+;
+; Copy specified file contents to $R9
+;
+Function copyFile
+
+  ClearErrors
+
+  Pop $0
+
+  FileOpen $1 $0 r
+
+ NoError:
+
+  FileRead $1 $2
+  IfErrors EOF 0
+  FileWrite $R9 $2
+
+  IfErrors 0 NoError
+
+ EOF:
+
+  FileClose $1
+
+  ClearErrors
+
+FunctionEnd
+
+
+;--------------------------------
+;Uninstaller Section
+
+Section Uninstall
+
+  Delete "$INSTDIR\modern.exe"
+  Delete "$INSTDIR\Uninstall.exe"
+
+  ; Delete Tomcat service
+  nsExec::ExecToLog '"$INSTDIR\bin\tomcat5.exe" //DS//Tomcat5'
+  ClearErrors
+
+  DeleteRegKey HKCR "JSPFile"
+  DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Apache Tomcat 5.5"
+  DeleteRegKey HKLM "SOFTWARE\Apache Software Foundation\Tomcat\5.5"
+  RMDir /r "$SMPROGRAMS\Apache Tomcat 5.5"
+  Delete "$INSTDIR\tomcat.ico"
+  Delete "$INSTDIR\LICENSE"
+  RMDir /r "$INSTDIR\bin"
+  RMDir /r "$INSTDIR\common"
+  Delete "$INSTDIR\conf\*.dtd"
+  RMDir /r "$INSTDIR\shared"
+  RMDir "$INSTDIR\logs"
+  RMDir /r "$INSTDIR\server"
+  RMDir /r "$INSTDIR\webapps\balancer"
+  RMDir /r "$INSTDIR\webapps\ROOT"
+  RMDir /r "$INSTDIR\webapps\tomcat-docs"
+  RMDir /r "$INSTDIR\webapps\servlets-examples"
+  RMDir /r "$INSTDIR\webapps\jsp-examples"
+  RMDir /r "$INSTDIR\webapps\webdav"
+  RMDir "$INSTDIR\webapps"
+  RMDir /r "$INSTDIR\work"
+  RMDir /r "$INSTDIR\temp"
+  RMDir /r "$INSTDIR\src"
+  RMDir "$INSTDIR"
+
+  ; if $INSTDIR was removed, skip these next ones
+  IfFileExists "$INSTDIR" 0 Removed 
+    MessageBox MB_YESNO|MB_ICONQUESTION \
+      "Remove all files in your Tomcat 5.5 directory? (If you have anything\
+ you created that you want to keep, click No)" IDNO Removed
+    Delete "$INSTDIR\*.*" ; this would be skipped if the user hits no
+    RMDir /r "$INSTDIR"
+    Sleep 500
+    IfFileExists "$INSTDIR" 0 Removed 
+      MessageBox MB_OK|MB_ICONEXCLAMATION \
+                 "Note: $INSTDIR could not be removed."
+  Removed:
+
+SectionEnd
+
+;eof
diff --git a/connectors/KEYS b/connectors/KEYS
new file mode 100644
index 0000000..e330357
--- /dev/null
+++ b/connectors/KEYS
@@ -0,0 +1,293 @@
+This file contains the PGP&GPG keys of various Apache developers.
+Please don't use them for email unless you have to. Their main
+purpose is code signing.
+
+Apache users: pgp < KEYS
+Apache developers: 
+        (pgpk -ll <your name> && pgpk -xa <your name>) >> this file.
+      or
+        (gpg --fingerprint --list-sigs <your name>
+             && gpg --armor --export <your name>) >> this file.
+
+Apache developers: please ensure that your key is also available via the
+PGP keyservers (such as pgpkeys.mit.edu).
+
+
+Type Bits/KeyID    Date       User ID
+pub  2048/F22C4FED 2001/07/02 Andy Armstrong <andy@tagish.com>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: PGPfreeware 7.0.3 for non-commercial use <http://www.pgp.com>
+
+mQGiBDtAWuURBADZ0KUEyUkSUiTA09e7tvEbX25STsjxrR+DNTainCls+XlkVOij
+gBv216lqge9tIsS0L6hCP4OQbFf/64qVtJssX4QXdyiZGb5wpmcj0Mz602Ew8r+N
+I0S5NvmogoYWW7BlP4r61jNxO5zrr03KaijM5r4ipJdLUxyOmM6P2jRPUwCg/5gm
+bpqiYl7pXX5FgDeB36tmD+UD/06iLqOnoiKO0vMbOk7URclhCObMNrHqxTxozMTS
+B9soYURbIeArei+plYo2n+1qB12ayybjhVu3uksXRdT9bEkyxMfslvLbIpDAG8Cz
+gNftTbKx/MVS7cQU0II8BKo2Akr+1FZah+sD4ovK8SfkMXUQUbTeefTntsAQKyyU
+9M9tA/9on9tBiHFl0qVJht6N4GiJ2G689v7rS2giLgKjetjiCduxBXEgvUSuyQID
+nF9ATrpXjITwsRlGKFmpZiFm5oCeCXihIVH0u6q066xNW2AXkLVoJ1l1Rs2Z0lsb
+0cq3xEAcwAmYLKQvCtgDV8CYgWKVmPi+49rSuQn7Lo9l02OUbLQgQW5keSBBcm1z
+dHJvbmcgPGFuZHlAdGFnaXNoLmNvbT6JAFgEEBECABgFAjtAWuUICwMJCAcCAQoC
+GQEFGwMAAAAACgkQajrT9PIsT+1plgCfXAovWnVL3MjrTfcGlFSKw7GHCSYAoJkz
+x+r2ANe8/0e+u5ZcYtSaSry+uQINBDtAWuUQCAD2Qle3CH8IF3KiutapQvMF6PlT
+ETlPtvFuuUs4INoBp1ajFOmPQFXz0AfGy0OplK33TGSGSfgMg71l6RfUodNQ+PVZ
+X9x2Uk89PY3bzpnhV5JZzf24rnRPxfx2vIPFRzBhznzJZv8V+bv9kV7HAarTW56N
+oKVyOtQa8L9GAFgr5fSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kj
+wEPwpVsYjY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obE
+AxnIByl6ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpMgs7AAIC
+B/0eHkYQ0Rv6s21TgpOzRBon+rQAv9ka0PlC7bj2eYWsCOBib8K7qO8hND0sW59p
+0uFQ01X7kC7L/4Ls1HTk0chEZMV0UrGAOKXHY1QFlxrNtFi5U3pTPITXDDfy+g/G
+6FTX3PLnGGvwXbtaiAq5UjQ6iXm03lh0BW6Q+kPtm8swPPfqfjYv0rrT+I8Ic3p2
+HplWKR2bpi3wqCSKB/AaTQJwTbh2x2+2cPVONPodgjZSJ9eQkErejkNSvqbumlTx
+dB81eoGa0Lo2xE7N+DNlCnILGE0X4hPMdj+N5fmyEbyx0WOB8crvCuODGGEQnXs/
+zbVO7FP+rj7YWjRh5pVD3bGiiQBMBBgRAgAMBQI7QFrlBRsMAAAAAAoJEGo60/Ty
+LE/tj/QAoOFNFa7rbAy+eT6mRNb7XztfcAbWAKD6Gd6S/7lEJU0k2TS5tozt4jMl
+vw==
+=/91Q
+-----END PGP PUBLIC KEY BLOCK-----
+
+Type Bits/KeyID    Date       User ID
+pub  1024D/86867BA6 2001-11-22 Jean-Frederic Clere (jfclere) <JFrederic.Clere@fjitsu-siemens.com>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.0.5 (GNU/Linux)
+Comment: For info see http://www.gnupg.org
+
+mQGiBDv9Gx8RBADclmKwDLcibNVipQnhYW+bFIpuQjQnRrqRwn3gXM+/luzzJYJ4
+bbWpw13zjX0EkrAJ8qH2A/d0EIU1eZ0zHrLgRvMUfLGFUX7FFFw18JKFLTVGhG4/
+8sSl3ydHSA2Kd1PF6xjBP7iM7sg5dJfEkyMzvK5H4F0ZpTqy3087wsg1wwCgitRy
+Zg4x3lWZSkOwBj472qaO9GkD/2q6kyWfAK6XFe3GuB5AAs3poMfN1eqW+duM4TA8
+zUiWK0Wxx4JXJbL7n0i4d+JdXJsrjSjF++KKfelcxsrSxoUIBegez25MUSvHe09D
+R3nqkY8CVO+viEtzRBqkSgCMbUjAtfkQ+vp2jDnWSmmkNfY0OYAzt+KRyJKcjUSJ
+gvOOA/45+DN9wuTELoFTvsXh1JgOL/QvW1fmQ2HrcQk94BkzIsfVGWClCiig5gNw
+LCxTbfgA5htpI8U7vPR9/5gH7U8Wy3HR6xQUZxcbttMeYit2VbDEJzF5r5S0pJvD
+vyk3n1kiKU7r49sjhxGgE8J/VvDpO6YcIsDs8LoULwuJTg0DTrRDSmVhbi1GcmVk
+ZXJpYyBDbGVyZSAoamZjbGVyZSkgPEpGcmVkZXJpYy5DbGVyZUBmdWppdHN1LXNp
+ZW1lbnMuY29tPohXBBMRAgAXBQI7/RsfBQsHCgMEAxUDAgMWAgECF4AACgkQ0+/m
+toaGe6amGQCeJU5VZ8QCi8+PY0QJHPA63e5uPyoAmgOWIwFm8A/xmW8qjEvVAWtb
+TjZxuQENBDv9GyMQBACCbFlSF+udW/Qz2oknDen8Hoql4Q1Q7CUQTbPjoQAcYgZg
+LrsR6hc9aCIf3Kt4qZBgQ1Oe9M/AemOFhU04UNp3dgHk91EYRvx80Rua992p/8V7
+QOhwIBVb2XE8as5nL2j8w6Jz7eSs/bivxm9yD0AH/I5H01RAJivRbOTsUgSkDwAD
+BQP5ARlW2Nqc0U17asQsmMYvT1UMiOiyBwUD/DIEG2Xy1hlEvdljg8WU26jcjpGq
+MrT69T4Z+eZ2oVyiRQTW4qMUBKc0Nbz89hL0qv9K41ExxxH+JgE1csRVvmwAT8Iy
+lnhof7TJLRBtvan3+p21Kxl1uQ7MbmLT875u+vc+J098fIiIRgQYEQIABgUCO/0b
+IwAKCRDT7+a2hoZ7pn9UAJ9f0TK0QQOtjQBvxAissopYhDKHGACePZg0k9sj69yw
+nVWrBS9fvFC9jcA=
+=BTiM
+-----END PGP PUBLIC KEY BLOCK-----
+
+Type Bits/KeyID    Date       User ID
+pub  1024D/E86E29AC 2002-02-13 kevin seguin <seguin@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.0.6 (MingW32)
+Comment: For info see http://www.gnupg.org
+
+mQGiBDxqtR8RBACbySxGrtf+flbowryS1Hj4z3zzEXD4CAEq6RjSGMtIraCDRJfp
+6Gexs+lQ6IhpdC4GfX70SUMjXXvT5suhXYeGOM4iJHqUsksgzEKjUqcfj1l3qmOs
+/doE8lcGGHcYbMplBcfuop+shZYiv9GEJ3gutwn/dNnhs/QA9bCdIj03lwCgvAcy
+QpT5JdTym2p2icd5e91mGIUEAJMw6JHTTcCiyoTRy7k8Cf65d8S7bTDLr6pqJVE2
+XU41CvW/pgL31akYAxpeZJJnsBaLaUiqh6K0qgfEMlDwDeC6gVogHBxWkEXdK1dr
+tGL4GIUcxQ1+ZvQhGg7dtjanmfMlylVgS+C48awJySkinRmaQDbQ0MKdFchLc/y1
+OR3IA/0VkIvlidehMPbZCalqhS9AEsDiFq5/u5AsQzDEp2nmTGlmBqjhc39kEnu4
+qKq08az1Gt6Q7sxXbjH/jYtDgd49FW5Yg4k5B3hpTgnbyRE6SGlKksu8qTmYkDve
+4rej6pvJRHwp6hDKxDG8qQoLWIgOfVC8960nurqx56QdV9YMsLQga2V2aW4gc2Vn
+dWluIDxzZWd1aW5AYXBhY2hlLm9yZz6IVwQTEQIAFwUCPGq1HwULBwoDBAMVAwID
+FgIBAheAAAoJEKy3f8Lobims3E0An0x3rrUMIijUMFoqnoT7muNGwmAzAJ990TWj
+dZO4ayh1M+cWhjaw9W+44bkBDQQ8arUkEAQApaMm5HUB1Yk2x5MavAs/O4zfWnOx
+YFOeXIPfGvhlhF2/Lrjs9icaa/tOM/CTCes19nDWP5Fc+pQxmgSPrgt3fsShwZJe
+p3iYodLbM76uXEgSvI4Wh6kwViHbN4V1GxJAd2ZPVb1v+lauGUCOgPFGw99UV9sO
+tTRXSbFS6AgqQzMAAwUD/jq6boxlnab/GUmKrILeLkv1X0G2/AEXEGRmG0nkhVdj
+OShoqtPr4y/UhMzJUOequs2CdvRlTIyAyZqN7A0Qp4mFfmsvp0dYYssTtE4bCzZe
+WxSKgjtBWBHXnH+Qzjb5R2Tz28kAxNY+dt7yxC+CkXWDZq/rsPgsXNbWXT49FnF8
+iEYEGBECAAYFAjxqtSQACgkQrLd/wuhuKazl7QCfQkz5t/3T6EtXZCcXz/hlswyI
+z30AoLr/7hwXgedEepBk/Gm9HUsbMnM8
+=S1mb
+-----END PGP PUBLIC KEY BLOCK-----
+
+
+Type bits      keyID      Date       User ID
+pub  1024D/307A10A5 2002-07-18 Henri Gomez *** RPM SIGNING KEY ***
+                                           <hgomez@users.sourceforge.net>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.0.6 (GNU/Linux)
+Comment: For info see http://www.gnupg.org
+
+mQGiBD02vbERBAC1v8fR6gjERpaz4UMfdy0hRVWCPSbOdF+Swm/IenjVzErco6zb
+MTa13umUNrDPBy/tTWiCCZrOnqi7fgDzWqPEqrXJjKAFVLEWE6MmKylPPEPG1/bm
+idkNGERSAZduvhKv777PzvEJJ/8eGe3wy/O8NbgIjCPtr4UklwCZS8cFuwCg8oMO
+UdT8qZRtzdxdAyu1m5fUb+MD/3IKJYWXsdtb6iBphCU4f/BoyjVC9EZJ1ywLuiVM
+siKbuaDUaXU9nWcbNKv+fx8uZ1NaadpfLokqqhnWcpnSiqw8HNR7SwsF1D33rkXK
+O4FSuVss/tIoqGdWFcJyPkP4yP5shxqR335narVw2vDa0+BiWkALbA2qVsSIdZDB
+LeFZA/47AMBS0U2BRk2rQT8LmMuFl7mR+wNBM4n7FUGdxsGn3TcYd4pXTNrEQPrV
+YNdooKlikgGk4hgFnIFX09Spmimqgq0goFue81rttVdZZ4uep8dTghY6gwmvcOxX
+jATbhWStBhdu9B35kzfHc+1QihD5Z94u4uyWIVBIzikcdiY8LbQqSGVucmkgR29t
+ZXogPGhnb21lekB1c2Vycy5zb3VyY2Vmb3JnZS5uZXQ+iFcEExECABcFAj02vbEF
+CwcKAwQDFQMCAxYCAQIXgAAKCRAZMdaEMHoQpYijAKCCP68ndU/kTXR9XAKLvibC
+3S8+1QCfUFQYte3Jo+MHKaWjsu9JGptRzo+5Ag0EPTa93RAIAKlsRJ5gOGTFsmaR
+W9k6MIh4c/MCy7J7HUxT5xTdHROa+3zUh+FAE/JaOx9ZtZtH863DFHA8cP4L+tpi
+PjBT6g2E94dwGcuH/OiSSCT4JSBukbGbOuLLdmFXqUl8+4gsL90Xal67FtNLwyLG
+1n7geLir0byD+OT7VLA5w+6G0NOpJEveV/FIa2qLgdRZ8vz73ybgMh18hBUrUmro
+jncp0rln2VU7VCH1C2aClKm7kK4mGAjIFIzKbguK+kM3b8NDHmXKpT6syyCtIM3h
+prkV1TUCAFqLI32aSdlTN79lpeA2zDga9k4/4X/RDHsFpRN2neRFGTNUtuUgYpQQ
+E5zWBmMAAwUH/RiGxyeBsad923IwE1+GAjxFl2tqF9xWk0J6yTnSK4nfhYAE9evV
+jwDEok9jRl4ILCcXx6YN/d/lWNuSbARKHz/3hLiTouPpwd3SSJ8is2x9PgpJz5JX
+cD0y1SkbPLvs3jH3ZmdcxZpuAmJeI/typqFKK5pWP44oXIH+XH/8nWDtmLEBkgKQ
+/ATQWenMTmZ6MIJ6aWKWGkO9QS6iYRz3PPPGQ1O8W02CeprM2wBtlb8J1Z3RxNhM
+rZcg/1Qi3V3D1HI4zw6tAFmDeBb8J4PaBQzqlhzx2EBTbfwNPhV8AlPvpxHEeGGn
+v+O1yhZr33SnyZdINNoNDn+owVMdmkobe9GIRgQYEQIABgUCPTa93QAKCRAZMdaE
+MHoQpRsTAJ4qst3MhLm48fBAEnzuzi/BIKr+AgCfYaCB/AvPoncQbHc8BcNGRimR
+P9A=
+=hQhz
+-----END PGP PUBLIC KEY BLOCK-----
+
+Type bits      keyID      Date       User ID
+DSS  1024      0x7572CDEF 2001/10/12 *** DEFAULT SIGNING KEY ***
+                                     Remy Maucherat <remm@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: PGP 6.5.1i
+
+mQGiBDvGnR4RBADPVhGl+eo6ie6UJ9E4sIW8xbBCYHQxIMwWalcLzbz2FKWl57VP
+JzoIaTZZP6op3yhRv4qRm7NGC/gpabRj/im8vK7eKgoh2VVKhqVdIjWbQW+u9pEb
+pXlp1AAFUbP4kqSIggV54s4lRulE6Uq0eDy6sXK8mBFLGRggwEFvLTHPYQCg/2qx
+u1/hBovB0I8+XJoSzconttMEAJVYrxD2W9FhnVCn3ffHhkjQBxUeXr8bQIRwid6R
+ukAqlcVkGohOngdJ94O7KL40mm2A0t9APFVC0tgEk0M7piB5cE1zAZjoLTrlvWrC
+0tH0LzEyaUgvHdG1fVeHtgwsiGW/raWDzCfiIvLCx80yiaMw2TtYXLmhjy+1qWv3
+v78JA/4idwIP7zEF8O6hSEcn5RopddUte5Ne+Ya+fsuSmKDCqPNnIkVvHYarFbkY
+QPDEiUA2fS9j1ShDz3t0Adt1TRbN2VbENH6rYbx9gwiQGu41jjRVit2Z9JM5YDyq
+VDyYH5PpyKOJkKPzuIFzq6Wn6dh/wczhDNn7YHXH49ES2nO+/LQgUmVteSBNYXVj
+aGVyYXQgPHJlbW1AYXBhY2hlLm9yZz6JAE4EEBECAA4FAjvGnR4ECwMBAgIZAQAK
+CRBuHJULdXLN72cHAKCbdPNI0ep6mGivq/xXoeQOPbBEBwCgpBbCFwc4Q6W+Y+VT
+KQh+f5jVojw=
+=1iBa
+-----END PGP PUBLIC KEY BLOCK-----
+
+Type Bits/KeyID     Date       User ID
+pub  1024D/564C17A3 2003-01-11 Mladen Turk <mturk@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.0.6 (MingW32)
+
+mQGiBD4fwXIRBAC7WRS8PYxi2YH0T1mX4HCYsF8aHoqxBzMnyFR4J896m1s96vGM
+BTSAwH2NKbiVqtfLokTbQkUVxtrgrF2HMB5NfYBg/JzT7pZL/Q2ThWUS7SJQQA4f
+a7/DpiLiHalp6iX45om6JTdIWEyXv26csIVhmtlkGBEPRhNRX8X4//BM0wCg7wcA
+yQ7c5NmoOJLVs+uHsRrnHo0D/R/dMyuWt7/o0eGIEuRlDl2q+YL8xLuVyJMXQBnd
+jo7jKpQ+Q1zl93aVTzsJa7mP2zZ7jqaJ855sdz6rvwyhGF1/qYMtm6zrmgBy2XPm
+J+57sfwSZr0bhIeMpCWjIw98z9sObq0v2r2oA3+J9E3Na/BZsCVTZVb3ew7ILmEp
+F5D7A/4zvjY41dakCAJsD1Xo8TS6hSqJf4zq9vX3ayJVvUjeo8n4sHNOwcbEnnui
+9zZaUH3F0x+3cDo7mS1Y4pD8THuqCZoSbSkiHnlved6nLXsKbqvVrVo+esEhfZCn
+Iji3gp+2TVNwdHXGM+4BAzMJCLsdXjByO6SNzB9a+H8RsRlZKrQ8TWxhZGVuIFR1
+cmsgKCoqKiBERUZBVUxUIFNJR05JTkcgS0VZICoqKikgPG10dXJrQGFwYWNoZS5v
+cmc+iF0EExECAB0FAj4fwXIFCRLP94AFCwcKAwQDFQMCAxYCAQIXgAAKCRAcUGQH
+VkwXo0jxAKCgHzXPIB4IAgoD7GMAohPQfX7j2QCeL6pAsf4pPufmPvbrrpDp6rQH
+GOS5Ag0EPh/BhhAIAKWzq7+/+nNYGpc7sXGkDNo9xncxcx/KbbJVT0rBteuaonQ4
+vYar1ITjIhOPmF9yPmpUddNrqgQTSO+Or+ZrVOndn+qC1gdY3qpKIN3KTjXloW38
+0Y84ezwdRLznQNkhgXwNcB55l/Z9kLaW2MS8CJzOuYSQT1CYbXg7XP3684ZmV1KC
+cGgcUt9VkIGqwsa2RFDNGvMbySedSkJ/70Q+PJlkXN+W86f8hi3HTjw2MCkNa5NL
++Byg8FEAm95YWrO6kCY3qaJYV7NRt9oVd+2V/NNzwYp3Or/QoYofvfNerupfwBmU
+GEXPyZCqqNH6nDv6chscsWvEA9KzhsWnsdKhmHsAAwUH/R6LwfWgtpaO42dQI4ZS
+VRBmCeWrXCuyVk0d13Yz0xLi5Z5m4g3MON3d+cRVUiyNX+hbDGpi2mkbsnL559Ef
+iqmzDmSz5GQHDutolhOPtLxLrC537ODn2q7hnYQwIQYYIUtYD5sYlzfGYC8olGCB
+IcKIdlGRWcxxiFCIJm5CX/jnSBsyDRpanlSrdkxhzAGsifqj4NQ19ayoeNoZg2ZP
+9SLIY7vbmOxJeHEYkx8AG25xOY1PLotb/0buSXPB8e71zb/DCV1rAhhUxAr/2JOQ
+RqlZBq6PfcHKLRitXRCeVvfldRxuWBIzhuTLUfRPYR6phjP50EzZPlbJzIvGwsOI
+RheITAQYEQIADAUCPh/BhgUJEs/3gAAKCRAcUGQHVkwXoy0JAJ9WTfqfYzW/F6qi
+5MxmqDnU9/G+6ACfQVmhZNnGTSfcwQCttwCaW3CRhDY=
+=MWUr
+-----END PGP PUBLIC KEY BLOCK-----
+
+Type bits      keyID      Date       User ID
+DSS  1024      0xFEA4C043 2001-06-24 Ignacio J. Ortega <nacho@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: PGPfreeware 7.0.3 for non-commercial use <http://www.pgp.com>
+
+mQGiBDs1PSgRBAD8g1RLIdDEk1+XW0DeaOrtzDPRUblHwty9S7a2UfiOEJA8GMZv
+GAT7HcEynxt6P4a58k3DceWvRw4kcu5PRF++qufzroWkSHK3V/gaDxva9XavfbS0
+vgQC5In4ZG+iKw1CbGeXzMnA28YIVbQztpKWFhyrD5q86DjrcBTefM2e0wCg/+iA
+J0DSHbKfiu9mqe8HzVcuXlsEALSSTdUJPRa80bmr6prsuc2heJLa22ddGHkJ5OXD
+pvY9mmY+fDe9wL5bnsbnDvmhwoDAsog/+jjWjJhleF+TR+wzEkmO5fYLv8qZPXet
+r6loSaY84XwrcN1ZMvGKfhfZMeAGg/McUeivT2q+3NH2dD5IK5tM960lPvq2kk6f
+41miA/4kxh/en3CG/qyfb0E33L6XY5xY+IMTnGX7tKFZuGcXtni04mqnmJAdGcoM
+Y8IwLQ1jyy3rJnXYKcJ59ZaIiku5fsOYOMu2MKRr7H6sBhC9wbmk1XfAHNJAaWTK
+GItHjCIIjneFZwZttC44PRspMI5bzb2sskSlH1gO7bt7nbDKMLQkSWduYWNpbyBK
+LiBPcnRlZ2EgPG5hY2hvQGFwYWNoZS5vcmc+iQBYBBARAgAYBQI7NT0oCAsDCQgH
+AgEKAhkBBRsDAAAAAAoJEAO7fiv+pMBDCqQAn08IyL3DNeIF6mo4DlE4BVk7rEN4
+AJ43Di7ytG4nj9y5qioBRhfBjkMZRIkARQQQEQIABgUCO4kqjAAKCRDUJrgPpMH8
+ePKfAKCKVXWM6aGJ1HlOPWdOFgq8r9TYEQCYnJBWcDiwjwzigr6NpL/PEvx4tYkB
+HAQQAQIABgUCPZ91XAAKCRCS/pCXKtS8kZrgCACTfC5BUNIyUVYrpWiJ/6Zqtemo
+0Oogx7N8Q2KaAKTLoopCXJGUiXAdwB1gBGVz/y1NHB4i0G1pZ4H+z4uYYeyqAXWD
+Yyi+8OqD+nW3FJe2xvAoAQJ/21wKhYK6MLCbK9KH3CFYHWBA5//N/rIHjees0U0i
+oCD3mrbWm1624Q3BhAMRTIMeut7d0PPoO5jGzvxoIiYshxLofV+Cgq5ZWwgjzXqZ
+aIggGRlA5dbJXQOT4Wl+zt68XKIiZfCYRhIUaGMO4anAA2sbud5y9Sf+l6iqGnBN
+CdKSIOky/Qp5NTwLrvZMwMlHxqMs4CbnHjjwfsV8Fim4Hdxq3HYnAv82FN+FuQEN
+BDs1PS0QBADYwkwjh6R0cTtgfQIS3U0bA+brKYjRCSDgvhsVe1rOBkAofSIlLr6S
+lDRbbCq339AoQ7Fv45C8QIKG77CAld/CDY/hmip3Be99Hy9mIxonTz5orWnbzSrG
+6GRvzZahOymcY4jSW/BemLWsxMNP6PsAe+6AV3C/1cWhOeiYOmb+2QACAgQAvWAu
+K9G82ViUVu5cSi5wPn/jyApB1Sw5Iv1YVOuhQLC6+stQl70QdxvRqIM1ENf8Iy3C
+bOcu0UxS15RAFaOIOKPf1ac8MhK9a+O1XGDXC2GU2wWp38zZKm8TVvjIjkjSM8BD
++3gtgPcM5D3jGpS8D/mhZv+Dngo1RANhsR/ZFhKJAEwEGBECAAwFAjs1PS0FGwwA
+AAAACgkQA7t+K/6kwEMaEQCdHPgrLNh7G5p0i9W47rAfFoqqcGMAnRmayWLtqL3n
+6TsHA6lbHce/xhZX
+=IWRP
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub  1024D/142B509B 2001-06-15 Glenn L. Nielsen
+     (Sign jakarta.apache.org distributions) <glenn@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.0.6 (SunOS)
+Comment: For info see http://www.gnupg.org
+
+mQGiBDspb/8RBACIbKPrFGOmb//UfnUz0rGa1rRe8J8feycmbBmKFWZmiPhvnVuV
+3GCWGcxDD0pDkM1GX+CXUnzra6gou+JEPvDW2U1Te+Us4fCROying5GM9WnLzFdf
+a7KcbPUH8mwGXkJkbbyhbskIM5yHLKQSi+Pz3cHRVwjs1ZhkcmyI8dyEpwCgk6C+
+DpeiekUI0RCuDBGFvEpCoekD/jBOrIoeSxW5bqQ0cb4xSwo/il8wQNB3NcqoakOS
+ED1n841mxRFnYWtaSovpsS0uuALg4ryAywfJLtqie9Ks1CF6vHkZT0/TGaAaRQzs
+RTc0qeLYYmbKpzf91JnMtKlD1vLEBtb0Ljd6Kn1cZToKMSCilfsr3EdXzMd/9vk7
+mEAPA/9gP23rUTo/1cGURQAEn72ohM/KKgvQL1SnAIeqDu1QuiRRLmJw3kx67DQ1
+KbftH2RRo2AQUYJgZDb3BG+5R3joNNSfhjW+NXjds82252tKKq1W+5z0084xU+GJ
+7O68TZE3elEwp5FfynIc0eyAwqHwlDC1vKThJeOQiDiwc4cmMbRLR2xlbm4gTC4g
+TmllbHNlbiAoU2lnbiBqYWthcnRhLmFwYWNoZS5vcmcgZGlzdHJpYnV0aW9ucykg
+PGdsZW5uQGFwYWNoZS5vcmc+iFcEExECABcFAjspb/8FCwcKAwQDFQMCAxYCAQIX
+gAAKCRD6PI0mFCtQm1T3AJ47Psui/BfovFKySI3m+vJnLa8EewCcCZqn04plmpiu
+lmtXWDav9mO1Cfc=
+=Gfxf
+-----END PGP PUBLIC KEY BLOCK-----
+
+Type Bits/KeyID     Date       User ID
+pub  1024D/364D8C7E 2004-03-05 Kurt Miller <truk@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.2.2 (OpenBSD)
+
+mQGiBEBIwvcRBACNMkLDqN6defWFQeMEEJxbXhzna7pf5DmcJ5EI2BPc9NRXS9mT
+mA9Q1Uy2BeeG2YHrXTPL4H1mHvJQcwg1QFKRFbKgEmeVY9wMvAkne9YrZdqofTD5
+5Z8DbH0uHRcN3DhTuC8rQKJOhHyJuntE2iZAPD6ASYkiPvygEFgB31gTSwCg0OqZ
+azyrfopwc2oiqTWaoATGBMkD/1orgI1zD95f5Qu3lSguVcr9pf04BCt+d3Wq/Wc3
+DJ9BeyA2YdeH0LHE7jmwp2j4kzJyROubGGrOaynvZjf+UP1xSetnR0xgj+FQaK5h
+xB0rvpv2QJE/aMbQhV+P8Naa9K8yH0YfFTR6X4pSgGBgh+aKCtejrvhySibA5JWQ
+peB+A/9CBBNr3V2hN4k3irelWE7jPjHDM2O7s9Mkfr1wsTHxfBNREedgz42pefOz
+EDM4C0bMsbdiB9eEKG6y5ozKrUcAWNz/uOrRfWeZ3EmXqs/LBZeKPb6D3XV54qet
+nR4P2mAM5XnxXywFda7jm0uBtBNXFJKF93ntvZ9jwflaDUDehbQdS3VydCBNaWxs
+ZXIgPHRydWtAYXBhY2hlLm9yZz6IYQQTEQIAIQUCQEjC9wUJA8JnAAYLCQgHAwID
+FQIDAxYCAQIeAQIXgAAKCRC5eaEVNk2Mfl6lAJ9ilKDorZG8xCndE9Vl+TFg+vuV
++wCfb1VXPPJeGJFQ5JXcP7neb71ZWzm5Ag0EQEjDChAIAInQdEjA33llK0yok0M2
+ONst7PwXTd50lzY1zvNacjtRw1hOEGWYMuO4TfDCd50foWVSF+XaM36ar8kS571c
+Ykye/VrVeXYRZRuBolT4bffz8h0QGTmV25uVp8BOqxjHAQmATTpvpct6RiQPwaxv
+cZe6hU7ebYualvmf1N5hHVw8kKM/+BrPl9Q0WYVh/4Qb7Mv5PfNZZbRAHmrmNuzZ
+ATNe5D1o6BvIhl3i6DCYRCvKHbyZWBuDcGXZ8IldqouAFbE/iOVfj1GfvDxWQKla
+qDQfyfMJ8SMW7oTNDTilnZQpFrRGR9ejxnhYeXDYd9gZdleBjtCcEws1pXBJaDuW
+MNsAAwUH/12Y/FoXLT84OK2GXCS7z/FSS8XjTwi8QTB+g0qnQ9HuZ7KAOugCPmu9
+8FVtzrlJ4qSBsD+8czeZyuzvmsB+Sel0qeTltApb+/ZG/KIrM76qdi2xk1Mabb49
+YtbkvnfVpaO8SHgBm86EDKxKMvBlCKdfgS4aqkcjjOfahBXfn0d1EIm7UtLUoAuU
+TDG2lihxhDtzeMdM6W3fPjvVTsMoNSw4vyMW2jNwXOEi3ekjsDT37qtjYP7TjmyI
+NS20P9dgRQwVpc5IIwNg5CW/xkpYFJtWfXooZiMzXbX6u/zyDQY2dcdPzYJL6iZJ
+dnM1e+Z8aB8sNShKAsYxiLqj8qK0/JKITAQYEQIADAUCQEjDCgUJA8JnAAAKCRC5
+eaEVNk2MftEEAKCtCN/jQibduopySodv4eCB/ayr6QCgsks47qwddcpKOjj6t2dE
+LlyuImI=
+=FGUc
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/connectors/LICENSE b/connectors/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/connectors/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/connectors/NOTICE b/connectors/NOTICE
new file mode 100644
index 0000000..3f59805
--- /dev/null
+++ b/connectors/NOTICE
@@ -0,0 +1,2 @@
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/connectors/README.txt b/connectors/README.txt
new file mode 100644
index 0000000..06bef93
--- /dev/null
+++ b/connectors/README.txt
@@ -0,0 +1,11 @@
+Jakarta Tomcat Connector 
+
+This package contains only the tomcat connector:
+
+- tomcat-utils.jar includes general purpose utilities used in tomcat - buffers, 
+thread pools, network code, introspection
+- tomcat-coyote.jar includes the base representation of Request, Response and the code common
+to all protocols
+- tomcat-http11.jar includes the HTTP/1.1 implementation, based on coyote.
+- tomcat-jk2.jar includes the Ajp java implementation along with Jk2 java components
+
diff --git a/connectors/RELEASE-NOTES.txt b/connectors/RELEASE-NOTES.txt
new file mode 100644
index 0000000..bc8fb46
--- /dev/null
+++ b/connectors/RELEASE-NOTES.txt
@@ -0,0 +1,9 @@
+
+                     Tomcat Connectors Version @VERSION@
+                            Release Notes
+
+This version matches the version included with tomcat-5.0.2, and supports tomcat 3.3, 4.0, 4.1, 5.0.
+Bugs and issues will be tracked with tomcat5 - this file will list only issues related with
+older versions.
+
+
diff --git a/connectors/ajp/CHANGES b/connectors/ajp/CHANGES
new file mode 100644
index 0000000..62cd2a8
--- /dev/null
+++ b/connectors/ajp/CHANGES
@@ -0,0 +1,6 @@
+JAKARTA TOMCAT CONNECTORS AJP CHANGELOG:            -*-text-*-
+Last modified at [$Date$]
+
+Changes in AJP HEAD:
+    * Imported proxy sources from 2.1-HEAD
+      [Mladen Turk]
diff --git a/connectors/ajp/ajplib/include/ajp.h b/connectors/ajp/ajplib/include/ajp.h
new file mode 100644
index 0000000..67c0d0b
--- /dev/null
+++ b/connectors/ajp/ajplib/include/ajp.h
@@ -0,0 +1,453 @@
+/* Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AJP_H
+#define AJP_H
+
+#include "apr_version.h"
+#include "apr.h"
+
+#include "apr_hooks.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "apr_buckets.h"
+#include "apr_md5.h"
+#include "apr_network_io.h"
+#include "apr_pools.h"
+#include "apr_strings.h"
+#include "apr_uri.h"
+#include "apr_date.h"
+#include "apr_fnmatch.h"
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
+#if APR_HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if APR_HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#define AJP13_DEF_HOST "127.0.0.1"
+#ifdef NETWARE
+#define AJP13_DEF_PORT 9009     /* default to 9009 since 8009 is used by OS */
+#else
+#define AJP13_DEF_PORT 8009
+#endif
+
+/* The following environment variables match mod_ssl! */
+#define AJP13_HTTPS_INDICATOR           "HTTPS"
+#define AJP13_SSL_CLIENT_CERT_INDICATOR "SSL_CLIENT_CERT"
+#define AJP13_SSL_CIPHER_INDICATOR      "SSL_CIPHER"
+#define AJP13_SSL_SESSION_INDICATOR     "SSL_SESSION_ID"
+#define AJP13_SSL_KEY_SIZE_INDICATOR    "SSL_CIPHER_USEKEYSIZE"
+
+#if APR_CHARSET_EBCDIC
+
+#define USE_CHARSET_EBCDIC
+#define ajp_xlate_to_ascii(b, l) ap_xlate_proto_to_ascii(b, l)
+#define ajp_xlate_from_ascii(b, l) ap_xlate_proto_from_ascii(b, l)
+
+#else                           /* APR_CHARSET_EBCDIC */
+
+#define ajp_xlate_to_ascii(b, l) 
+#define ajp_xlate_from_ascii(b, l) 
+
+#endif
+
+#ifdef AJP_USE_HTTPD_WRAP
+#include "httpd_wrap.h"
+#else
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "http_log.h"
+#endif
+
+
+/** AJP Specific error codes
+ */
+/** Buffer overflow exception */
+#define AJP_EOVERFLOW           (APR_OS_START_USERERR + 1) 
+/** Destination Buffer is to small */
+#define AJP_ETOSMALL            (APR_OS_START_USERERR + 2) 
+/** Invalid input parameters */
+#define AJP_EINVAL              (APR_OS_START_USERERR + 3) 
+/** Bad message signature */
+#define AJP_EBAD_SIGNATURE      (APR_OS_START_USERERR + 4) 
+/** Incoming message too bg */
+#define AJP_ETOBIG              (APR_OS_START_USERERR + 5) 
+/** Missing message header */
+#define AJP_ENO_HEADER          (APR_OS_START_USERERR + 6) 
+/** Bad message header */
+#define AJP_EBAD_HEADER         (APR_OS_START_USERERR + 7) 
+/** Bad message */
+#define AJP_EBAD_MESSAGE        (APR_OS_START_USERERR + 8) 
+/** Cant log via AJP14 */
+#define AJP_ELOGFAIL            (APR_OS_START_USERERR + 9) 
+
+/** A structure that represents ajp message */ 
+typedef struct ajp_msg ajp_msg_t;
+
+/** A structure that represents ajp message */ 
+struct ajp_msg
+{
+    /** The buffer holding a AJP message */ 
+    apr_byte_t  *buf;
+    /** The length of AJP message header (defaults to AJP_HEADER_LEN) */ 
+    apr_size_t  header_len;
+    /** The length of AJP message */ 
+    apr_size_t  len;
+    /** The current read position */ 
+    apr_size_t  pos;
+    /** Flag indicating the origing of the message */ 
+    int         server_side;
+};
+
+/**
+ * @defgroup AJP_defines AJP definitions 
+ * @{
+ */
+/**
+ * Signature for the messages sent from Apache to tomcat
+ */
+#define AJP13_WS_HEADER             0x1234
+#define AJP_HEADER_LEN              4
+#define AJP_HEADER_SZ_LEN           2
+#define AJP_MSG_BUFFER_SZ           (8*1024)
+#define AJP13_MAX_SEND_BODY_SZ      (AJP_MSG_BUFFER_SZ - 6)
+
+/** Send a request from web server to container*/
+#define CMD_AJP13_FORWARD_REQUEST   (unsigned char)2
+/** Write a body chunk from the servlet container to the web server */
+#define CMD_AJP13_SEND_BODY_CHUNK   (unsigned char)3
+/** Send response headers from the servlet container to the web server. */
+#define CMD_AJP13_SEND_HEADERS      (unsigned char)4
+/** Marks the end of response. */
+#define CMD_AJP13_END_RESPONSE      (unsigned char)5
+/** Get further data from the web server if it hasn't all been transferred yet. */
+#define CMD_AJP13_GET_BODY_CHUNK    (unsigned char)6
+/** The web server asks the container to shut itself down. */
+#define CMD_AJP13_SHUTDOWN          (unsigned char)7
+/** Webserver ask container to take control (logon phase) */
+#define CMD_AJP13_PING              (unsigned char)8
+/** Container response to cping request */
+#define CMD_AJP13_CPONG             (unsigned char)9
+/** Webserver check if container is alive, since container should respond by cpong */
+#define CMD_AJP13_CPING             (unsigned char)10
+
+/** @} */
+
+/**
+ * @defgroup AJP_api AJP API functions
+ * @{
+ */
+/**
+ * Check a new AJP Message by looking at signature and return its size
+ *
+ * @param msg       AJP Message to check
+ * @param len       Pointer to returned len
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_check_header(ajp_msg_t *msg, apr_size_t *len);
+
+/**
+ * Reset an AJP Message
+ *
+ * @param msg       AJP Message to reset
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_reset(ajp_msg_t *msg);
+
+/**
+ * Mark the end of an AJP Message
+ *
+ * @param msg       AJP Message to end
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_end(ajp_msg_t *msg);
+
+/**
+ * Add an unsigned 32bits value to AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param value     value to add to AJP Message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_append_uint32(ajp_msg_t *msg, apr_uint32_t value);
+
+/**
+ * Add an unsigned 16bits value to AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param value     value to add to AJP Message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_append_uint16(ajp_msg_t *msg, apr_uint16_t value);
+
+/**
+ * Add an unsigned 8bits value to AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param value     value to add to AJP Message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_append_uint8(ajp_msg_t *msg, apr_byte_t value);
+
+/**
+ *  Add a String in AJP message, and transform the String in ASCII 
+ *  if convert is set and we're on an EBCDIC machine    
+ *
+ * @param msg       AJP Message to get value from
+ * @param value     Pointer to String
+ * @param convert   When set told to convert String to ASCII
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_append_string_ex(ajp_msg_t *msg, const char *value,
+                                      int convert);
+/**
+ *  Add a String in AJP message, and transform 
+ *  the String in ASCII if we're on an EBCDIC machine    
+ */
+#define ajp_msg_append_string(m, v) ajp_msg_append_string_ex(m, v, 1)
+
+/**
+ *  Add a String in AJP message. 
+ */
+#define ajp_msg_append_string_ascii(m, v) ajp_msg_append_string_ex(m, v, 0)
+
+/**
+ * Add a Byte array to AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param value     Pointer to Byte array
+ * @param valuelen  Byte array len
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_append_bytes(ajp_msg_t *msg, const apr_byte_t *value,
+                                  apr_size_t valuelen);
+
+/**
+ * Get a 32bits unsigned value from AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param rvalue    Pointer where value will be returned
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_get_uint32(ajp_msg_t *msg, apr_uint32_t *rvalue);
+
+/**
+ * Get a 16bits unsigned value from AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param rvalue    Pointer where value will be returned
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_get_uint16(ajp_msg_t *msg, apr_uint16_t *rvalue);
+
+/**
+ * Peek a 16bits unsigned value from AJP Message, position in message
+ * is not updated
+ *
+ * @param msg       AJP Message to get value from
+ * @param rvalue    Pointer where value will be returned
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_peek_uint16(ajp_msg_t *msg, apr_uint16_t *rvalue);
+
+/**
+ * Get a 8bits unsigned value from AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param rvalue    Pointer where value will be returned
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_get_uint8(ajp_msg_t *msg, apr_byte_t *rvalue);
+
+/**
+ * Peek a 8bits unsigned value from AJP Message, position in message
+ * is not updated
+ *
+ * @param msg       AJP Message to get value from
+ * @param rvalue    Pointer where value will be returned
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_peek_uint8(ajp_msg_t *msg, apr_byte_t *rvalue);
+
+/**
+ * Get a String value from AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param rvalue    Pointer where value will be returned
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_get_string(ajp_msg_t *msg, char **rvalue);
+
+
+/**
+ * Get a Byte array from AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param rvalue    Pointer where value will be returned
+ * @param rvalueLen Pointer where Byte array len will be returned
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_get_bytes(ajp_msg_t *msg, apr_byte_t **rvalue,
+                               apr_size_t *rvalue_len);
+
+/**
+ * Create an AJP Message from pool
+ *
+ * @param pool      memory pool to allocate AJP message from
+ * @param rmsg      Pointer to newly created AJP message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_create(apr_pool_t *pool, ajp_msg_t **rmsg);
+
+/**
+ * Recopy an AJP Message to another
+ *
+ * @param smsg      source AJP message
+ * @param dmsg      destination AJP message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_copy(ajp_msg_t *smsg, ajp_msg_t *dmsg);
+
+/**
+ * Serialize in an AJP Message a PING command
+ *
+ * +-----------------------+
+ * | PING CMD (1 byte)     |
+ * +-----------------------+
+ *
+ * @param smsg      AJP message to put serialized message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_serialize_ping(ajp_msg_t *msg);
+
+/** 
+ * Serialize in an AJP Message a CPING command
+ *
+ * +-----------------------+
+ * | CPING CMD (1 byte)    |
+ * +-----------------------+
+ *
+ * @param smsg      AJP message to put serialized message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_serialize_cping(ajp_msg_t *msg);
+
+/**
+ * Dump up to the first 1024 bytes on an AJP Message
+ *
+ * @param msg       AJP Message to dump
+ * @param err       error string to display
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_dump(ajp_msg_t *msg, char *err);
+
+/** 
+ * Send an AJP message to backend
+ *
+ * @param soct      backend socket
+ * @param smsg      AJP message to put serialized message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_ilink_send(apr_socket_t *sock, ajp_msg_t *msg);
+
+/** 
+ * Receive an AJP message from backend
+ *
+ * @param sock      backend socket
+ * @param smsg      AJP message to put serialized message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_ilink_receive(apr_socket_t *sock, ajp_msg_t *msg);
+
+/**
+ * Build the ajp header message and send it
+ * @param sock      backend socket
+ * @param r         current request
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_send_header(apr_socket_t *sock, request_rec  *r);
+
+/**
+ * Read the ajp message and return the type of the message.
+ * @param sock      backend socket
+ * @param r         current request
+ * @param msg       returned AJP message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_read_header(apr_socket_t *sock,
+                             request_rec  *r,
+                             ajp_msg_t **msg);
+
+/**
+ * Allocate a msg to send data
+ * @param r         current request
+ * @param ptr       data buffer
+ * @param len       the length of allocated data buffer
+ * @param msg       returned AJP message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t  ajp_alloc_data_msg(request_rec *r, char **ptr, apr_size_t *len,
+                                 ajp_msg_t **msg);
+
+/**
+ * Send the data message
+ * @param sock      backend socket
+ * @param r         current request
+ * @param msg       AJP message to send
+ * @param len       AJP message length      
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t  ajp_send_data_msg(apr_socket_t *sock, request_rec  *r,
+                                ajp_msg_t *msg, apr_size_t len);
+
+/**
+ * Parse the message type 
+ * @param r         current request
+ * @param msg       AJP message
+ * @return          AJP message type.
+ */
+int ajp_parse_type(request_rec  *r, ajp_msg_t *msg);
+
+/**
+ * Parse the header message from container 
+ * @param r         current request
+ * @param msg       AJP message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_parse_header(request_rec  *r, ajp_msg_t *msg);
+
+/** 
+ * Parse the message body and return data address and length 
+ * @param r         current request
+ * @param msg       AJP message
+ * @param len       returned AJP message length 
+ * @param ptr       returned data
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t  ajp_parse_data(request_rec  *r, ajp_msg_t *msg,
+                             apr_uint16_t *len, char **ptr);
+
+/** @} */
+
+#endif /* AJP_H */
+
diff --git a/connectors/ajp/ajplib/include/ajp_header.h b/connectors/ajp/ajplib/include/ajp_header.h
new file mode 100644
index 0000000..ff2b3f4
--- /dev/null
+++ b/connectors/ajp/ajplib/include/ajp_header.h
@@ -0,0 +1,165 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+#ifndef AJP_HEADER_H
+#define AJP_HEADER_H
+
+/*
+ * Conditional request attributes
+ * 
+ */
+#define SC_A_CONTEXT            (unsigned char)1
+#define SC_A_SERVLET_PATH       (unsigned char)2
+#define SC_A_REMOTE_USER        (unsigned char)3
+#define SC_A_AUTH_TYPE          (unsigned char)4
+#define SC_A_QUERY_STRING       (unsigned char)5
+#define SC_A_JVM_ROUTE          (unsigned char)6
+#define SC_A_SSL_CERT           (unsigned char)7
+#define SC_A_SSL_CIPHER         (unsigned char)8
+#define SC_A_SSL_SESSION        (unsigned char)9
+#define SC_A_REQ_ATTRIBUTE      (unsigned char)10
+#define SC_A_SSL_KEY_SIZE       (unsigned char)11       /* only in if JkOptions +ForwardKeySize */
+#define SC_A_SECRET             (unsigned char)12
+#define SC_A_ARE_DONE           (unsigned char)0xFF
+
+/*
+ * Request methods, coded as numbers instead of strings.
+ * The list of methods was taken from Section 5.1.1 of RFC 2616,
+ * RFC 2518, the ACL IETF draft, and the DeltaV IESG Proposed Standard.
+ *          Method        = "OPTIONS"
+ *                        | "GET"    
+ *                        | "HEAD"   
+ *                        | "POST"   
+ *                        | "PUT"    
+ *                        | "DELETE" 
+ *                        | "TRACE"  
+ *                        | "PROPFIND"
+ *                        | "PROPPATCH"
+ *                        | "MKCOL"
+ *                        | "COPY"
+ *                        | "MOVE"
+ *                        | "LOCK"
+ *                        | "UNLOCK"
+ *                        | "ACL"
+ *                        | "REPORT"
+ *                        | "VERSION-CONTROL"
+ *                        | "CHECKIN"
+ *                        | "CHECKOUT"
+ *                        | "UNCHECKOUT"
+ *                        | "SEARCH"
+ *                        | "MKWORKSPACE"
+ *                        | "UPDATE"
+ *                        | "LABEL"
+ *                        | "MERGE"
+ *                        | "BASELINE-CONTROL"
+ *                        | "MKACTIVITY"
+ * 
+ */
+#define SC_M_OPTIONS            (unsigned char)1
+#define SC_M_GET                (unsigned char)2
+#define SC_M_HEAD               (unsigned char)3
+#define SC_M_POST               (unsigned char)4
+#define SC_M_PUT                (unsigned char)5
+#define SC_M_DELETE             (unsigned char)6
+#define SC_M_TRACE              (unsigned char)7
+#define SC_M_PROPFIND           (unsigned char)8
+#define SC_M_PROPPATCH          (unsigned char)9
+#define SC_M_MKCOL              (unsigned char)10
+#define SC_M_COPY               (unsigned char)11
+#define SC_M_MOVE               (unsigned char)12
+#define SC_M_LOCK               (unsigned char)13
+#define SC_M_UNLOCK             (unsigned char)14
+#define SC_M_ACL                (unsigned char)15
+#define SC_M_REPORT             (unsigned char)16
+#define SC_M_VERSION_CONTROL    (unsigned char)17
+#define SC_M_CHECKIN            (unsigned char)18
+#define SC_M_CHECKOUT           (unsigned char)19
+#define SC_M_UNCHECKOUT         (unsigned char)20
+#define SC_M_SEARCH             (unsigned char)21
+#define SC_M_MKWORKSPACE        (unsigned char)22
+#define SC_M_UPDATE             (unsigned char)23
+#define SC_M_LABEL              (unsigned char)24
+#define SC_M_MERGE              (unsigned char)25
+#define SC_M_BASELINE_CONTROL   (unsigned char)26
+#define SC_M_MKACTIVITY         (unsigned char)27
+
+
+/*
+ * Frequent request headers, these headers are coded as numbers
+ * instead of strings.
+ * 
+ * Accept
+ * Accept-Charset
+ * Accept-Encoding
+ * Accept-Language
+ * Authorization
+ * Connection
+ * Content-Type
+ * Content-Length
+ * Cookie
+ * Cookie2
+ * Host
+ * Pragma
+ * Referer
+ * User-Agent
+ * 
+ */
+
+#define SC_ACCEPT               (unsigned short)0xA001
+#define SC_ACCEPT_CHARSET       (unsigned short)0xA002
+#define SC_ACCEPT_ENCODING      (unsigned short)0xA003
+#define SC_ACCEPT_LANGUAGE      (unsigned short)0xA004
+#define SC_AUTHORIZATION        (unsigned short)0xA005
+#define SC_CONNECTION           (unsigned short)0xA006
+#define SC_CONTENT_TYPE         (unsigned short)0xA007
+#define SC_CONTENT_LENGTH       (unsigned short)0xA008
+#define SC_COOKIE               (unsigned short)0xA009    
+#define SC_COOKIE2              (unsigned short)0xA00A
+#define SC_HOST                 (unsigned short)0xA00B
+#define SC_PRAGMA               (unsigned short)0xA00C
+#define SC_REFERER              (unsigned short)0xA00D
+#define SC_USER_AGENT           (unsigned short)0xA00E
+
+/*
+ * Frequent response headers, these headers are coded as numbers
+ * instead of strings.
+ * 
+ * Content-Type
+ * Content-Language
+ * Content-Length
+ * Date
+ * Last-Modified
+ * Location
+ * Set-Cookie
+ * Servlet-Engine
+ * Status
+ * WWW-Authenticate
+ * 
+ */
+
+#define SC_RESP_CONTENT_TYPE        (unsigned short)0xA001
+#define SC_RESP_CONTENT_LANGUAGE    (unsigned short)0xA002
+#define SC_RESP_CONTENT_LENGTH      (unsigned short)0xA003
+#define SC_RESP_DATE                (unsigned short)0xA004
+#define SC_RESP_LAST_MODIFIED       (unsigned short)0xA005
+#define SC_RESP_LOCATION            (unsigned short)0xA006
+#define SC_RESP_SET_COOKIE          (unsigned short)0xA007
+#define SC_RESP_SET_COOKIE2         (unsigned short)0xA008
+#define SC_RESP_SERVLET_ENGINE      (unsigned short)0xA009
+#define SC_RESP_STATUS              (unsigned short)0xA00A
+#define SC_RESP_WWW_AUTHENTICATE    (unsigned short)0xA00B
+#define SC_RES_HEADERS_NUM          11
+
+#endif /* AJP_HEADER_H */
diff --git a/connectors/ajp/ajplib/include/ajp_logon.h b/connectors/ajp/ajplib/include/ajp_logon.h
new file mode 100644
index 0000000..b33081a
--- /dev/null
+++ b/connectors/ajp/ajplib/include/ajp_logon.h
@@ -0,0 +1,51 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+
+/*
+ * +-------------------------+-------------------------+
+ * | LOGIN SEED CMD (1 byte) | MD5 of entropy (String) |
+ * +-------------------------+-------------------------+
+ *
+ * +--------------------+------------------------+------------------------------+
+ * | LOGOK CMD (1 byte) | NEGOCIED DATA (32bits) | SERVLET ENGINE INFO(CString) |
+ * +--------------------+------------------------+------------------------------+
+ *
+ *
+ * +---------------------+-----------------------+
+ * | LOGNOK CMD (1 byte) | FAILURE CODE (32bits) |
+ * +---------------------+-----------------------+
+ */
+ 
+/*
+ * Third Login Phase (web server -> servlet engine), md5 of seed + secret is sent
+ */
+#define AJP14_LOGCOMP_CMD	            (apr_byte_t)0x12
+
+/* web-server want context info after login */
+#define AJP14_CONTEXT_INFO_NEG          0x80000000
+
+/* web-server want context updates */
+#define AJP14_CONTEXT_UPDATE_NEG        0x40000000
+
+/* communication could use AJP14 */
+#define AJP14_PROTO_SUPPORT_AJP14_NEG   0x00010000
+
+#define AJP14_ENTROPY_SEED_LEN		    32      /* we're using MD5 => 32 chars */
+#define AJP14_COMPUTED_KEY_LEN		    32      /* we're using MD5 also */
+
+
+#define AJP14_MD5_DIGESTSIZE            16
diff --git a/connectors/ajp/ajplib/src/ajp_header.c b/connectors/ajp/ajplib/src/ajp_header.c
new file mode 100644
index 0000000..2948c73
--- /dev/null
+++ b/connectors/ajp/ajplib/src/ajp_header.c
@@ -0,0 +1,725 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "ajp_header.h"
+#include "ajp.h"
+
+static const char *response_trans_headers[] = {
+    "Content-Type", 
+    "Content-Language", 
+    "Content-Length", 
+    "Date", 
+    "Last-Modified", 
+    "Location", 
+    "Set-Cookie", 
+    "Set-Cookie2", 
+    "Servlet-Engine", 
+    "Status", 
+    "WWW-Authenticate"
+};
+
+static const char *long_res_header_for_sc(int sc) 
+{
+    const char *rc = NULL;
+    sc = sc & 0X00FF;
+    if(sc <= SC_RES_HEADERS_NUM && sc > 0) {
+        rc = response_trans_headers[sc - 1];
+    }
+
+    return rc;
+}
+
+#define UNKNOWN_METHOD (-1)
+
+static int sc_for_req_header(const char *header_name)
+{
+    char header[16];
+    apr_size_t len = strlen(header_name);
+    const char *p = header_name;
+    int i = 0;
+
+    /* ACCEPT-LANGUAGE is the longest headeer
+     * that is of interest.
+     */
+    if (len < 4 || len > 15)
+        return UNKNOWN_METHOD;
+    
+    while (*p)
+        header[i++] = apr_toupper(*p++);
+    header[i] = '\0';
+    p = &header[1];
+
+    switch (header[0]) {
+        case 'A':
+            if (memcmp(p, "CCEPT", 5) == 0) {
+                if (!header[6])
+                    return SC_ACCEPT;
+                else if (header[6] == '-') {
+                    p += 6;
+                    if (memcmp(p, "CHARSET", 7) == 0)
+                        return SC_ACCEPT_CHARSET;
+                    else if (memcmp(p,  "ENCODING", 8) == 0)
+                        return SC_ACCEPT_ENCODING;
+                    else if (memcmp(p, "LANGUAGE", 8) == 0)
+                        return SC_ACCEPT_LANGUAGE;
+                    else
+                        return UNKNOWN_METHOD;
+                }
+                else
+                    return UNKNOWN_METHOD;
+            }
+            else if (memcmp(p, "UTHORIZATION", 12) == 0)
+                return SC_AUTHORIZATION;
+            else
+                return UNKNOWN_METHOD;
+        break;
+        case 'C':
+            if (memcmp(p, "OOKIE", 5) == 0)
+                return SC_COOKIE;
+            else if(memcmp(p, "ONNECTION", 9) == 0)
+                return SC_CONNECTION;
+            else if(memcmp(p, "ONTENT-TYPE", 11) == 0)
+                return SC_CONTENT_TYPE;
+            else if(memcmp(p, "ONTENT-LENGTH", 13) == 0)
+                return SC_CONTENT_LENGTH;
+            else if(memcmp(p, "OOKIE2", 6) == 0)
+                return SC_COOKIE2;
+            else
+                return UNKNOWN_METHOD;
+        break;
+        case 'H':
+            if(memcmp(p, "OST", 3) == 0)
+                return SC_HOST;
+            else
+                return UNKNOWN_METHOD;
+        break;
+        case 'P':
+            if(memcmp(p, "RAGMA", 5) == 0)
+                return SC_PRAGMA;
+            else
+                return UNKNOWN_METHOD;
+        break;
+        case 'R':
+            if(memcmp(p, "EFERER", 6) == 0)
+                return SC_REFERER;
+            else
+                return UNKNOWN_METHOD;
+        break;
+        case 'U':
+            if(memcmp(p, "SER-AGENT", 9) == 0)
+                return SC_USER_AGENT;
+            else
+                return UNKNOWN_METHOD;
+        break;
+        default:
+            return UNKNOWN_METHOD;
+    }
+
+    /* NOTREACHED */
+}
+
+/* Apache method number to SC methods transform table */
+static const unsigned char sc_for_req_method_table[] = {
+    SC_M_GET,
+    SC_M_PUT,
+    SC_M_POST,
+    SC_M_DELETE,
+    0,                      /* M_DELETE */
+    SC_M_OPTIONS,
+    SC_M_TRACE,
+    0,                      /* M_PATCH  */
+    SC_M_PROPFIND,
+    SC_M_PROPPATCH,
+    SC_M_MKCOL,
+    SC_M_COPY,
+    SC_M_MOVE,
+    SC_M_LOCK,
+    SC_M_UNLOCK,
+    SC_M_VERSION_CONTROL,
+    SC_M_CHECKOUT,
+    SC_M_UNCHECKOUT,
+    SC_M_CHECKIN,
+    SC_M_UPDATE,
+    SC_M_LABEL,
+    SC_M_REPORT,
+    SC_M_MKWORKSPACE,
+    SC_M_MKACTIVITY,
+    SC_M_BASELINE_CONTROL,
+    SC_M_MERGE,           
+    0                       /* M_INVALID */
+};
+
+static int sc_for_req_method_by_id(int method_id)
+{
+    if (method_id < 0 || method_id > M_INVALID)
+        return UNKNOWN_METHOD;
+    else
+        return sc_for_req_method_table[method_id] ?
+               sc_for_req_method_table[method_id] : UNKNOWN_METHOD;
+}
+
+/*
+ * Message structure
+ *
+ *
+AJPV13_REQUEST/AJPV14_REQUEST=
+    request_prefix (1) (byte)
+    method         (byte)
+    protocol       (string)
+    req_uri        (string)
+    remote_addr    (string)
+    remote_host    (string)
+    server_name    (string)
+    server_port    (short)
+    is_ssl         (boolean)
+    num_headers    (short)
+    num_headers*(req_header_name header_value)
+
+    ?context       (byte)(string)
+    ?servlet_path  (byte)(string)
+    ?remote_user   (byte)(string)
+    ?auth_type     (byte)(string)
+    ?query_string  (byte)(string)
+    ?jvm_route     (byte)(string)
+    ?ssl_cert      (byte)(string)
+    ?ssl_cipher    (byte)(string)
+    ?ssl_session   (byte)(string)
+    ?ssl_key_size  (byte)(int)      via JkOptions +ForwardKeySize
+    request_terminator (byte)
+    ?body          content_length*(var binary)
+
+ */
+
+static apr_status_t ajp_marshal_into_msgb(ajp_msg_t    *msg,
+                                 request_rec *r)
+{
+    int method;
+    apr_uint32_t i, num_headers = 0;
+    apr_byte_t is_ssl;
+    char *remote_host;
+    char *uri;
+    const char *session_route, *envvar;
+    const apr_array_header_t *arr = apr_table_elts(r->subprocess_env);
+    const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                         "Into ajp_marshal_into_msgb");
+
+    if ((method = sc_for_req_method_by_id(r->method_number)) == UNKNOWN_METHOD) { 
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+               "Error ajp_marshal_into_msgb - No such method %s",
+               r->method);
+        return APR_EGENERAL;
+    }
+
+    /* XXXX need something */
+    is_ssl = (apr_byte_t) 0; /* s->is_ssl */
+
+    if (r->headers_in && apr_table_elts(r->headers_in)) {
+        const apr_array_header_t *t = apr_table_elts(r->headers_in);
+        num_headers = t->nelts;
+    }
+
+    remote_host = (char *)ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_HOST, NULL);
+
+    uri = apr_pstrdup(r->pool, r->uri);
+    if (uri != NULL) {
+        char *query_str = strchr(uri, '?');
+        if (query_str != NULL) {
+            *query_str = 0;
+        }
+    }
+    
+
+    ajp_msg_reset(msg);
+
+    if (ajp_msg_append_uint8(msg, CMD_AJP13_FORWARD_REQUEST)     ||
+        ajp_msg_append_uint8(msg, method)                        ||
+        ajp_msg_append_string(msg, r->protocol)                  ||
+        ajp_msg_append_string(msg, uri)                          ||
+        ajp_msg_append_string(msg, r->connection->remote_ip)     ||
+        ajp_msg_append_string(msg, remote_host)                  ||
+        ajp_msg_append_string(msg, ap_get_server_name(r))        ||
+        ajp_msg_append_uint16(msg, (apr_uint16_t)r->connection->local_addr->port) ||
+        ajp_msg_append_uint8(msg, is_ssl)                        ||
+        ajp_msg_append_uint16(msg, (apr_uint16_t) num_headers)) {
+
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+               "Error ajp_marshal_into_msgb - "
+               "Error appending the message begining");
+        return APR_EGENERAL;
+    }
+
+    for (i = 0 ; i < num_headers ; i++) {
+        int sc;
+        const apr_array_header_t *t = apr_table_elts(r->headers_in);
+        const apr_table_entry_t *elts = (apr_table_entry_t *)t->elts;
+
+        if ((sc = sc_for_req_header(elts[i].key)) != UNKNOWN_METHOD) {
+            if (ajp_msg_append_uint16(msg, (apr_uint16_t)sc)) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                       "Error ajp_marshal_into_msgb - "
+                       "Error appending the header name");
+                return APR_EGENERAL;
+            }
+        }
+        else {
+            if (ajp_msg_append_string(msg, elts[i].key)) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                       "Error ajp_marshal_into_msgb - "
+                       "Error appending the header name");
+                return APR_EGENERAL;
+            }
+        }
+        
+        if (ajp_msg_append_string(msg, elts[i].val)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the header value");
+            return APR_EGENERAL;
+        }
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                   "ajp_marshal_into_msgb: Header[%d] [%s] = [%s]",
+                   i, elts[i].key, elts[i].val);
+    }
+
+/* XXXX need to figure out how to do this
+    if (s->secret) {
+        if (ajp_msg_append_uint8(msg, SC_A_SECRET) ||
+            ajp_msg_append_string(msg, s->secret)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending secret");
+            return APR_EGENERAL;
+        }
+    }
+ */
+        
+    if (r->user) {
+        if (ajp_msg_append_uint8(msg, SC_A_REMOTE_USER) ||
+            ajp_msg_append_string(msg, r->user)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the remote user");
+            return APR_EGENERAL;
+        }
+    }
+    if (r->ap_auth_type) {
+        if (ajp_msg_append_uint8(msg, SC_A_AUTH_TYPE) ||
+            ajp_msg_append_string(msg, r->ap_auth_type)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the auth type");
+            return APR_EGENERAL;
+        }
+    }
+    /* XXXX  ebcdic (args converted?) */
+    if (r->args) {
+        if (ajp_msg_append_uint8(msg, SC_A_QUERY_STRING) ||
+            ajp_msg_append_string(msg, r->args)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the query string");
+            return APR_EGENERAL;
+        }
+    }
+    if ((session_route = apr_table_get(r->notes, "session-route"))) {
+        if (ajp_msg_append_uint8(msg, SC_A_JVM_ROUTE) ||
+            ajp_msg_append_string(msg, session_route)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the jvm route");
+            return APR_EGENERAL;
+        }
+    }
+/* XXX: Is the subprocess_env a right place?
+ * <Location /examples>
+ *   ProxyPass ajp://remote:8009/servlets-examples 
+ *   SetEnv SSL_SESSION_ID CUSTOM_SSL_SESSION_ID
+ * </Location>
+ */
+    if ((envvar = apr_table_get(r->subprocess_env,
+                                AJP13_SSL_CLIENT_CERT_INDICATOR))) {
+        if (ajp_msg_append_uint8(msg, SC_A_SSL_CERT) ||
+            ajp_msg_append_string(msg, envvar)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the SSL certificates");
+            return APR_EGENERAL;
+        }
+    }
+
+    if ((envvar = apr_table_get(r->subprocess_env,
+                                AJP13_SSL_CIPHER_INDICATOR))) {
+        if (ajp_msg_append_uint8(msg, SC_A_SSL_CIPHER) ||
+            ajp_msg_append_string(msg, envvar)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the SSL ciphers");
+            return APR_EGENERAL;
+        }
+    }
+
+    if ((envvar = apr_table_get(r->subprocess_env,
+                                AJP13_SSL_SESSION_INDICATOR))) {
+        if (ajp_msg_append_uint8(msg, SC_A_SSL_SESSION) ||
+            ajp_msg_append_string(msg, envvar)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the SSL session");
+            return APR_EGENERAL;
+        }
+    }
+
+    /*
+     * ssl_key_size is required by Servlet 2.3 API
+     * added support only in ajp14 mode
+     * JFC removed: ae->proto == AJP14_PROTO
+     */
+ /* XXXX ignored for the moment
+    if (s->ssl_key_size != -1) {
+        if (ajp_msg_append_uint8(msg, SC_A_SSL_KEY_SIZE) ||
+            ajp_msg_append_uint16(msg, (unsigned short) s->ssl_key_size)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the SSL key size");
+            return APR_EGENERAL;
+        }
+    }
+ */
+    /* Use the environment vars prefixed with AJP_
+     * and pass it to the header striping that prefix.
+     */
+    for (i = 0; i < (apr_uint32_t)arr->nelts; i++) {
+        if (!strncmp(elts[i].key, "AJP_", 4)) {
+            if (ajp_msg_append_uint8(msg, SC_A_REQ_ATTRIBUTE) ||
+                ajp_msg_append_string(msg, elts[i].key + 4)   ||
+                ajp_msg_append_string(msg, elts[i].val)) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                        "Error ajp_marshal_into_msgb - "
+                        "Error appending attribute %s=%s",
+                        elts[i].key, elts[i].val);
+                return APR_EGENERAL;
+            }
+        }
+    }
+
+    if (ajp_msg_append_uint8(msg, SC_A_ARE_DONE)) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+               "Error ajp_marshal_into_msgb - "
+               "Error appending the message end");
+        return APR_EGENERAL;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+           "ajp_marshal_into_msgb - Done");
+    return APR_SUCCESS;
+}
+
+/*
+AJPV13_RESPONSE/AJPV14_RESPONSE:=
+    response_prefix (2)
+    status          (short)
+    status_msg      (short)
+    num_headers     (short)
+    num_headers*(res_header_name header_value)
+    *body_chunk
+    terminator      boolean <! -- recycle connection or not  -->
+
+req_header_name := 
+    sc_req_header_name | (string)
+
+res_header_name := 
+    sc_res_header_name | (string)
+
+header_value :=
+    (string)
+
+body_chunk :=
+    length  (short)
+    body    length*(var binary)
+
+ */
+
+
+static apr_status_t ajp_unmarshal_response(ajp_msg_t   *msg,
+                                  request_rec  *r)
+{
+    apr_uint16_t status;
+    apr_status_t rc;
+    char *ptr;
+    apr_uint16_t  num_headers;
+    int i;
+
+    rc = ajp_msg_get_uint16(msg, &status);
+
+    if (rc != APR_SUCCESS) {
+         ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+               "Error ajp_unmarshal_response - Null status");
+        return APR_EGENERAL;
+    }
+    r->status = status;
+
+    rc = ajp_msg_get_string(msg, &ptr);
+    if (rc == APR_SUCCESS) {
+        r->status_line =  apr_psprintf(r->pool, "%d %s", status, ptr);
+#if defined(AS400) || defined(_OSD_POSIX)
+        ap_xlate_proto_from_ascii(r->status_line, strlen(r->status_line));
+#endif
+    } else {
+        r->status_line = NULL;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+           "ajp_unmarshal_response: status = %d", status);
+
+    rc = ajp_msg_get_uint16(msg, &num_headers);
+    if (rc == APR_SUCCESS) {
+        r->headers_out = apr_table_make(r->pool, num_headers);
+    } else {
+        r->headers_out = NULL;
+        num_headers = 0;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+           "ajp_unmarshal_response: Number of headers is = %d",
+           num_headers);
+
+    for(i = 0 ; i < (int) num_headers ; i++) {
+        apr_uint16_t name;
+        char *stringname;
+        char *value;
+        rc  = ajp_msg_peek_uint16(msg, &name);
+        if (rc != APR_SUCCESS) {
+            return APR_EGENERAL;
+        }
+                
+        if ((name & 0XFF00) == 0XA000) {
+            ajp_msg_peek_uint16(msg, &name);
+            stringname = (char *)long_res_header_for_sc(name);
+            if (stringname == NULL) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                       "Error ajp_unmarshal_response - "
+                       "No such sc (%08x)",
+                       name);
+                return APR_EGENERAL;
+            }
+        } else {
+            name = 0;
+            rc = ajp_msg_get_string(msg, &stringname);
+            if (rc != APR_SUCCESS) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                       "Error ajp_unmarshal_response - "
+                       "Null header name");
+                return APR_EGENERAL;
+            }
+#if defined(AS400) || defined(_OSD_POSIX)
+            ap_xlate_proto_from_ascii(stringname, strlen(stringname));
+#endif
+        }
+
+        rc = ajp_msg_get_string(msg, &value);
+        if (rc != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                   "Error ajp_unmarshal_response - "
+                   "Null header value");
+            return APR_EGENERAL;
+        }
+
+#if defined(AS400) || defined(_OSD_POSIX)
+        ap_xlate_proto_from_ascii(value, strlen(value));
+#endif
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+               "ajp_unmarshal_response: Header[%d] [%s] = [%s]", 
+                       i, stringname, value);
+        apr_table_add(r->headers_out, stringname, value);
+
+        /* Content-type needs an additional handling */
+        if (memcmp(stringname, "Content-Type", 12) == 0) {
+             /* add corresponding filter */
+            ap_set_content_type(r, apr_pstrdup(r->pool, value));
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+               "ajp_unmarshal_response: ap_set_content_type done");
+        }
+    }
+
+    return APR_SUCCESS;
+}
+
+/*
+ * Build the ajp header message and send it
+ */
+apr_status_t ajp_send_header(apr_socket_t *sock,
+                                  request_rec  *r)
+{
+    ajp_msg_t *msg;
+    apr_status_t rc;
+
+    rc = ajp_msg_create(r->pool, &msg);
+    if (rc != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+               "ajp_send_header: ajp_msg_create failed");
+        return rc;
+    }
+
+    rc = ajp_marshal_into_msgb(msg, r);    
+    if (rc != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+               "ajp_send_header: ajp_marshal_into_msgb failed");
+        return rc;
+    }
+
+    rc = ajp_ilink_send(sock, msg);
+    if (rc != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+               "ajp_send_header: ajp_ilink_send failed");
+        return rc;
+    }
+
+    return APR_SUCCESS;
+}
+
+/*
+ * Read the ajp message and return the type of the message.
+ */
+apr_status_t ajp_read_header(apr_socket_t *sock,
+                             request_rec  *r,
+                             ajp_msg_t **msg)
+{
+    apr_byte_t result;
+    apr_status_t rc;
+    
+    rc = ajp_msg_create(r->pool, msg);
+    if (rc != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+               "ajp_read_header: ajp_msg_create failed");
+        return rc;
+    }
+    ajp_msg_reset(*msg);
+    rc = ajp_ilink_receive(sock, *msg);
+    if (rc != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+               "ajp_read_header: ajp_ilink_receive failed");
+        return rc;
+    }
+    rc = ajp_msg_peek_uint8(*msg, &result);
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+               "ajp_read_header: ajp_ilink_received %02x", result);
+    return APR_SUCCESS;
+}
+
+/* parse the msg to read the type */
+int ajp_parse_type(request_rec  *r, ajp_msg_t *msg)
+{
+    apr_byte_t result;
+    ajp_msg_peek_uint8(msg, &result);
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+               "ajp_parse_type: got %02x", result);
+    return (int) result;
+}
+
+/* parse the header */
+apr_status_t ajp_parse_header(request_rec  *r, ajp_msg_t *msg)
+{
+    apr_byte_t result;
+    apr_status_t rc;
+
+    rc = ajp_msg_get_uint8(msg, &result);
+    if (rc != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+               "ajp_parse_headers: ajp_msg_get_byte failed");
+        return rc;
+    }
+    if (result != CMD_AJP13_SEND_HEADERS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+               "ajp_parse_headers: wrong type %02x expecting 0x04", result);
+        return APR_EGENERAL;
+    }
+    return ajp_unmarshal_response(msg, r);
+}
+
+/* parse the body and return data address and length */
+apr_status_t  ajp_parse_data(request_rec  *r, ajp_msg_t *msg,
+                             apr_uint16_t *len, char **ptr)
+{
+    apr_byte_t result;
+    apr_status_t rc;
+
+    rc = ajp_msg_get_uint8(msg, &result);
+    if (rc != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+               "ajp_parse_data: ajp_msg_get_byte failed");
+        return rc;
+    }
+    if (result != CMD_AJP13_SEND_BODY_CHUNK) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+               "ajp_parse_data: wrong type %02x expecting 0x03", result);
+        return APR_EGENERAL;
+    }
+    rc = ajp_msg_get_uint16(msg, len);
+    if (rc != APR_SUCCESS) {
+        return APR_EGENERAL;
+    }
+    *ptr = (char *)&(msg->buf[msg->pos]);
+    return APR_SUCCESS;
+}
+
+/*
+ * Allocate a msg to send data
+ */
+apr_status_t  ajp_alloc_data_msg(request_rec *r, char **ptr, apr_size_t *len,
+                                 ajp_msg_t **msg)
+{
+    apr_status_t rc;
+
+    rc = ajp_msg_create(r->pool, msg);
+    if (rc != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+               "ajp_alloc_data_msg: ajp_msg_create failed");
+        return rc;
+    }
+    ajp_msg_reset(*msg);
+    *ptr = (char *)&((*msg)->buf[6]);
+    *len = AJP_MSG_BUFFER_SZ-6;
+
+    return APR_SUCCESS;
+}
+
+/*
+ * Send the data message
+ */
+apr_status_t  ajp_send_data_msg(apr_socket_t *sock, request_rec  *r,
+                                ajp_msg_t *msg, apr_size_t len)
+{
+    apr_status_t rc;
+
+    msg->buf[4] = (apr_byte_t)((len >> 8) & 0xFF);
+    msg->buf[5] = (apr_byte_t)(len & 0xFF);
+
+    msg->len += len + 2; /* + 1 XXXX where is '\0' */
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+               "ajp_send_data_msg: sending %d", len);
+
+    rc = ajp_ilink_send(sock, msg);
+    if (rc != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+               "ajp_send_data_msg: ajp_ilink_send failed");
+        return rc;
+    }
+
+    return APR_SUCCESS;
+}
diff --git a/connectors/ajp/ajplib/src/ajp_link.c b/connectors/ajp/ajplib/src/ajp_link.c
new file mode 100644
index 0000000..5314be7
--- /dev/null
+++ b/connectors/ajp/ajplib/src/ajp_link.c
@@ -0,0 +1,132 @@
+/* Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ajp.h"
+
+
+apr_status_t ajp_ilink_send(apr_socket_t *sock, ajp_msg_t *msg)
+{
+    char         *buf;
+    apr_status_t status;
+    apr_size_t   length;
+
+    if (sock == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_ilink_send(): NULL socket provided");
+        return AJP_EINVAL;
+    }
+    
+    ajp_msg_end(msg);
+    
+    length = msg->len;
+    buf    = (char *)msg->buf;
+
+    do {
+        apr_size_t written = length;
+
+        status = apr_socket_send(sock, buf, &written);
+        if (status != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, status, NULL,
+                          "ajp_ilink_send(): send failed");
+            return status;
+        }
+        length -= written;
+        buf    += written;
+    } while (length);
+
+    return APR_SUCCESS;
+}
+
+
+static apr_status_t ilink_read(apr_socket_t *sock, char * buf,
+                               apr_size_t len)
+{
+    apr_size_t   length;
+    apr_status_t status;
+    apr_size_t   rdlen;
+
+    if (sock == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_ilink_readN(): NULL socket provided");
+        return AJP_EINVAL;
+    }
+
+    rdlen  = 0;
+    length = len;
+    
+    while (rdlen < len) {
+
+        status = apr_socket_recv(sock, buf + rdlen, &length);
+
+        if (status == APR_EOF)
+            return status;          /* socket closed. */
+        else if (APR_STATUS_IS_EAGAIN(status))
+            continue;
+        else if (status != APR_SUCCESS)
+            return status;          /* any error. */
+            
+        rdlen += length;
+        length = len - rdlen;
+    }
+    return APR_SUCCESS;
+}
+
+
+apr_status_t ajp_ilink_receive(apr_socket_t *sock, ajp_msg_t *msg)
+{
+    apr_status_t status;
+    apr_size_t   hlen;
+    apr_size_t   blen;
+
+    if (sock == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_ilink_receive(): NULL socket provided");
+        return AJP_EINVAL;
+    }
+
+    hlen = msg->header_len;
+    
+    status = ilink_read(sock, msg->buf, hlen);
+    
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, status, NULL,
+                "ajp_ilink_receive() can't receive header\n");
+        return AJP_ENO_HEADER;
+    }
+    
+    status = ajp_msg_check_header(msg, &blen);
+
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_ilink_receive() received bad header\n");
+        return AJP_EBAD_HEADER;
+    }
+
+    status = ilink_read(sock, msg->buf + hlen, blen);
+
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, status, NULL,
+                      "ajp_ilink_receive() error while receiving message body %of length %d\n",
+                      hlen);
+        return AJP_EBAD_MESSAGE;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+                  "ajp_ilink_receive() received packet len=%d type=%d\n",
+                  blen, (int)msg->buf[hlen]);
+
+    return APR_SUCCESS;
+}
+
diff --git a/connectors/ajp/ajplib/src/ajp_logon.c b/connectors/ajp/ajplib/src/ajp_logon.c
new file mode 100644
index 0000000..6c22204
--- /dev/null
+++ b/connectors/ajp/ajplib/src/ajp_logon.c
@@ -0,0 +1,214 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "ajp_logon.h"
+#include "ajp.h"
+
+
+
+/**
+ * Binary to hex C String (null terminated)
+ *
+ * @param org        byte array to transform
+ * @param dst        string result
+ * @param n          len of byte array
+ * @return           APR_SUCCESS or error
+ */
+static char * hextocstr(apr_byte_t *org, char *dst, int n)
+{
+    char       *os = dst;
+    apr_byte_t  v;
+    static char zitohex[] = "0123456789ABCDEF";
+
+    while (--n >= 0) {
+        v = *org++;
+        *dst++ = zitohex[v >> 4];
+        *dst++ = zitohex[v & 0x0f];
+    }
+    *dst = 0;
+
+    return (os);
+}
+
+/**
+ * Compute the MD5 of org and (if not null org2) string
+ *
+ * @param org        First String to compute MD5 from
+ * @param org2       Second String to compute MD5 from (if null no action)
+ * @param dst        Destination MD5 Hex CString
+ * @return           APR_SUCCESS or error
+ */
+apr_status_t comp_md5(char *org, char *org2, char *dst)
+{
+    apr_md5_ctx_t ctx;
+    unsigned char buf[AJP14_MD5_DIGESTSIZE + 1];
+
+    apr_md5_init(&ctx);
+    apr_md5_update(&ctx, org, (apr_size_t)strlen(org));
+
+    if (org2 != NULL)
+        apr_md5_update(&ctx, org2, (apr_size_t)strlen(org2));
+
+    apr_md5_final(buf, &ctx);
+
+    hextocstr(buf, dst, AJP14_MD5_DIGESTSIZE);
+    
+    return APR_SUCCESS;
+}
+
+/**
+ * Decode the Incoming Login Command and build reply
+ *
+ * @param msg        AJP Message to be decoded and then filled
+ * @param secret     secret string to be used in logon phase
+ * @param servername local server name (ie: Apache 2.0.50)
+ * @return           APR_SUCCESS or error
+ */
+apr_status_t ajp_handle_login(ajp_msg_t *msg, char *secret, char *servername)
+{
+    int             status;
+    char            *entropy;
+    char            computedKey[AJP14_COMPUTED_KEY_LEN];
+
+    status = ajp_msg_get_string(msg, &entropy);
+    
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_handle_login(): can't get seed");
+
+        return AJP_ELOGFAIL;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+                 "ajp_handle_login(): received entropy %s",
+                 entropy);
+
+    comp_md5(entropy, secret, computedKey);
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+                 "ajp_handle_login(): computed md5 (%s/%s) -> (%s)",
+                 entropy, secret, computedKey);
+
+    ajp_msg_reset(msg);
+
+    /* LOGCOMP CMD */    
+    status = ajp_msg_append_uint8(msg, AJP14_LOGCOMP_CMD);
+    
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_handle_login(): can't log command");
+
+        return AJP_ELOGFAIL;
+    }
+
+    /* COMPUTED-SEED */
+    status = ajp_msg_append_string(msg, computedKey);
+
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_handle_login(): can't serialize computed secret");
+
+        return AJP_ELOGFAIL;
+    }
+
+    /* NEGOCIATION OPTION */    
+    status = ajp_msg_append_uint32(msg, AJP14_CONTEXT_INFO_NEG | AJP14_PROTO_SUPPORT_AJP14_NEG);
+
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_handle_login(): can't append negociation header");
+
+        return AJP_ELOGFAIL;
+    }
+
+    /* SERVER NAME */    
+    status = ajp_msg_append_string(msg, servername);
+
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_handle_login(): can't serialize server name");
+
+        return AJP_ELOGFAIL;
+    }
+
+    return APR_SUCCESS;
+}
+
+
+/**
+ * Decode the LogOk Command. After that we're done, the connection is
+ * perfect and ready.
+ *
+ * @param msg        AJP Message to be decoded
+ * @return           APR_SUCCESS or error
+ */
+apr_status_t ajp_handle_logok(ajp_msg_t *msg)
+{
+    apr_status_t status;
+    apr_uint32_t negociation;
+    char         *server_name;
+
+    status = ajp_msg_get_uint32(msg, &negociation);
+    
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_handle_logok(): can't get negociation header");
+
+        return AJP_ELOGFAIL;
+    }
+
+    status = ajp_msg_get_string(msg, &server_name);
+
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_handle_logok(): can't get servlet engine name");
+
+        return AJP_ELOGFAIL;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+                 "ajp_handle_logok(): Successfully logged to %s",
+                 server_name);
+
+    return APR_SUCCESS;
+}
+
+
+/**
+ * Decode the Log Nok Command 
+ *
+ * @param msg        AJP Message to be decoded
+ */
+apr_status_t ajp_handle_lognok(ajp_msg_t *msg)
+{
+    apr_status_t status;
+    apr_uint32_t failurecode;
+
+    status = ajp_msg_get_uint32(msg, &failurecode);
+
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_handle_lognok(): can't get failure code");
+
+        return AJP_ELOGFAIL;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+                 "ajp_handle_logok(): logon failure code is %08lx",
+                 (long)failurecode);
+
+    return APR_SUCCESS;
+}
diff --git a/connectors/ajp/ajplib/src/ajp_msg.c b/connectors/ajp/ajplib/src/ajp_msg.c
new file mode 100644
index 0000000..3128d93
--- /dev/null
+++ b/connectors/ajp/ajplib/src/ajp_msg.c
@@ -0,0 +1,607 @@
+/* Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ajp.h"
+
+
+static char *hex_table = "0123456789ABCDEF";
+
+/**
+ * Dump up to the first 1024 bytes on an AJP Message
+ *
+ * @param msg       AJP Message to dump
+ * @param err       error string to display
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_dump(ajp_msg_t *msg, char *err)
+{
+    apr_size_t  i, j;
+    char        line[80];
+    char        *current;
+    apr_byte_t  x;
+    apr_size_t  len = msg->len;
+
+    /* Display only first 1024 bytes */
+    if (len > 1024)
+        len = 1024;
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+                  "ajp_msg_dump(): %s pos=%d len=%d max=%d",
+                  err, msg->pos, msg->len, AJP_MSG_BUFFER_SZ);
+
+    for (i = 0; i < len; i += 16) {
+        current = line;
+
+        for (j = 0; j < 16; j++) {
+             x = msg->buf[i + j];
+
+            *current++ = hex_table[x >> 4];
+            *current++ = hex_table[x & 0x0f];
+            *current++ = ' ';
+        }
+        *current++ = ' ';
+        *current++ = '-';
+        *current++ = ' ';
+        for (j = 0; j < 16; j++) {
+            x = msg->buf[i + j];
+
+            if (x > 0x20 && x < 0x7F) {
+                *current++ = x;
+            }
+            else {
+                *current++ = '.';
+            }
+        }
+
+        *current++ = '\0';
+
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+                      "ajp_msg_dump(): %.4x    %s",
+                      i, line);
+    }
+    
+    return APR_SUCCESS;
+}
+
+
+/**
+ * Check a new AJP Message by looking at signature and return its size
+ *
+ * @param msg       AJP Message to check
+ * @param len       Pointer to returned len
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_check_header(ajp_msg_t *msg, apr_size_t *len)
+{
+    apr_byte_t *head = msg->buf;
+    apr_size_t msglen;
+
+    if (!((head[0] == 0x41 && head[1] == 0x42) ||
+          (head[0] == 0x12 && head[1] == 0x34))) {
+
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_check_msg_header() got bad signature %x%x",
+                      head[0], head[1]);
+
+        return AJP_EBAD_SIGNATURE;
+    }
+
+    msglen  = ((head[2] & 0xff) << 8);
+    msglen += (head[3] & 0xFF);
+
+    if (msglen > AJP_MSG_BUFFER_SZ) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_check_msg_header() incoming message is too big %d, max is %d",
+                      msglen, AJP_MSG_BUFFER_SZ);
+        return AJP_ETOBIG;
+    }
+
+    msg->len = msglen + AJP_HEADER_LEN;
+    msg->pos = AJP_HEADER_LEN;
+    *len     = msglen;
+    
+    return APR_SUCCESS;
+}
+
+/**
+ * Reset an AJP Message
+ *
+ * @param msg       AJP Message to reset
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_reset(ajp_msg_t *msg)
+{
+    msg->len = AJP_HEADER_LEN;
+    msg->pos = AJP_HEADER_LEN;
+    
+    return APR_SUCCESS;
+}
+
+/**
+ * Mark the end of an AJP Message
+ *
+ * @param msg       AJP Message to end
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_end(ajp_msg_t *msg)
+{
+    apr_size_t len = msg->len - AJP_HEADER_LEN;
+
+    if (msg->server_side) {
+        msg->buf[0] = 0x41;
+        msg->buf[1] = 0x42;
+    }
+    else {
+        msg->buf[0] = 0x12;
+        msg->buf[1] = 0x34;
+    }
+
+    msg->buf[2] = (apr_byte_t)((len >> 8) & 0xFF);
+    msg->buf[3] = (apr_byte_t)(len & 0xFF);
+    
+    return APR_SUCCESS;
+}
+
+/**
+ * Add an unsigned 32bits value to AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param value     value to add to AJP Message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_append_uint32(ajp_msg_t *msg, apr_uint32_t value)
+{
+    apr_size_t len = msg->len;
+
+    if ((len + 4) > AJP_MSG_BUFFER_SZ) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_msg_append_uint32(): BufferOverflowException %d %d",
+                      msg->pos, msg->len);
+        return AJP_EOVERFLOW;
+    }
+
+    msg->buf[len]     = (apr_byte_t)((value >> 24) & 0xFF);
+    msg->buf[len + 1] = (apr_byte_t)((value >> 16) & 0xFF);
+    msg->buf[len + 2] = (apr_byte_t)((value >> 8) & 0xFF);
+    msg->buf[len + 3] = (apr_byte_t)(value & 0xFF);
+
+    msg->len += 4;
+
+    return APR_SUCCESS;
+}
+
+/**
+ * Add an unsigned 16bits value to AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param value     value to add to AJP Message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_append_uint16(ajp_msg_t *msg, apr_uint16_t value)
+{
+    apr_size_t len = msg->len;
+
+    if ((len + 2) > AJP_MSG_BUFFER_SZ) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_msg_append_uint16(): BufferOverflowException %d %d",
+                      msg->pos, msg->len);
+        return AJP_EOVERFLOW;
+    }
+
+    msg->buf[len]     = (apr_byte_t)((value >> 8) & 0xFF);
+    msg->buf[len + 1] = (apr_byte_t)(value & 0xFF);
+
+    msg->len += 2;
+
+    return APR_SUCCESS;
+}
+
+/**
+ * Add an unsigned 8bits value to AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param value     value to add to AJP Message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_append_uint8(ajp_msg_t *msg, apr_byte_t value)
+{
+    apr_size_t len = msg->len;
+
+    if ((len + 1) > AJP_MSG_BUFFER_SZ) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_msg_append_uint8(): BufferOverflowException %d %d",
+                      msg->pos, msg->len);
+        return AJP_EOVERFLOW;
+    }
+
+    msg->buf[len] = value;
+    msg->len += 1;
+
+    return APR_SUCCESS;
+}
+
+/**
+ *  Add a String in AJP message, and transform the String in ASCII 
+ *  if convert is set and we're on an EBCDIC machine    
+ *
+ * @param msg       AJP Message to get value from
+ * @param value     Pointer to String
+ * @param convert   When set told to convert String to ASCII
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_append_string_ex(ajp_msg_t *msg, const char *value,
+                                      int convert)
+{
+    size_t len;
+
+    if (value == NULL) {
+        return(ajp_msg_append_uint16(msg, 0xFFFF));
+    }
+
+    len = strlen(value);
+    if ((msg->len + len + 2) > AJP_MSG_BUFFER_SZ) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+            "ajp_msg_append_cvt_string(): BufferOverflowException %d %d",
+            msg->pos, msg->len);
+        return AJP_EOVERFLOW;
+    }
+
+    /* ignore error - we checked once */
+    ajp_msg_append_uint16(msg, (apr_uint16_t)len);
+
+    /* We checked for space !!  */
+    memcpy(msg->buf + msg->len, value, len + 1); /* including \0 */
+
+    if (convert)   /* convert from EBCDIC if needed */
+        ajp_xlate_to_ascii((char *)msg->buf + msg->len, len + 1);
+
+    msg->len += len + 1;
+
+    return APR_SUCCESS;
+}
+
+/**
+ * Add a Byte array to AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param value     Pointer to Byte array
+ * @param valuelen  Byte array len
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_append_bytes(ajp_msg_t *msg, const apr_byte_t *value,
+                                  apr_size_t valuelen)
+{
+    if (! valuelen) {
+        return APR_SUCCESS; /* Shouldn't we indicate an error ? */
+    }
+
+    if ((msg->len + valuelen) > AJP_MSG_BUFFER_SZ) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_msg_append_bytes(): BufferOverflowException %d %d",
+                      msg->pos, msg->len);
+        return AJP_EOVERFLOW;
+    }
+
+    /* We checked for space !!  */
+    memcpy(msg->buf + msg->len, value, valuelen);
+    msg->len += valuelen;
+
+    return APR_SUCCESS;
+}
+
+/**
+ * Get a 32bits unsigned value from AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param rvalue    Pointer where value will be returned
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_get_uint32(ajp_msg_t *msg, apr_uint32_t *rvalue)
+{
+    apr_uint32_t value;
+
+    if ((msg->pos + 3) > msg->len) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_msg_get_long(): BufferOverflowException %d %d",
+                      msg->pos, msg->len);
+
+        return AJP_EOVERFLOW;
+    }
+
+    value  = ((msg->buf[(msg->pos++)] & 0xFF) << 24);
+    value |= ((msg->buf[(msg->pos++)] & 0xFF) << 16);
+    value |= ((msg->buf[(msg->pos++)] & 0xFF) << 8);
+    value |= ((msg->buf[(msg->pos++)] & 0xFF));
+    
+    *rvalue = value;
+    return APR_SUCCESS;
+}
+
+
+/**
+ * Get a 16bits unsigned value from AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param rvalue    Pointer where value will be returned
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_get_uint16(ajp_msg_t *msg, apr_uint16_t *rvalue)
+{
+    apr_uint16_t value;
+    
+    if ((msg->pos + 1) > msg->len) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_msg_get_int(): BufferOverflowException %d %d",
+                      msg->pos, msg->len);
+
+        return AJP_EOVERFLOW;
+    }
+
+    value  = ((msg->buf[(msg->pos++)] & 0xFF) << 8);
+    value += ((msg->buf[(msg->pos++)] & 0xFF));
+
+    *rvalue = value;
+    return APR_SUCCESS;
+}
+
+/**
+ * Peek a 16bits unsigned value from AJP Message, position in message
+ * is not updated
+ *
+ * @param msg       AJP Message to get value from
+ * @param rvalue    Pointer where value will be returned
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_peek_uint16(ajp_msg_t *msg, apr_uint16_t *rvalue)
+{
+    apr_uint16_t value;
+
+    if ((msg->pos + 1) > msg->len) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_msg_peek_int(): BufferOverflowException %d %d",
+                      msg->pos, msg->len);
+
+        return AJP_EOVERFLOW;
+    }
+    
+    value = ((msg->buf[(msg->pos)] & 0xFF) << 8);
+    value += ((msg->buf[(msg->pos + 1)] & 0xFF));
+    
+    *rvalue = value;
+    return APR_SUCCESS;
+}
+
+/**
+ * Peek a 8bits unsigned value from AJP Message, position in message
+ * is not updated
+ *
+ * @param msg       AJP Message to get value from
+ * @param rvalue    Pointer where value will be returned
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_peek_uint8(ajp_msg_t *msg, apr_byte_t *rvalue)
+{
+    if (msg->pos > msg->len) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_msg_peek_uint8(): BufferOverflowException %d %d",
+                      msg->pos, msg->len);
+
+        return AJP_EOVERFLOW;
+    }
+    
+    *rvalue = msg->buf[msg->pos];
+    return APR_SUCCESS;
+}
+
+/**
+ * Get a 8bits unsigned value from AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param rvalue    Pointer where value will be returned
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_get_uint8(ajp_msg_t *msg, apr_byte_t *rvalue)
+{
+
+    if (msg->pos > msg->len) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_msg_get_uint8(): BufferOverflowException %d %d",
+                      msg->pos, msg->len);
+
+        return AJP_EOVERFLOW;
+    }
+    
+    *rvalue = msg->buf[msg->pos++];
+    return APR_SUCCESS;
+}
+
+
+/**
+ * Get a String value from AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param rvalue    Pointer where value will be returned
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_get_string(ajp_msg_t *msg, char **rvalue)
+{
+    apr_uint16_t size;
+    apr_size_t   start;
+    apr_status_t status;
+       
+    status = ajp_msg_get_uint16(msg, &size);
+    start = msg->pos;
+
+    if ((status != APR_SUCCESS) || (size + start > AJP_MSG_BUFFER_SZ)) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_msg_get_string(): BufferOverflowException %d %d",
+                      msg->pos, msg->len);
+
+        return AJP_EOVERFLOW;
+    }
+
+    msg->pos += (apr_size_t)size;
+    msg->pos++;                   /* a String in AJP is NULL terminated */
+
+    *rvalue = (char *)(msg->buf + start);
+    return APR_SUCCESS;
+}
+
+
+/**
+ * Get a Byte array from AJP Message
+ *
+ * @param msg       AJP Message to get value from
+ * @param rvalue    Pointer where value will be returned
+ * @param rvalueLen Pointer where Byte array len will be returned
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_get_bytes(ajp_msg_t *msg, apr_byte_t **rvalue,
+                               apr_size_t *rvalue_len)
+{
+    apr_uint16_t size;
+    apr_size_t   start;
+    apr_status_t status;
+
+    status = ajp_msg_get_uint16(msg, &size);
+    /* save the current position */
+    start = msg->pos;
+
+    if ((status != APR_SUCCESS) || (size + start > AJP_MSG_BUFFER_SZ)) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_msg_get_bytes(): BufferOverflowException %d %d",
+                      msg->pos, msg->len);
+        return AJP_EOVERFLOW;
+    }
+    msg->pos += (apr_size_t)size;   /* only bytes, no trailer */
+
+    *rvalue     = msg->buf + start;
+    *rvalue_len = size;
+
+    return APR_SUCCESS;
+}
+
+
+/**
+ * Create an AJP Message from pool
+ *
+ * @param pool      memory pool to allocate AJP message from
+ * @param rmsg      Pointer to newly created AJP message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_create(apr_pool_t *pool, ajp_msg_t **rmsg)
+{
+    ajp_msg_t *msg = (ajp_msg_t *)apr_pcalloc(pool, sizeof(ajp_msg_t));
+
+    if (!msg) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_msg_create(): can't allocate AJP message memory");
+        return APR_ENOPOOL;
+    }
+    
+    msg->server_side = 0;
+
+    msg->buf = (apr_byte_t *)apr_palloc(pool, AJP_MSG_BUFFER_SZ);
+    
+    /* XXX: This should never happen
+     * In case if the OS cannont allocate 8K of data
+     * we are in serious trouble
+     * No need to check the alloc return value, cause the
+     * core dump is probably the best solution anyhow.
+     */
+    if (msg->buf == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_msg_create(): can't allocate AJP message memory");
+        return APR_ENOPOOL;
+    }
+
+    msg->len = 0;
+    msg->header_len = AJP_HEADER_LEN;
+    *rmsg = msg;
+    
+    return APR_SUCCESS;
+}
+
+/**
+ * Recopy an AJP Message to another
+ *
+ * @param smsg      source AJP message
+ * @param dmsg      destination AJP message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_copy(ajp_msg_t *smsg, ajp_msg_t *dmsg)
+{
+    if (dmsg == NULL) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                      "ajp_msg_copy(): destination msg is null");
+        return AJP_EINVAL;
+    }
+    
+    if (smsg->len > AJP_MSG_BUFFER_SZ) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+            "ajp_msg_copy(): destination buffer too small %d, max size is %d",
+            smsg->len, AJP_MSG_BUFFER_SZ);
+        return  AJP_ETOSMALL;
+    }
+
+    memcpy(dmsg->buf, smsg->buf, smsg->len);
+    dmsg->len = smsg->len;
+    dmsg->pos = smsg->pos;
+
+    return APR_SUCCESS;
+}
+
+
+/**
+ * Serialize in an AJP Message a PING command
+ *
+ * +-----------------------+
+ * | PING CMD (1 byte)     |
+ * +-----------------------+
+ *
+ * @param smsg      AJP message to put serialized message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_serialize_ping(ajp_msg_t *msg)
+{
+    apr_status_t rc;
+    ajp_msg_reset(msg);
+
+    if ((rc = ajp_msg_append_uint8(msg, CMD_AJP13_PING)) != APR_SUCCESS)
+        return rc;
+        
+    return APR_SUCCESS;
+}
+
+/** 
+ * Serialize in an AJP Message a CPING command
+ *
+ * +-----------------------+
+ * | CPING CMD (1 byte)    |
+ * +-----------------------+
+ *
+ * @param smsg      AJP message to put serialized message
+ * @return          APR_SUCCESS or error
+ */
+apr_status_t ajp_msg_serialize_cping(ajp_msg_t *msg)
+{
+    apr_status_t rc;
+    ajp_msg_reset(msg);
+
+    if ((rc = ajp_msg_append_uint8(msg, CMD_AJP13_CPING)) != APR_SUCCESS)
+        return rc;
+        
+    return APR_SUCCESS;
+}
diff --git a/connectors/ajp/ajplib/test/Makefile b/connectors/ajp/ajplib/test/Makefile
new file mode 100644
index 0000000..77e291c
--- /dev/null
+++ b/connectors/ajp/ajplib/test/Makefile
@@ -0,0 +1,40 @@
+#
+# You need an installed httpd-2.x to use this Makefile
+#
+
+APACHE_DIR=/home2/apache20/apache20
+APR_DIR=$(APACHE_DIR)
+APU_DIR=$(APACHE_DIR)
+
+include $(APR_DIR)/build/config_vars.mk
+
+# in httpd-2.x sources...
+#APR_DIR=$(APACHE_DIR)/srclib/apr
+#APU_DIR=$(APACHE_DIR)/srclib/apr-util
+
+APR_INCLUDE=$(APR_DIR)/include
+APU_INCLUDE=$(APU_DIR)/include
+APA_INCLUDE=$(APACHE_DIR)/include
+
+SRC_DIR=.
+INCLUDE=.
+AJP_OBJECTS = ajp_link.lo  ajp_msg.lo  httpd_wrap.lo ajp_header.lo
+AJP_PROGRAM = testajp.lo
+AJP_LIB     = lib_ajp.la
+
+INCLUDES = -I $(INCLUDE) -I $(APR_INCLUDE) -I $(APU_INCLUDE) -I $(APA_INCLUDE)
+
+include $(APR_DIR)/build/apr_rules.mk
+
+all: testajp
+
+testajp: $(AJP_PROGRAM) $(AJP_LIB)
+	$(LINK) $(AJP_PROGRAM) $(AJP_LIB) $(AP_LIBS)
+
+$(AJP_LIB): $(AJP_OBJECTS)
+	$(LINK) $(AJP_OBJECTS)
+
+clean:
+	rm -f *.o *.so *.lo *.la *.slo
+	rm -rf .libs
+	rm -f testajp
diff --git a/connectors/ajp/ajplib/test/Makefile.netware b/connectors/ajp/ajplib/test/Makefile.netware
new file mode 100644
index 0000000..76a9116
--- /dev/null
+++ b/connectors/ajp/ajplib/test/Makefile.netware
@@ -0,0 +1,328 @@
+#
+# Makefile for ajplib (NetWare version - gnu make)
+# created by Guenter Knauf <eflash@gmx.net>
+#
+
+# Edit the path below to point to the base of your Apache 2.x includes.
+ifndef AP_HOME
+AP_HOME	= c:/projects/gccnlm/apache_2.0.50-sdk
+endif
+# Edit the path below to point to the base of your NetWare Java SDK.
+ifndef NW_JDK
+NW_JDK	= c:/projects/sdks/java-nw
+endif
+# Edit the path below to point to the base of your Novell NDK.
+ifndef NDKBASE
+NDKBASE	= c:/novell
+endif
+
+ifndef INSTDIR
+INSTDIR	= ../dist-$(AJP_VERSION_STR)-bin-nw
+endif
+
+# Edit the vars below to change NLM target settings.
+TARGETS = testajp.nlm
+LTARGET = libajp.lib
+VERSION	= $(AJP_VERSION)
+COPYR	= Copyright (c) 2000-2004 The Apache Software Foundation. All rights reserved.
+DESCR	= AJP lib for Jakarta/Tomcat $(AJP_VERSION_STR)
+MTSAFE	= YES
+STACK	= 64000
+#SCREEN	= none
+MODULES	= aprlib
+#EXPORTS	=
+# Comment the line below if you dont want to load protected automatically.
+LDRING	= 3
+
+# Edit the var below to point to your lib architecture.
+ifndef LIBARCH
+LIBARCH = LIBC
+endif
+NW_WINSOCK = 1
+
+# must be equal to DEBUG or NDEBUG
+DB	= NDEBUG
+# DB	= DEBUG
+# DB	= CURLDEBUG
+# Optimization: -O<n> or debugging: -g
+ifeq ($(DB),NDEBUG)
+	OPT	= -O2
+	OBJDIR	= release
+else
+	OPT	= -g
+	OBJDIR	= debug
+endif
+
+# Include the version info retrieved from ajp.h
+#-include $(OBJDIR)/version.inc
+AJP_VERSION = 1,0,0
+AJP_VERSION_STR = 1.0.0-dev-beta
+
+# Check if we are on Linux in order to set the echo delimiter if needed.
+ifeq ($(findstring linux,$(OSTYPE)),linux)
+DL	= '
+#-include $(NDKBASE)/nlmconv/ncpfs.inc
+endif
+
+# The following line defines your compiler.
+ifdef METROWERKS
+	CC = mwccnlm
+else
+	CC = gcc
+endif
+YACC	= bison -y
+CP	= cp -afv
+# RM	= rm -f
+# if you want to mark the target as MTSAFE you will need a tool for
+# generating the xdc data for the linker; here's a minimal tool:
+# http://www.gknw.com/development/prgtools/mkxdc.zip
+MPKXDC	= mkxdc
+
+# Global flags for all compilers
+CFLAGS	= $(OPT) -D$(DB) -DNETWARE -DHAVE_CONFIG_H -nostdinc
+CFLAGS	+= -DAJP_USE_HTTPD_WRAP
+
+ifeq ($(CC),mwccnlm)
+LD	= mwldnlm
+LDFLAGS	= -nostdlib $(PRELUDE) $(OBJS) $(OBJWRAP) $(<:.def=.o) -o $@ -commandfile
+AR	= mwldnlm
+ARFLAGS	= -type library -w nocmdline $(OBJDIR)/*.o -o
+CFLAGS	+= -msgstyle gcc -gccinc -inline off -opt nointrinsics -proc 586
+CFLAGS	+= -relax_pointers
+CFLAGS	+= -w on,nounused
+ifeq ($(LIBARCH),LIBC)
+	PRELUDE = $(SDK_LIBC)/imports/libcpre.o
+	CFLAGS += -align 4
+else
+	PRELUDE = "$(METROWERKS)/Novell Support/libraries/runtime/prelude.obj"
+#	CFLAGS += -include "$(METROWERKS)/Novell Support/headers/nlm_prefix.h"
+	CFLAGS += -align 1
+endif
+else
+LD	= nlmconv
+LDFLAGS	= -T
+AR	= ar
+ARFLAGS	= -cq
+CFLAGS	+= -fno-builtin -fpack-struct -fpcc-struct-return
+CFLAGS	+= -Wall -Wno-format # -pedantic
+ifeq ($(LIBARCH),LIBC)
+	PRELUDE = $(SDK_LIBC)/imports/libcpre.gcc.o
+else
+	PRELUDE = $(SDK_CLIB)/imports/clibpre.gcc.o
+	CFLAGS += -include $(NDKBASE)/nlmconv/genlm.h
+endif
+endif
+
+NDK_ROOT = $(NDKBASE)/ndk
+SDK_CLIB = $(NDK_ROOT)/nwsdk
+SDK_LIBC = $(NDK_ROOT)/libc
+
+INCLUDES += -I$(AP_HOME)/include
+
+ifeq ($(LIBARCH),LIBC)
+	INCLUDES += -I$(SDK_LIBC)/include -I$(SDK_LIBC)/include/nks
+	INCLUDES += -I$(SDK_LIBC)/include/winsock
+	CFLAGS += -D_POSIX_SOURCE
+#	CFLAGS += -D__ANSIC__
+else
+	INCLUDES += -I$(SDK_CLIB)/include/nlm -I$(SDK_CLIB)/include
+	# INCLUDES += -I$(SDK_CLIB)/include/nlm/obsolete
+	CFLAGS += -DNETDB_USE_INTERNET
+endif
+CFLAGS	+= -I. $(INCLUDES)
+
+ifeq ($(MTSAFE),YES)
+	XDCOPT = -n
+endif
+ifeq ($(MTSAFE),NO)
+	XDCOPT = -u
+endif
+
+OBJS	= \
+	$(OBJDIR)/ajp_header.o \
+	$(OBJDIR)/ajp_link.o \
+	$(OBJDIR)/ajp_msg.o
+
+OBJWRAP	= $(OBJDIR)/httpd_wrap.o
+
+.PHONY: lib nlm prebuild dist install clean
+
+nlm: prebuild $(TARGETS) 
+
+lib: prebuild $(LTARGET)
+
+prebuild: $(OBJDIR) $(OBJDIR)/version.inc config.h
+
+dist: all
+	-$(RM) $(OBJS) $(OBJDIR)/*.map $(OBJDIR)/*.ncv
+	-$(RM) $(OBJDIR)/*.def $(OBJDIR)/*.xdc $(OBJDIR)/version.inc
+
+install: $(INSTDIR) all
+	@$(CP) *.nlm $(INSTDIR)
+	@$(CP) ../CHANGES $(INSTDIR)
+	@$(CP) ../COPYING $(INSTDIR)
+	@$(CP) ../README $(INSTDIR)
+	@$(CP) ../RELEASE-NOTES $(INSTDIR)
+
+clean:
+	-$(RM) $(LTARGET) $(TARGETS) $(OBJWRAP) config.h
+	-$(RM) -r $(OBJDIR)
+
+%.lib: $(OBJS)
+	@echo Creating $@
+	@-$(RM) $@
+	@$(AR) $(ARFLAGS) $@ $^
+
+%.nlm: $(OBJDIR)/%.def $(OBJDIR)/%.o $(OBJDIR)/%.xdc $(OBJS) $(OBJWRAP) 
+	@echo Linking $@
+	@-$(RM) $@
+	@$(LD) $(LDFLAGS) $<
+
+$(INSTDIR):
+	@mkdir $(INSTDIR)
+
+$(OBJDIR):
+	@mkdir $(OBJDIR)
+
+$(OBJDIR)/%.o: %.c
+#	@echo Compiling $<
+	$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJDIR)/version.inc: ajp.h $(OBJDIR)
+#	@echo Creating $@
+#	@awk -f ../packages/NetWare/get_ver.awk $< > $@
+
+$(OBJDIR)/%.xdc: Makefile.netware
+	@echo Creating $@
+	@$(MPKXDC) $(XDCOPT) $@
+
+$(OBJDIR)/%.def: Makefile.netware
+	@echo Creating $@
+	@echo $(DL)# DEF file for linking with $(LD)$(DL) > $@
+	@echo $(DL)# Do not edit this file - it is created by make!$(DL) >> $@
+	@echo $(DL)# All your changes will be lost!!$(DL) >> $@
+	@echo $(DL)#$(DL) >> $@
+	@echo $(DL)copyright "$(COPYR)"$(DL) >> $@
+	@echo $(DL)description "$(DESCR)"$(DL) >> $@
+	@echo $(DL)version $(VERSION)$(DL) >> $@
+ifdef NLMTYPE
+	@echo $(DL)type $(NLMTYPE)$(DL) >> $@
+endif
+ifdef STACK
+	@echo $(DL)stack $(STACK)$(DL) >> $@
+endif
+ifdef SCREEN
+	@echo $(DL)screenname "$(SCREEN)"$(DL) >> $@
+else
+	@echo $(DL)screenname "DEFAULT"$(DL) >> $@
+endif
+ifeq ($(DB),DEBUG)
+	@echo $(DL)debug$(DL) >> $@
+endif
+	@echo $(DL)threadname "$^"$(DL) >> $@
+ifdef XDCOPT
+	@echo $(DL)xdcdata $(@:.def=.xdc)$(DL) >> $@
+endif
+ifeq ($(LDRING),0)
+	@echo $(DL)flag_on 16$(DL) >> $@
+endif
+ifeq ($(LDRING),3)
+	@echo $(DL)flag_on 512$(DL) >> $@
+endif
+ifeq ($(LIBARCH),CLIB)
+	@echo $(DL)start _Prelude$(DL) >> $@
+	@echo $(DL)exit _Stop$(DL) >> $@
+	@echo $(DL)import @$(SDK_CLIB)/imports/clib.imp$(DL) >> $@
+	@echo $(DL)import @$(SDK_CLIB)/imports/threads.imp$(DL) >> $@
+	@echo $(DL)import @$(SDK_CLIB)/imports/nlmlib.imp$(DL) >> $@
+	@echo $(DL)import @$(SDK_CLIB)/imports/socklib.imp$(DL) >> $@
+	@echo $(DL)module clib$(DL) >> $@
+else
+	@echo $(DL)flag_on 64$(DL) >> $@
+	@echo $(DL)pseudopreemption$(DL) >> $@
+	@echo $(DL)start _LibCPrelude$(DL) >> $@
+	@echo $(DL)exit _LibCPostlude$(DL) >> $@
+	@echo $(DL)check _LibCCheckUnload$(DL) >> $@
+	@echo $(DL)import @$(SDK_LIBC)/imports/libc.imp$(DL) >> $@
+	@echo $(DL)import @$(SDK_LIBC)/imports/netware.imp$(DL) >> $@
+	@echo $(DL)module libc$(DL) >> $@
+endif
+	@echo $(DL)import @$(AP_HOME)/lib/aprlib.imp$(DL) >> $@
+ifdef MODULES
+	@echo $(DL)module $(MODULES)$(DL) >> $@
+endif
+ifdef EXPORTS
+	@echo $(DL)export $(EXPORTS)$(DL) >> $@
+endif
+ifdef IMPORTS
+	@echo $(DL)import $(IMPORTS)$(DL) >> $@
+endif
+ifeq ($(LD),nlmconv)
+	@echo $(DL)input $(OBJS)$(DL) >> $@
+	@echo $(DL)input $(OBJWRAP)$(DL) >> $@
+	@echo $(DL)input $(PRELUDE)$(DL) >> $@
+	@echo $(DL)output $(notdir $(@:.def=.nlm))$(DL) >> $@
+endif
+
+config.h: Makefile.netware
+	@echo Creating $@
+	@echo $(DL)/* $@ for NetWare target.$(DL) > $@
+	@echo $(DL)** Do not edit this file - it is created by make!$(DL) >> $@
+	@echo $(DL)** All your changes will be lost!!$(DL) >> $@
+	@echo $(DL)*/$(DL) >> $@
+	@echo $(DL)#define OS "i586-pc-NetWare"$(DL) >> $@
+	@echo $(DL)#define VERSION "$(AJP_VERSION_STR)"$(DL) >> $@
+	@echo $(DL)#define PACKAGE_BUGREPORT "ajp-bug@apache.org"$(DL) >> $@
+	@echo $(DL)#define HAVE_ARPA_INET_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_ASSERT_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_DLFCN_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_DLOPEN 1$(DL) >> $@
+	@echo $(DL)#define HAVE_ERR_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_FCNTL_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_FIONBIO 1$(DL) >> $@
+	@echo $(DL)#define HAVE_GETHOSTBYADDR 1$(DL) >> $@
+	@echo $(DL)#define HAVE_GETTIMEOFDAY 1$(DL) >> $@
+	@echo $(DL)#define HAVE_INET_ADDR 1$(DL) >> $@
+	@echo $(DL)#define HAVE_INET_NTOA 1$(DL) >> $@
+	@echo $(DL)#define HAVE_INET_PTON 1$(DL) >> $@
+	@echo $(DL)#define HAVE_INTTYPES_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_LIMITS_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_LONGLONG 1$(DL) >> $@
+	@echo $(DL)#define HAVE_MALLOC_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_NETINET_IN_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_SELECT 1$(DL) >> $@
+	@echo $(DL)#define HAVE_SETJMP_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_SIGNAL 1$(DL) >> $@
+	@echo $(DL)#define HAVE_SOCKET 1$(DL) >> $@
+	@echo $(DL)#define HAVE_STDINT_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_STDLIB_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_STRCASECMP 1$(DL) >> $@
+	@echo $(DL)#define HAVE_STRDUP 1$(DL) >> $@
+	@echo $(DL)#define HAVE_STRFTIME 1$(DL) >> $@
+	@echo $(DL)#define HAVE_STRING_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_STRLCAT 1$(DL) >> $@
+	@echo $(DL)#define HAVE_STRLCPY 1$(DL) >> $@
+	@echo $(DL)#define HAVE_STRSTR 1$(DL) >> $@
+	@echo $(DL)#define HAVE_SYS_PARAM_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_SYS_SELECT_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_SYS_STAT_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_SYS_TIME_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_TERMIOS_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_TIME_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_UNAME 1$(DL) >> $@
+	@echo $(DL)#define HAVE_UNISTD_H 1$(DL) >> $@
+	@echo $(DL)#define STDC_HEADERS 1$(DL) >> $@
+	@echo $(DL)#define TIME_WITH_SYS_TIME 1$(DL) >> $@
+ifdef NW_WINSOCK
+	@echo $(DL)#define HAVE_CLOSESOCKET 1$(DL) >> $@
+else
+	@echo $(DL)#define HAVE_SYS_TYPES_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_SYS_SOCKET_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_SYS_SOCKIO_H 1$(DL) >> $@
+	@echo $(DL)#define HAVE_NETDB_H 1$(DL) >> $@
+endif
+ifdef OLD_NOVELLSDK
+	@echo $(DL)#define socklen_t int$(DL) >> $@
+endif
+
+
diff --git a/connectors/ajp/ajplib/test/httpd_wrap.c b/connectors/ajp/ajplib/test/httpd_wrap.c
new file mode 100644
index 0000000..a975e55
--- /dev/null
+++ b/connectors/ajp/ajplib/test/httpd_wrap.c
@@ -0,0 +1,671 @@
+/* Copyright 2000-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "apr_buckets.h"
+#include "apr_md5.h"
+#include "apr_network_io.h"
+#include "apr_pools.h"
+#include "apr_strings.h"
+#include "apr_uri.h"
+#include "apr_date.h"
+#include "apr_fnmatch.h"
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+ 
+#include "apr_hooks.h"
+#include "apr_optional_hooks.h"
+#include "apr_buckets.h"
+
+#include "httpd_wrap.h"
+
+int AP_DECLARE_DATA ap_default_loglevel = DEFAULT_LOGLEVEL;
+
+static const char *levels[] = {
+    "emerg",
+    "alert",
+    "crit",
+    "error",
+    "warn",
+    "notice",
+    "info",
+    "debug",
+    NULL
+};
+
+static void log_error_core(const char *file, int line, int level,
+                           apr_status_t status,
+                           const char *fmt, va_list args)
+{
+    FILE *stream;
+    char timstr[32];
+    char errstr[MAX_STRING_LEN];
+    
+    /* Skip the loging for lower levels */
+    if (level < 0 || level > ap_default_loglevel)
+        return;
+    if (level < APLOG_WARNING)
+        stream = stderr;
+    else
+        stream = stdout;
+    apr_ctime(&timstr[0], apr_time_now());
+    fprintf(stream, "[%s] [%s] ", timstr, levels[level]);
+    if (file && level == APLOG_DEBUG) {
+#ifndef WIN32
+        char *e = strrchr(file, '/');
+#else
+        char *e = strrchr(file, '\\');
+#endif
+        if (e)
+            fprintf(stream, "%s (%d) ", e + 1, line);
+    }
+
+    if (status != 0) {
+        if (status < APR_OS_START_EAIERR) {
+            fprintf(stream, "(%d)", status);
+        }
+        else if (status < APR_OS_START_SYSERR) {
+            fprintf(stream, "(EAI %d)", status - APR_OS_START_EAIERR);
+        }
+        else if (status < 100000 + APR_OS_START_SYSERR) {
+            fprintf(stream, "(OS %d)", status - APR_OS_START_SYSERR);
+        }
+        else {
+            fprintf(stream, "(os 0x%08x)", status - APR_OS_START_SYSERR);
+        }
+        apr_strerror(status, errstr, MAX_STRING_LEN);
+        fprintf(stream, " %s ", errstr);
+    }
+
+    apr_vsnprintf(errstr, MAX_STRING_LEN, fmt, args);
+    fputs(errstr, stream);
+    fputs("\n", stream);    
+    if (level < APLOG_WARNING)
+        fflush(stream);
+
+}
+
+AP_DECLARE(void) ap_log_error(const char *file, int line, int level,
+                              apr_status_t status, const server_rec *s,
+                              const char *fmt, ...)
+{
+    va_list args;
+
+    va_start(args, fmt);
+    log_error_core(file, line, level, status, fmt, args);
+    va_end(args);
+}
+
+AP_DECLARE(void) ap_log_perror(const char *file, int line, int level,
+                               apr_status_t status, apr_pool_t *p,
+                               const char *fmt, ...)
+{
+    va_list args;
+
+    va_start(args, fmt);
+    log_error_core(file, line, level, status, fmt, args);
+    va_end(args);
+}
+ 
+
+AP_DECLARE(void) ap_log_rerror(const char *file, int line, int level,
+                               apr_status_t status, const request_rec *r,
+                               const char *fmt, ...)
+{
+    va_list args;
+
+    va_start(args, fmt);
+    log_error_core(file, line, level, status, fmt, args);
+    va_end(args);
+}
+
+AP_DECLARE(request_rec *) ap_wrap_create_request(conn_rec *conn)
+{
+    request_rec *r;
+    apr_pool_t *p;
+
+    apr_pool_create(&p, conn->pool);
+    apr_pool_tag(p, "request");
+    r = apr_pcalloc(p, sizeof(request_rec));
+    r->pool            = p;
+    r->connection      = conn;
+    r->server          = conn->base_server;
+
+    r->user            = NULL;
+    r->ap_auth_type    = NULL;
+
+    r->headers_in      = apr_table_make(r->pool, 25);
+    r->subprocess_env  = apr_table_make(r->pool, 25);
+    r->headers_out     = apr_table_make(r->pool, 12);
+    r->err_headers_out = apr_table_make(r->pool, 5);
+    r->notes           = apr_table_make(r->pool, 5);
+
+
+    r->status          = HTTP_REQUEST_TIME_OUT;  /* Until we get a request */
+    r->the_request     = NULL;
+
+    r->status = HTTP_OK;                         /* Until further notice. */
+    return r;
+}
+
+AP_DECLARE(process_rec *) ap_wrap_create_process(int argc, const char * const *argv)
+{
+    process_rec *process;
+    apr_pool_t *cntx;
+    apr_status_t stat;
+
+    stat = apr_pool_create(&cntx, NULL);
+    if (stat != APR_SUCCESS) {
+        /* XXX From the time that we took away the NULL pool->malloc mapping
+         *     we have been unable to log here without segfaulting.
+         */
+        ap_log_error(APLOG_MARK, APLOG_ERR, stat, NULL,
+                     "apr_pool_create() failed to create "
+                     "initial context");
+        apr_terminate();
+        exit(1);
+    }
+
+    apr_pool_tag(cntx, "process");
+
+    process = apr_palloc(cntx, sizeof(process_rec));
+    process->pool = cntx;
+
+    apr_pool_create(&process->pconf, process->pool);
+    apr_pool_tag(process->pconf, "pconf");
+    process->argc = argc;
+    process->argv = argv;
+    process->short_name = apr_filepath_name_get(argv[0]);
+    return process;
+}
+
+AP_DECLARE(server_rec *) ap_wrap_create_server(process_rec *process, apr_pool_t *p)
+{
+    apr_status_t rv;
+    server_rec *s = (server_rec *) apr_pcalloc(p, sizeof(server_rec));
+
+    s->process = process;
+    s->port = 0;
+    s->server_admin = DEFAULT_ADMIN;
+    s->server_hostname = NULL;
+    s->loglevel = DEFAULT_LOGLEVEL;
+    s->limit_req_line = DEFAULT_LIMIT_REQUEST_LINE;
+    s->limit_req_fieldsize = DEFAULT_LIMIT_REQUEST_FIELDSIZE;
+    s->limit_req_fields = DEFAULT_LIMIT_REQUEST_FIELDS;
+    s->timeout = apr_time_from_sec(DEFAULT_TIMEOUT);
+    s->keep_alive_timeout = apr_time_from_sec(DEFAULT_KEEPALIVE_TIMEOUT);
+    s->keep_alive_max = DEFAULT_KEEPALIVE;
+    s->keep_alive = 1;
+    s->addrs = apr_pcalloc(p, sizeof(server_addr_rec));
+
+    /* NOT virtual host; don't match any real network interface */
+    rv = apr_sockaddr_info_get(&s->addrs->host_addr,
+                               NULL, APR_INET, 0, 0, p);
+
+    s->addrs->host_port = 0; /* matches any port */
+    s->addrs->virthost = ""; /* must be non-NULL */
+
+    return s;
+} 
+
+AP_DECLARE(conn_rec *) ap_run_create_connection(apr_pool_t *ptrans,
+                                  server_rec *server,
+                                  apr_socket_t *csd, long id, void *sbh,
+                                  apr_bucket_alloc_t *alloc)
+{
+    apr_status_t rv;
+    conn_rec *c = (conn_rec *) apr_pcalloc(ptrans, sizeof(conn_rec));
+
+    c->sbh = sbh;
+
+    c->conn_config = ap_create_conn_config(ptrans);
+    /* Got a connection structure, so initialize what fields we can
+     * (the rest are zeroed out by pcalloc).
+     */
+    c->notes = apr_table_make(ptrans, 5);
+
+    c->pool = ptrans;
+
+    /* Socket is used only for backend connections
+     * Since we don't have client socket skip the 
+     * creation of adresses. They will be default
+     * to 127.0.0.1:0 both local and remote
+     */
+    if (csd) {
+        if ((rv = apr_socket_addr_get(&c->local_addr, APR_LOCAL, csd))
+            != APR_SUCCESS) {
+                ap_log_error(APLOG_MARK, APLOG_INFO, rv, server,
+                    "apr_socket_addr_get(APR_LOCAL)");
+                apr_socket_close(csd);
+                return NULL;
+         }
+         if ((rv = apr_socket_addr_get(&c->remote_addr, APR_REMOTE, csd))
+                != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_INFO, rv, server,
+                    "apr_socket_addr_get(APR_REMOTE)");
+                apr_socket_close(csd);
+            return NULL;
+         }
+    } 
+    else {
+        /* localhost should be reachable on all platforms */
+        if ((rv = apr_sockaddr_info_get(&c->local_addr, "localhost",
+                                        APR_UNSPEC, 0,
+                                        APR_IPV4_ADDR_OK, 
+                                        c->pool))
+            != APR_SUCCESS) {
+                ap_log_error(APLOG_MARK, APLOG_INFO, rv, server,
+                    "apr_sockaddr_info_get()");
+                return NULL;
+         }
+         c->remote_addr = c->local_addr;        
+    }
+    apr_sockaddr_ip_get(&c->local_ip, c->local_addr);
+    apr_sockaddr_ip_get(&c->remote_ip, c->remote_addr);
+    c->base_server = server;
+
+    c->id = id;
+    c->bucket_alloc = alloc;
+
+    return c;
+} 
+
+AP_DECLARE(const char *) ap_get_remote_host(conn_rec *conn, void *dir_config,
+                                            int type, int *str_is_ip)
+{
+    int ignored_str_is_ip;
+
+    if (!str_is_ip) { /* caller doesn't want to know */
+        str_is_ip = &ignored_str_is_ip;
+    }
+    *str_is_ip = 0;
+
+    /*
+     * Return the desired information; either the remote DNS name, if found,
+     * or either NULL (if the hostname was requested) or the IP address
+     * (if any identifier was requested).
+     */
+    if (conn->remote_host != NULL && conn->remote_host[0] != '\0') {
+        return conn->remote_host;
+    }
+    else {
+        if (type == REMOTE_HOST || type == REMOTE_DOUBLE_REV) {
+            return NULL;
+        }
+        else {
+            *str_is_ip = 1;
+            return conn->remote_ip;
+        }
+    }
+} 
+
+AP_DECLARE(const char *) ap_get_server_name(request_rec *r)
+{
+    /* default */
+    return r->server->server_hostname;
+} 
+
+AP_DECLARE(void) ap_set_content_type(request_rec *r, const char *ct)
+{
+    r->content_type = ct;
+}
+
+
+#define UNKNOWN_METHOD (-1)
+
+static int lookup_builtin_method(const char *method, apr_size_t len)
+{
+    /* Note: the following code was generated by the "shilka" tool from
+       the "cocom" parsing/compilation toolkit. It is an optimized lookup
+       based on analysis of the input keywords. Postprocessing was done
+       on the shilka output, but the basic structure and analysis is
+       from there. Should new HTTP methods be added, then manual insertion
+       into this code is fine, or simply re-running the shilka tool on
+       the appropriate input. */
+
+    /* Note: it is also quite reasonable to just use our method_registry,
+       but I'm assuming (probably incorrectly) we want more speed here
+       (based on the optimizations the previous code was doing). */
+
+    switch (len)
+    {
+    case 3:
+        switch (method[0])
+        {
+        case 'P':
+            return (method[1] == 'U'
+                    && method[2] == 'T'
+                    ? M_PUT : UNKNOWN_METHOD);
+        case 'G':
+            return (method[1] == 'E'
+                    && method[2] == 'T'
+                    ? M_GET : UNKNOWN_METHOD);
+        default:
+            return UNKNOWN_METHOD;
+        }
+
+    case 4:
+        switch (method[0])
+        {
+        case 'H':
+            return (method[1] == 'E'
+                    && method[2] == 'A'
+                    && method[3] == 'D'
+                    ? M_GET : UNKNOWN_METHOD);
+        case 'P':
+            return (method[1] == 'O'
+                    && method[2] == 'S'
+                    && method[3] == 'T'
+                    ? M_POST : UNKNOWN_METHOD);
+        case 'M':
+            return (method[1] == 'O'
+                    && method[2] == 'V'
+                    && method[3] == 'E'
+                    ? M_MOVE : UNKNOWN_METHOD);
+        case 'L':
+            return (method[1] == 'O'
+                    && method[2] == 'C'
+                    && method[3] == 'K'
+                    ? M_LOCK : UNKNOWN_METHOD);
+        case 'C':
+            return (method[1] == 'O'
+                    && method[2] == 'P'
+                    && method[3] == 'Y'
+                    ? M_COPY : UNKNOWN_METHOD);
+        default:
+            return UNKNOWN_METHOD;
+        }
+
+    case 5:
+        switch (method[2])
+        {
+        case 'T':
+            return (memcmp(method, "PATCH", 5) == 0
+                    ? M_PATCH : UNKNOWN_METHOD);
+        case 'R':
+            return (memcmp(method, "MERGE", 5) == 0
+                    ? M_MERGE : UNKNOWN_METHOD);
+        case 'C':
+            return (memcmp(method, "MKCOL", 5) == 0
+                    ? M_MKCOL : UNKNOWN_METHOD);
+        case 'B':
+            return (memcmp(method, "LABEL", 5) == 0
+                    ? M_LABEL : UNKNOWN_METHOD);
+        case 'A':
+            return (memcmp(method, "TRACE", 5) == 0
+                    ? M_TRACE : UNKNOWN_METHOD);
+        default:
+            return UNKNOWN_METHOD;
+        }
+
+    case 6:
+        switch (method[0])
+        {
+        case 'U':
+            switch (method[5])
+            {
+            case 'K':
+                return (memcmp(method, "UNLOCK", 6) == 0
+                        ? M_UNLOCK : UNKNOWN_METHOD);
+            case 'E':
+                return (memcmp(method, "UPDATE", 6) == 0
+                        ? M_UPDATE : UNKNOWN_METHOD);
+            default:
+                return UNKNOWN_METHOD;
+            }
+        case 'R':
+            return (memcmp(method, "REPORT", 6) == 0
+                    ? M_REPORT : UNKNOWN_METHOD);
+        case 'D':
+            return (memcmp(method, "DELETE", 6) == 0
+                    ? M_DELETE : UNKNOWN_METHOD);
+        default:
+            return UNKNOWN_METHOD;
+        }
+
+    case 7:
+        switch (method[1])
+        {
+        case 'P':
+            return (memcmp(method, "OPTIONS", 7) == 0
+                    ? M_OPTIONS : UNKNOWN_METHOD);
+        case 'O':
+            return (memcmp(method, "CONNECT", 7) == 0
+                    ? M_CONNECT : UNKNOWN_METHOD);
+        case 'H':
+            return (memcmp(method, "CHECKIN", 7) == 0
+                    ? M_CHECKIN : UNKNOWN_METHOD);
+        default:
+            return UNKNOWN_METHOD;
+        }
+
+    case 8:
+        switch (method[0])
+        {
+        case 'P':
+            return (memcmp(method, "PROPFIND", 8) == 0
+                    ? M_PROPFIND : UNKNOWN_METHOD);
+        case 'C':
+            return (memcmp(method, "CHECKOUT", 8) == 0
+                    ? M_CHECKOUT : UNKNOWN_METHOD);
+        default:
+            return UNKNOWN_METHOD;
+        }
+
+    case 9:
+        return (memcmp(method, "PROPPATCH", 9) == 0
+                ? M_PROPPATCH : UNKNOWN_METHOD);
+
+    case 10:
+        switch (method[0])
+        {
+        case 'U':
+            return (memcmp(method, "UNCHECKOUT", 10) == 0
+                    ? M_UNCHECKOUT : UNKNOWN_METHOD);
+        case 'M':
+            return (memcmp(method, "MKACTIVITY", 10) == 0
+                    ? M_MKACTIVITY : UNKNOWN_METHOD);
+        default:
+            return UNKNOWN_METHOD;
+        }
+
+    case 11:
+        return (memcmp(method, "MKWORKSPACE", 11) == 0
+                ? M_MKWORKSPACE : UNKNOWN_METHOD);
+
+    case 15:
+        return (memcmp(method, "VERSION-CONTROL", 15) == 0
+                ? M_VERSION_CONTROL : UNKNOWN_METHOD);
+
+    case 16:
+        return (memcmp(method, "BASELINE-CONTROL", 16) == 0
+                ? M_BASELINE_CONTROL : UNKNOWN_METHOD);
+
+    default:
+        return UNKNOWN_METHOD;
+    }
+
+    /* NOTREACHED */
+}
+
+/* Get the method number associated with the given string, assumed to
+ * contain an HTTP method.  Returns M_INVALID if not recognized.
+ *
+ * This is the first step toward placing method names in a configurable
+ * list.  Hopefully it (and other routines) can eventually be moved to
+ * something like a mod_http_methods.c, complete with config stuff.
+ */
+AP_DECLARE(int) ap_method_number_of(const char *method)
+{
+    apr_size_t len = strlen(method);
+    int which = lookup_builtin_method(method, len);
+
+    if (which != UNKNOWN_METHOD)
+        return which;
+
+    return M_INVALID;
+}
+
+static ap_conf_vector_t *create_empty_config(apr_pool_t *p)
+{
+    void *conf_vector = apr_pcalloc(p, sizeof(void *) *
+                                    (DYNAMIC_MODULE_LIMIT));
+    return conf_vector;
+}
+
+AP_CORE_DECLARE(ap_conf_vector_t *) ap_create_request_config(apr_pool_t *p)
+{
+    return create_empty_config(p);
+}
+
+AP_CORE_DECLARE(ap_conf_vector_t *) ap_create_conn_config(apr_pool_t *p)
+{
+    return create_empty_config(p);
+}
+
+AP_DECLARE(apr_status_t) ap_wrap_make_request(request_rec *r, const char *url,
+                                              const char *method,
+                                              const char *content_type,
+                                              const char *content_encoding,
+                                              apr_size_t content_length, char *content)
+{
+    apr_status_t rc;
+
+    if (!url) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Missing url");
+        return APR_EINVAL;
+    }
+    if (!method)
+        method = "GET";
+    if ((r->method_number = lookup_builtin_method(method, strlen(method))) ==
+                                UNKNOWN_METHOD) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Unknown HTTP metdod %s",
+                      method);
+        return APR_EINVAL;
+    }
+    r->method = method;
+    r->protocol = AP_SERVER_PROTOCOL;
+    r->proto_num = HTTP_VERSION(1, 1);
+    r->request_config  = ap_create_request_config(r->pool);
+
+    if ((rc = apr_uri_parse(r->pool, url, &r->parsed_uri)) != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "error parsing uri");
+        return APR_EINVAL;
+    }
+    r->user = r->parsed_uri.user;
+    r->content_type = content_type;
+    r->content_encoding = content_encoding;
+    r->uri = r->parsed_uri.path;
+    if (r->parsed_uri.query)
+        r->unparsed_uri = apr_pstrcat(r->pool, r->parsed_uri.path, "?",
+                                      r->parsed_uri.query, NULL);
+    else
+        r->unparsed_uri = r->uri;
+    if (!r->parsed_uri.port)
+        r->parsed_uri.port = r->server->port;
+    
+    if (r->parsed_uri.hostname) {
+        if (r->parsed_uri.port)
+        apr_table_addn(r->headers_in, "Host",
+            apr_psprintf(r->pool, "%s:%d", r->parsed_uri.hostname, r->parsed_uri.port));
+        else
+            apr_table_addn(r->headers_in, "Host", r->parsed_uri.hostname);
+    }
+    if (r->content_type)
+        apr_table_addn(r->headers_in, "Content-Type", r->content_type);
+    if (r->content_encoding)
+        apr_table_addn(r->headers_in, "Transfer-Encoding", r->content_encoding);
+    if (content_length)
+        apr_table_addn(r->headers_in, "Content-Length", apr_itoa(r->pool,
+                                                       (int)content_length));
+    apr_table_addn(r->headers_in, "Accept", "*/*");
+    apr_table_addn(r->headers_in, "Pragma", "no-cache");
+    apr_table_addn(r->headers_in, "User-Agent", "httpd-wrap/1.0");
+    apr_table_addn(r->headers_in, "Accept-Charset", "iso-8859-2");
+    apr_table_addn(r->headers_in, "Accept-Language", "hr");
+
+    /* Create a simple bucket brigade for post data inside connection */
+    if (content) {
+        apr_bucket *e;
+        if (!r->connection->bucket_alloc)
+            r->connection->bucket_alloc = apr_bucket_alloc_create(r->connection->pool);
+        r->connection->bb = apr_brigade_create(r->connection->pool,
+                                               r->connection->bucket_alloc);
+        e = apr_bucket_transient_create(content, content_length, r->connection->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(r->connection->bb, e);
+ 
+    }
+    return APR_SUCCESS;
+}
+
+AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy)
+{
+    const char *lenp = apr_table_get(r->headers_in, "Content-Length");
+    
+    r->remaining = 0;
+    r->read_length = 0;
+    if (lenp)
+        r->remaining = atoi(lenp);
+    return OK;
+}
+
+AP_DECLARE(int) ap_should_client_block(request_rec *r)
+{
+    /* First check if we have already read the request body */
+    /* Since we are not using chunked skip the readed checking */
+    if (r->read_length)
+        return 0;
+    else
+        return 1;
+}
+
+AP_DECLARE(long) ap_get_client_block(request_rec *r, char *buffer,
+                                     apr_size_t bufsiz)
+{
+    apr_status_t rv;
+ 
+    if (r->remaining < 0 || r->remaining == 0)
+        return 0;
+    
+    rv = apr_brigade_flatten(r->connection->bb, buffer, &bufsiz); 
+
+    if (rv != APR_SUCCESS) {
+        return -1;
+    }
+
+    r->read_length += bufsiz;
+    r->remaining   -= bufsiz;
+
+    /* Remove the readed part */
+    if (bufsiz) {
+        apr_bucket *e;
+        e = APR_BRIGADE_FIRST(r->connection->bb);
+        apr_bucket_split(e, bufsiz);
+        e = APR_BRIGADE_FIRST(r->connection->bb);
+        APR_BUCKET_REMOVE(e);
+    }
+
+    return (long)bufsiz; 
+
+}
+
+/* No op */
+AP_DECLARE(int) ap_discard_request_body(request_rec *r)
+{
+    return OK;
+}
+
diff --git a/connectors/ajp/ajplib/test/httpd_wrap.h b/connectors/ajp/ajplib/test/httpd_wrap.h
new file mode 100644
index 0000000..a51cd78
--- /dev/null
+++ b/connectors/ajp/ajplib/test/httpd_wrap.h
@@ -0,0 +1,787 @@
+/* Copyright 2000-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef HTTPD_WRAP_H
+#define HTTPD_WRAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "apr.h"
+#include "apr_hooks.h"
+#include "apr_optional_hooks.h"
+#include "apr_thread_proc.h"
+
+/* No WIN32 dll support */
+
+#define AP_DECLARE(type)            type
+#define AP_DECLARE_NONSTD(type)     type
+#define AP_DECLARE_DATA
+/**
+ * @internal
+ * modules should not used functions marked AP_CORE_DECLARE
+ */
+#ifndef AP_CORE_DECLARE
+# define AP_CORE_DECLARE	AP_DECLARE
+#endif 
+
+/** The default string lengths */
+#define MAX_STRING_LEN HUGE_STRING_LEN
+#define HUGE_STRING_LEN 8192
+
+/** The size of the server's internal read-write buffers */
+#define AP_IOBUFSIZE 8192
+
+#define DECLINED -1     /**< Module declines to handle */
+#define DONE -2         /**< Module has served the response completely 
+                 *  - it's safe to die() with no more output
+                 */
+#define OK 0            /**< Module has handled this stage. */
+
+
+/**
+ * @defgroup HTTP_Status HTTP Status Codes
+ * @{
+ */
+/**
+ * The size of the static array in http_protocol.c for storing
+ * all of the potential response status-lines (a sparse table).
+ * A future version should dynamically generate the apr_table_t at startup.
+ */
+#define RESPONSE_CODES 57
+
+#define HTTP_CONTINUE                      100
+#define HTTP_SWITCHING_PROTOCOLS           101
+#define HTTP_PROCESSING                    102
+#define HTTP_OK                            200
+#define HTTP_CREATED                       201
+#define HTTP_ACCEPTED                      202
+#define HTTP_NON_AUTHORITATIVE             203
+#define HTTP_NO_CONTENT                    204
+#define HTTP_RESET_CONTENT                 205
+#define HTTP_PARTIAL_CONTENT               206
+#define HTTP_MULTI_STATUS                  207
+#define HTTP_MULTIPLE_CHOICES              300
+#define HTTP_MOVED_PERMANENTLY             301
+#define HTTP_MOVED_TEMPORARILY             302
+#define HTTP_SEE_OTHER                     303
+#define HTTP_NOT_MODIFIED                  304
+#define HTTP_USE_PROXY                     305
+#define HTTP_TEMPORARY_REDIRECT            307
+#define HTTP_BAD_REQUEST                   400
+#define HTTP_UNAUTHORIZED                  401
+#define HTTP_PAYMENT_REQUIRED              402
+#define HTTP_FORBIDDEN                     403
+#define HTTP_NOT_FOUND                     404
+#define HTTP_METHOD_NOT_ALLOWED            405
+#define HTTP_NOT_ACCEPTABLE                406
+#define HTTP_PROXY_AUTHENTICATION_REQUIRED 407
+#define HTTP_REQUEST_TIME_OUT              408
+#define HTTP_CONFLICT                      409
+#define HTTP_GONE                          410
+#define HTTP_LENGTH_REQUIRED               411
+#define HTTP_PRECONDITION_FAILED           412
+#define HTTP_REQUEST_ENTITY_TOO_LARGE      413
+#define HTTP_REQUEST_URI_TOO_LARGE         414
+#define HTTP_UNSUPPORTED_MEDIA_TYPE        415
+#define HTTP_RANGE_NOT_SATISFIABLE         416
+#define HTTP_EXPECTATION_FAILED            417
+#define HTTP_UNPROCESSABLE_ENTITY          422
+#define HTTP_LOCKED                        423
+#define HTTP_FAILED_DEPENDENCY             424
+#define HTTP_UPGRADE_REQUIRED              426
+#define HTTP_INTERNAL_SERVER_ERROR         500
+#define HTTP_NOT_IMPLEMENTED               501
+#define HTTP_BAD_GATEWAY                   502
+#define HTTP_SERVICE_UNAVAILABLE           503
+#define HTTP_GATEWAY_TIME_OUT              504
+#define HTTP_VERSION_NOT_SUPPORTED         505
+#define HTTP_VARIANT_ALSO_VARIES           506
+#define HTTP_INSUFFICIENT_STORAGE          507
+#define HTTP_NOT_EXTENDED                  510
+
+/** is the status code informational */
+#define ap_is_HTTP_INFO(x)         (((x) >= 100)&&((x) < 200))
+/** is the status code OK ?*/
+#define ap_is_HTTP_SUCCESS(x)      (((x) >= 200)&&((x) < 300))
+/** is the status code a redirect */
+#define ap_is_HTTP_REDIRECT(x)     (((x) >= 300)&&((x) < 400))
+/** is the status code a error (client or server) */
+#define ap_is_HTTP_ERROR(x)        (((x) >= 400)&&((x) < 600))
+/** is the status code a client error  */
+#define ap_is_HTTP_CLIENT_ERROR(x) (((x) >= 400)&&((x) < 500))
+/** is the status code a server error  */
+#define ap_is_HTTP_SERVER_ERROR(x) (((x) >= 500)&&((x) < 600))
+
+/** should the status code drop the connection */
+#define ap_status_drops_connection(x) \
+                                   (((x) == HTTP_BAD_REQUEST)           || \
+                                    ((x) == HTTP_REQUEST_TIME_OUT)      || \
+                                    ((x) == HTTP_LENGTH_REQUIRED)       || \
+                                    ((x) == HTTP_REQUEST_ENTITY_TOO_LARGE) || \
+                                    ((x) == HTTP_REQUEST_URI_TOO_LARGE) || \
+                                    ((x) == HTTP_INTERNAL_SERVER_ERROR) || \
+                                    ((x) == HTTP_SERVICE_UNAVAILABLE) || \
+                    ((x) == HTTP_NOT_IMPLEMENTED))
+                    
+/* Maximum number of dynamically loaded modules */
+#ifndef DYNAMIC_MODULE_LIMIT
+#define DYNAMIC_MODULE_LIMIT 64
+#endif
+/* Default administrator's address */
+#define DEFAULT_ADMIN "[no address given]"
+/* The timeout for waiting for messages */
+#ifndef DEFAULT_TIMEOUT
+#define DEFAULT_TIMEOUT 300 
+#endif
+/* The timeout for waiting for keepalive timeout until next request */
+#ifndef DEFAULT_KEEPALIVE_TIMEOUT
+#define DEFAULT_KEEPALIVE_TIMEOUT 15
+#endif
+/* The number of requests to entertain per connection */
+#ifndef DEFAULT_KEEPALIVE
+#define DEFAULT_KEEPALIVE 100
+#endif
+#ifndef DEFAULT_LIMIT_REQUEST_LINE
+#define DEFAULT_LIMIT_REQUEST_LINE 8190
+#endif /* default limit on bytes in Request-Line (Method+URI+HTTP-version) */
+#ifndef DEFAULT_LIMIT_REQUEST_FIELDSIZE
+#define DEFAULT_LIMIT_REQUEST_FIELDSIZE 8190
+#endif /* default limit on bytes in any one header field  */
+#ifndef DEFAULT_LIMIT_REQUEST_FIELDS
+#define DEFAULT_LIMIT_REQUEST_FIELDS 100
+#endif /* default limit on number of request header fields */
+#ifndef DEFAULT_CONTENT_TYPE
+#define DEFAULT_CONTENT_TYPE "text/plain"
+#endif
+/**
+ * The address 255.255.255.255, when used as a virtualhost address,
+ * will become the "default" server when the ip doesn't match other vhosts.
+ */
+#define DEFAULT_VHOST_ADDR 0xfffffffful
+ 
+/* options for get_remote_host() */
+/* REMOTE_HOST returns the hostname, or NULL if the hostname
+ * lookup fails.  It will force a DNS lookup according to the
+ * HostnameLookups setting.
+ */
+#define REMOTE_HOST (0)
+
+/* REMOTE_NAME returns the hostname, or the dotted quad if the
+ * hostname lookup fails.  It will force a DNS lookup according
+ * to the HostnameLookups setting.
+ */
+#define REMOTE_NAME (1)
+
+/* REMOTE_NOLOOKUP is like REMOTE_NAME except that a DNS lookup is
+ * never forced.
+ */
+#define REMOTE_NOLOOKUP (2)
+
+/* REMOTE_DOUBLE_REV will always force a DNS lookup, and also force
+ * a double reverse lookup, regardless of the HostnameLookups
+ * setting.  The result is the (double reverse checked) hostname,
+ * or NULL if any of the lookups fail.
+ */
+#define REMOTE_DOUBLE_REV (3)
+                     
+/** @} */
+/**
+ * @defgroup Methods List of Methods recognized by the server
+ * @{
+ */
+/**
+ * Methods recognized (but not necessarily handled) by the server.
+ * These constants are used in bit shifting masks of size int, so it is
+ * unsafe to have more methods than bits in an int.  HEAD == M_GET.
+ * This list must be tracked by the list in http_protocol.c in routine
+ * ap_method_name_of().
+ */
+#define M_GET                   0       /* RFC 2616: HTTP */
+#define M_PUT                   1       /*  :             */
+#define M_POST                  2
+#define M_DELETE                3
+#define M_CONNECT               4
+#define M_OPTIONS               5
+#define M_TRACE                 6       /* RFC 2616: HTTP */
+#define M_PATCH                 7       /* no rfc(!)  ### remove this one? */
+#define M_PROPFIND              8       /* RFC 2518: WebDAV */
+#define M_PROPPATCH             9       /*  :               */
+#define M_MKCOL                 10
+#define M_COPY                  11
+#define M_MOVE                  12
+#define M_LOCK                  13
+#define M_UNLOCK                14      /* RFC 2518: WebDAV */
+#define M_VERSION_CONTROL       15      /* RFC 3253: WebDAV Versioning */
+#define M_CHECKOUT              16      /*  :                          */
+#define M_UNCHECKOUT            17
+#define M_CHECKIN               18
+#define M_UPDATE                19
+#define M_LABEL                 20
+#define M_REPORT                21
+#define M_MKWORKSPACE           22
+#define M_MKACTIVITY            23
+#define M_BASELINE_CONTROL      24
+#define M_MERGE                 25
+#define M_INVALID               26      /* RFC 3253: WebDAV Versioning */
+
+/**
+ * METHODS needs to be equal to the number of bits
+ * we are using for limit masks.
+ */
+#define METHODS     64
+
+/**
+ * The method mask bit to shift for anding with a bitmask.
+ */
+#define AP_METHOD_BIT ((apr_int64_t)1)
+
+/*
+ * This is a convenience macro to ease with checking a mask
+ * against a method name.
+ */
+#define AP_METHOD_CHECK_ALLOWED(mask, methname) \
+    ((mask) & (AP_METHOD_BIT << ap_method_number_of((methname))))
+
+/** @} */
+
+/** default HTTP Server protocol */
+#define AP_SERVER_PROTOCOL "HTTP/1.1"
+/** Internal representation for a HTTP protocol number, e.g., HTTP/1.1 */
+
+#define HTTP_VERSION(major,minor) (1000*(major)+(minor))
+/** Major part of HTTP protocol */
+#define HTTP_VERSION_MAJOR(number) ((number)/1000)
+/** Minor part of HTTP protocol */
+#define HTTP_VERSION_MINOR(number) ((number)%1000)
+
+/**
+ * @defgroup values_request_rec_body Possible values for request_rec.read_body 
+ * @{
+ * Possible values for request_rec.read_body (set by handling module):
+ */
+
+/** Send 413 error if message has any body */
+#define REQUEST_NO_BODY          0
+/** Send 411 error if body without Content-Length */
+#define REQUEST_CHUNKED_ERROR    1
+/** If chunked, remove the chunks for me. */
+#define REQUEST_CHUNKED_DECHUNK  2
+/** @} */
+ 
+
+/* fake structure declarations */
+typedef struct process_rec  process_rec;
+typedef struct request_rec  request_rec;
+typedef struct conn_rec     conn_rec;
+typedef struct server_rec   server_rec;
+typedef struct ap_conf_vector_t ap_conf_vector_t;
+ 
+
+/* fake structure definitions */
+/** A structure that represents one process */
+struct process_rec {
+    /** Global pool. Cleared upon normal exit */
+    apr_pool_t *pool;
+    /** Configuration pool. Cleared upon restart */
+    apr_pool_t *pconf;
+    /** Number of command line arguments passed to the program */
+    int argc;
+    /** The command line arguments */
+    const char * const *argv;
+    /** The program name used to execute the program */
+    const char *short_name;
+};
+
+/** A structure that represents the current request */
+struct request_rec {
+    /** The pool associated with the request */
+    apr_pool_t *pool;
+    /** The connection to the client */
+    conn_rec *connection;
+    /** The virtual host for this request */
+    server_rec *server;
+    /** First line of request */
+    char *the_request;
+    /** HTTP/0.9, "simple" request (e.g. GET /foo\n w/no headers) */
+    int assbackwards;
+    /** A proxy request (calculated during post_read_request/translate_name)
+     *  possible values PROXYREQ_NONE, PROXYREQ_PROXY, PROXYREQ_REVERSE,
+     *                  PROXYREQ_RESPONSE
+     */
+    int proxyreq;
+    /** HEAD request, as opposed to GET */
+    int header_only;
+    /** Protocol string, as given to us, or HTTP/0.9 */
+    char *protocol;
+    /** Protocol version number of protocol; 1.1 = 1001 */
+    int proto_num;
+    /** Host, as set by full URI or Host: */
+    const char *hostname;
+
+    /** Time when the request started */
+    apr_time_t request_time;
+
+    /** Status line, if set by script */
+    const char *status_line;
+    /** Status line */
+    int status;
+
+    /* Request method, two ways; also, protocol, etc..  Outside of protocol.c,
+     * look, but don't touch.
+     */
+
+    /** Request method (eg. GET, HEAD, POST, etc.) */
+    const char *method;
+    /** M_GET, M_POST, etc. */
+    int method_number;
+ 
+    /** MIME header environment from the request */
+    apr_table_t *headers_in;
+    /** MIME header environment for the response */
+    apr_table_t *headers_out;
+    /** MIME header environment for the response, printed even on errors and
+     * persist across internal redirects */
+    apr_table_t *err_headers_out;
+    /** Array of environment variables to be used for sub processes */
+    apr_table_t *subprocess_env;
+    /** Notes from one module to another */
+    apr_table_t *notes;
+
+    /* content_type, handler, content_encoding, and all content_languages 
+     * MUST be lowercased strings.  They may be pointers to static strings;
+     * they should not be modified in place.
+     */
+    /** The content-type for the current request */
+    const char *content_type;   /* Break these out --- we dispatch on 'em */
+    /** The handler string that we use to call a handler function */
+    const char *handler;    /* What we *really* dispatch on */
+
+    /** Remaining bytes left to read from the request body */
+    apr_off_t remaining;
+    /** Number of bytes that have been read  from the request body */
+    apr_off_t read_length;
+
+    /** How to encode the data */
+    const char *content_encoding;
+    /** Array of strings representing the content languages */
+    apr_array_header_t *content_languages;
+    /** If an authentication check was made, this gets set to the user name. */
+    char *user; 
+    /** If an authentication check was made, this gets set to the auth type. */
+    char *ap_auth_type;
+     /** The URI without any parsing performed */
+    char *unparsed_uri; 
+    /** The path portion of the URI */
+    char *uri;
+    /** The filename on disk corresponding to this response */
+    char *filename;
+    /* XXX: What does this mean? Please define "canonicalize" -aaron */
+    /** The true filename, we canonicalize r->filename if these don't match */
+    char *canonical_filename;
+    /** The PATH_INFO extracted from this request */
+    char *path_info;
+    /** The QUERY_ARGS extracted from this request */
+    char *args; 
+    /** ST_MODE set to zero if no such file */
+    apr_finfo_t finfo;
+    /** A struct containing the components of URI */
+    apr_uri_t parsed_uri;
+    /** Options set in config files, etc. */
+    struct ap_conf_vector_t *per_dir_config;
+    /** Notes on *this* request */
+    struct ap_conf_vector_t *request_config;         
+};
+
+/** Structure to store things which are per connection */
+struct conn_rec {
+    /** Pool associated with this connection */
+    apr_pool_t *pool;
+    /** Physical vhost this conn came in on */
+    server_rec *base_server;
+    /* Information about the connection itself */
+    /** local address */
+    apr_sockaddr_t *local_addr;
+    /** remote address */
+    apr_sockaddr_t *remote_addr;
+    /** Client's IP address */
+    char *remote_ip;
+    /** Client's DNS name, if known.  NULL if DNS hasn't been checked,
+     *  "" if it has and no address was found.  N.B. Only access this though
+     * get_remote_host() */
+    char *remote_host; 
+    /** How many times have we used it? */
+    int keepalives;
+    /** server IP address */
+    char *local_ip;
+    /** used for ap_get_server_name when UseCanonicalName is set to DNS
+     *  (ignores setting of HostnameLookups) */
+    char *local_host;
+     /** ID of this connection; unique at any point in time */
+    long id;
+    /** Notes on *this* connection */
+    struct ap_conf_vector_t *conn_config;
+    /** send note from one module to another, must remain valid for all
+     *  requests on this conn */
+    apr_table_t *notes; 
+    /** handle to scoreboard information for this connection */
+    void *sbh; 
+    /** The bucket allocator to use for all bucket/brigade creations */
+    struct apr_bucket_alloc_t *bucket_alloc;
+    /** This doesn't exists in the original, but it is here to simulate the network */
+    apr_bucket_brigade *bb;    
+};
+
+/** A structure to be used for Per-vhost config */
+typedef struct server_addr_rec server_addr_rec;
+struct server_addr_rec {
+    /** The next server in the list */
+    server_addr_rec *next;
+    /** The bound address, for this server */
+    apr_sockaddr_t *host_addr;
+    /** The bound port, for this server */
+    apr_port_t host_port;
+    /** The name given in <VirtualHost> */
+    char *virthost;
+};
+
+/** A structure to store information for each virtual server */
+struct server_rec {
+    /** The process this server is running in */
+    process_rec *process;
+    /* Contact information */
+    /** The admin's contact information */
+    char *server_admin;
+     /** The server hostname */
+    char *server_hostname;
+    /** for redirects, etc. */
+    apr_port_t port;
+    /** The log level for this server */
+    int loglevel;
+
+    server_addr_rec *addrs; 
+    /** Timeout, as an apr interval, before we give up */
+    apr_interval_time_t timeout;
+    /** The apr interval we will wait for another request */
+    apr_interval_time_t keep_alive_timeout;
+    /** Maximum requests per connection */
+    int keep_alive_max;
+    /** Use persistent connections? */
+    int keep_alive;
+ 
+    /** true if this is the virtual server */
+    int is_virtual; 
+    /** limit on size of the HTTP request line    */
+    int limit_req_line;
+    /** limit on size of any request header field */
+    int limit_req_fieldsize;
+    /** limit on number of request header fields  */
+    int limit_req_fields;
+
+};
+
+/* Apache logging support */
+#define APLOG_EMERG 0   /* system is unusable */
+#define APLOG_ALERT 1   /* action must be taken immediately */
+#define APLOG_CRIT  2   /* critical conditions */
+#define APLOG_ERR   3   /* error conditions */
+#define APLOG_WARNING   4   /* warning conditions */
+#define APLOG_NOTICE    5   /* normal but significant condition */
+#define APLOG_INFO  6   /* informational */
+#define APLOG_DEBUG 7   /* debug-level messages */
+
+#define APLOG_LEVELMASK 7   /* mask off the level value */
+
+
+/* APLOG_NOERRNO is ignored and should not be used.  It will be
+ * removed in a future release of Apache.
+ */
+#define APLOG_NOERRNO       (APLOG_LEVELMASK + 1)
+
+/* Use APLOG_TOCLIENT on ap_log_rerror() to give content
+ * handlers the option of including the error text in the 
+ * ErrorDocument sent back to the client. Setting APLOG_TOCLIENT
+ * will cause the error text to be saved in the request_rec->notes 
+ * table, keyed to the string "error-notes", if and only if:
+ * - the severity level of the message is APLOG_WARNING or greater
+ * - there are no other "error-notes" set in request_rec->notes
+ * Once error-notes is set, it is up to the content handler to
+ * determine whether this text should be sent back to the client.
+ * Note: Client generated text streams sent back to the client MUST 
+ * be escaped to prevent CSS attacks.
+ */
+#define APLOG_TOCLIENT          ((APLOG_LEVELMASK + 1) * 2)
+
+/* normal but significant condition on startup, usually printed to stderr */
+#define APLOG_STARTUP           ((APLOG_LEVELMASK + 1) * 4) 
+
+#ifndef DEFAULT_LOGLEVEL
+#define DEFAULT_LOGLEVEL    APLOG_WARNING
+#endif
+
+extern int AP_DECLARE_DATA ap_default_loglevel;
+
+#define APLOG_MARK  __FILE__,__LINE__
+
+
+/**
+ * One of the primary logging routines in Apache.  This uses a printf-like
+ * format to log messages to the error_log.
+ * @param file The file in which this function is called
+ * @param line The line number on which this function is called
+ * @param level The level of this error message
+ * @param status The status code from the previous command
+ * @param s The server on which we are logging
+ * @param fmt The format string
+ * @param ... The arguments to use to fill out fmt.
+ * @tip Use APLOG_MARK to fill out file and line
+ * @warning It is VERY IMPORTANT that you not include any raw data from 
+ * the network, such as the request-URI or request header fields, within 
+ * the format string.  Doing so makes the server vulnerable to a 
+ * denial-of-service attack and other messy behavior.  Instead, use a 
+ * simple format string like "%s", followed by the string containing the 
+ * untrusted data.
+ * @deffunc void ap_log_error(const char *file, int line, int level, apr_status_t status, const server_rec *s, const char *fmt, ...) 
+ */
+AP_DECLARE(void) ap_log_error(const char *file, int line, int level, 
+                             apr_status_t status, const server_rec *s, 
+                             const char *fmt, ...)
+                __attribute__((format(printf,6,7)));
+
+/**
+ * The second of the primary logging routines in Apache.  This uses 
+ * a printf-like format to log messages to the error_log.
+ * @param file The file in which this function is called
+ * @param line The line number on which this function is called
+ * @param level The level of this error message
+ * @param status The status code from the previous command
+ * @param p The pool which we are logging for
+ * @param fmt The format string
+ * @param ... The arguments to use to fill out fmt.
+ * @tip Use APLOG_MARK to fill out file and line
+ * @warning It is VERY IMPORTANT that you not include any raw data from 
+ * the network, such as the request-URI or request header fields, within 
+ * the format string.  Doing so makes the server vulnerable to a 
+ * denial-of-service attack and other messy behavior.  Instead, use a 
+ * simple format string like "%s", followed by the string containing the 
+ * untrusted data.
+ * @deffunc void ap_log_perror(const char *file, int line, int level, apr_status_t status, apr_pool_t *p, const char *fmt, ...) 
+ */
+AP_DECLARE(void) ap_log_perror(const char *file, int line, int level, 
+                             apr_status_t status, apr_pool_t *p, 
+                             const char *fmt, ...)
+                __attribute__((format(printf,6,7)));
+
+/**
+ * The last of the primary logging routines in Apache.  This uses 
+ * a printf-like format to log messages to the error_log.
+ * @param file The file in which this function is called
+ * @param line The line number on which this function is called
+ * @param level The level of this error message
+ * @param status The status code from the previous command
+ * @param s The request which we are logging for
+ * @param fmt The format string
+ * @param ... The arguments to use to fill out fmt.
+ * @tip Use APLOG_MARK to fill out file and line
+ * @warning It is VERY IMPORTANT that you not include any raw data from 
+ * the network, such as the request-URI or request header fields, within 
+ * the format string.  Doing so makes the server vulnerable to a 
+ * denial-of-service attack and other messy behavior.  Instead, use a 
+ * simple format string like "%s", followed by the string containing the 
+ * untrusted data.
+ * @deffunc void ap_log_rerror(const char *file, int line, int level, apr_status_t status, request_rec *r, const char *fmt, ...) 
+ */
+AP_DECLARE(void) ap_log_rerror(const char *file, int line, int level, 
+                               apr_status_t status, const request_rec *r, 
+                               const char *fmt, ...)
+                __attribute__((format(printf,6,7)));
+/**
+ * create_connection is a RUN_FIRST hook which allows modules to create 
+ * connections. In general, you should not install filters with the 
+ * create_connection hook. If you require vhost configuration information 
+ * to make filter installation decisions, you must use the pre_connection
+ * or install_network_transport hook. This hook should close the connection
+ * if it encounters a fatal error condition.
+ *
+ * @param p The pool from which to allocate the connection record
+ * @param csd The socket that has been accepted
+ * @param conn_id A unique identifier for this connection.  The ID only
+ *                needs to be unique at that time, not forever.
+ * @param sbh A handle to scoreboard information for this connection.
+ * @return An allocated connection record or NULL.
+ */ 
+AP_DECLARE(conn_rec *) ap_run_create_connection(apr_pool_t *ptrans,
+                                  server_rec *server,
+                                  apr_socket_t *csd, long id, void *sbh,
+                                  apr_bucket_alloc_t *alloc);
+
+/**
+ * Get the current server name from the request
+ * @param r The current request
+ * @return the server name
+ * @deffunc const char *ap_get_server_name(request_rec *r)
+ */
+AP_DECLARE(const char *) ap_get_server_name(request_rec *r);
+
+/**
+ * Lookup the remote client's DNS name or IP address
+ * @param conn The current connection
+ * @param dir_config The directory config vector from the request
+ * @param type The type of lookup to perform.  One of:
+ * <pre>
+ *     REMOTE_HOST returns the hostname, or NULL if the hostname
+ *                 lookup fails.  It will force a DNS lookup according to the
+ *                 HostnameLookups setting.
+ *     REMOTE_NAME returns the hostname, or the dotted quad if the
+ *                 hostname lookup fails.  It will force a DNS lookup according
+ *                 to the HostnameLookups setting.
+ *     REMOTE_NOLOOKUP is like REMOTE_NAME except that a DNS lookup is
+ *                     never forced.
+ *     REMOTE_DOUBLE_REV will always force a DNS lookup, and also force
+ *                   a double reverse lookup, regardless of the HostnameLookups
+ *                   setting.  The result is the (double reverse checked) 
+ *                   hostname, or NULL if any of the lookups fail.
+ * </pre>
+ * @param str_is_ip unless NULL is passed, this will be set to non-zero on output when an IP address 
+ *        string is returned
+ * @return The remote hostname
+ * @deffunc const char *ap_get_remote_host(conn_rec *conn, void *dir_config, int type, int *str_is_ip)
+ */
+AP_DECLARE(const char *) ap_get_remote_host(conn_rec *conn, void *dir_config, int type, int *str_is_ip);
+
+/* Reading a block of data from the client connection (e.g., POST arg) */
+
+/**
+ * Setup the client to allow Apache to read the request body.
+ * @param r The current request
+ * @param read_policy How the server should interpret a chunked 
+ *                    transfer-encoding.  One of: <pre>
+ *    REQUEST_NO_BODY          Send 413 error if message has any body
+ *    REQUEST_CHUNKED_ERROR    Send 411 error if body without Content-Length
+ *    REQUEST_CHUNKED_DECHUNK  If chunked, remove the chunks for me.
+ * </pre>
+ * @return either OK or an error code
+ * @deffunc int ap_setup_client_block(request_rec *r, int read_policy)
+ */
+AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy);
+
+/**
+ * Determine if the client has sent any data.  This also sends a 
+ * 100 Continue response to HTTP/1.1 clients, so modules should not be called
+ * until the module is ready to read content.
+ * @warning Never call this function more than once.
+ * @param r The current request
+ * @return 0 if there is no message to read, 1 otherwise
+ * @deffunc int ap_should_client_block(request_rec *r)
+ */
+AP_DECLARE(int) ap_should_client_block(request_rec *r);
+
+/**
+ * Call this in a loop.  It will put data into a buffer and return the length
+ * of the input block
+ * @param r The current request
+ * @param buffer The buffer in which to store the data
+ * @param bufsiz The size of the buffer
+ * @return Number of bytes inserted into the buffer.  When done reading, 0
+ *         if EOF, or -1 if there was an error
+ * @deffunc long ap_get_client_block(request_rec *r, char *buffer, apr_size_t bufsiz)
+ */
+AP_DECLARE(long) ap_get_client_block(request_rec *r, char *buffer, apr_size_t bufsiz);
+
+/**
+ * In HTTP/1.1, any method can have a body.  However, most GET handlers
+ * wouldn't know what to do with a request body if they received one.
+ * This helper routine tests for and reads any message body in the request,
+ * simply discarding whatever it receives.  We need to do this because
+ * failing to read the request body would cause it to be interpreted
+ * as the next request on a persistent connection.
+ * @param r The current request
+ * @return error status if request is malformed, OK otherwise 
+ * @deffunc int ap_discard_request_body(request_rec *r)
+ */
+AP_DECLARE(int) ap_discard_request_body(request_rec *r);
+
+/**
+ * Set the content type for this request (r->content_type). 
+ * @param r The current request
+ * @param ct The new content type
+ * @deffunc void ap_set_content_type(request_rec *r, const char* ct)
+ * @warning This function must be called to set r->content_type in order 
+ * for the AddOutputFilterByType directive to work correctly.
+ */
+AP_DECLARE(void) ap_set_content_type(request_rec *r, const char *ct);
+
+/**
+ * Setup the config vector for a request_rec
+ * @param p The pool to allocate the config vector from
+ * @return The config vector
+ */
+AP_CORE_DECLARE(ap_conf_vector_t*) ap_create_request_config(apr_pool_t *p);
+
+/* For http_connection.c... */
+/**
+ * Setup the config vector for a connection_rec
+ * @param p The pool to allocate the config vector from
+ * @return The config vector
+ */
+AP_CORE_DECLARE(ap_conf_vector_t*) ap_create_conn_config(apr_pool_t *p);
+
+/**
+ * Get the method number associated with the given string, assumed to
+ * contain an HTTP method.  Returns M_INVALID if not recognized.
+ * @param method A string containing a valid HTTP method
+ * @return The method number
+ */
+AP_DECLARE(int) ap_method_number_of(const char *method);
+
+/**
+ * create the request_rec structure from fake client connection 
+ */
+AP_DECLARE(request_rec *) ap_wrap_create_request(conn_rec *conn);
+
+/**
+ * create the server_rec structure from process_rec. 
+ */
+AP_DECLARE(server_rec *) ap_wrap_create_server(process_rec *process, apr_pool_t *p);
+
+/**
+ * create the main process_rec. 
+ */
+AP_DECLARE(process_rec *) ap_wrap_create_process(int argc, const char * const *argv);
+
+/**
+ * Fill the request_rec with data. 
+ * @param r      The current request
+ * @param url    The full url http://[username:password@]hostname[:port]/uri
+ * @param method Method "GET", "POST", etc...
+ * @param content_type The post data content type
+ * @param content_encoding The post data transfer encoding
+ * @param content_length The post data content length
+ * @param content The post content data.
+ * @return APR_SUCCESS or error
+ */
+AP_DECLARE(apr_status_t) ap_wrap_make_request(request_rec *r, const char *url,
+                                              const char *method,
+                                              const char *content_type,
+                                              const char *content_encoding,
+                                              apr_size_t content_length, char *content);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HTTPD_WRAP_H */
diff --git a/connectors/ajp/ajplib/test/test.sln b/connectors/ajp/ajplib/test/test.sln
new file mode 100644
index 0000000..60d3207
--- /dev/null
+++ b/connectors/ajp/ajplib/test/test.sln
@@ -0,0 +1,94 @@
+Microsoft Visual Studio Solution File, Format Version 8.00

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testajp", "testajp.vcproj", "{398AC1EA-8B86-4746-B4F3-D174440703E0}"

+	ProjectSection(ProjectDependencies) = postProject

+		{A58AA747-485E-42FE-880F-15B9A498B75D} = {A58AA747-485E-42FE-880F-15B9A498B75D}

+		{54D9CF8D-ED01-4E87-89C4-F06F9D757EED} = {54D9CF8D-ED01-4E87-89C4-F06F9D757EED}

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "apr", "..\srclib\apr\apr.vcproj", "{54C317DF-BBE3-4F62-96CF-A6D7344EB615}"

+	ProjectSection(ProjectDependencies) = postProject

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libapr", "..\srclib\apr\libapr.vcproj", "{54D9CF8D-ED01-4E87-89C4-F06F9D757EED}"

+	ProjectSection(ProjectDependencies) = postProject

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "aprutil", "..\srclib\apr-util\aprutil.vcproj", "{CA59109B-88A7-4547-BDCE-9568AB08DD53}"

+	ProjectSection(ProjectDependencies) = postProject

+		{315E11BD-4452-48FE-AF47-A39BF4EDD608} = {315E11BD-4452-48FE-AF47-A39BF4EDD608}

+		{EF7D84CE-5F76-47C8-A198-B3EF72870920} = {EF7D84CE-5F76-47C8-A198-B3EF72870920}

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libaprutil", "..\srclib\apr-util\libaprutil.vcproj", "{A58AA747-485E-42FE-880F-15B9A498B75D}"

+	ProjectSection(ProjectDependencies) = postProject

+		{54D9CF8D-ED01-4E87-89C4-F06F9D757EED} = {54D9CF8D-ED01-4E87-89C4-F06F9D757EED}

+		{315E11BD-4452-48FE-AF47-A39BF4EDD608} = {315E11BD-4452-48FE-AF47-A39BF4EDD608}

+		{EF7D84CE-5F76-47C8-A198-B3EF72870920} = {EF7D84CE-5F76-47C8-A198-B3EF72870920}

+		{DDCAEED8-D4D0-4C34-A961-FCA7D8EC26B7} = {DDCAEED8-D4D0-4C34-A961-FCA7D8EC26B7}

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gen_uri_delims", "..\srclib\apr-util\uri\gen_uri_delims.vcproj", "{315E11BD-4452-48FE-AF47-A39BF4EDD608}"

+	ProjectSection(ProjectDependencies) = postProject

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xml", "..\srclib\apr-util\xml\expat\lib\xml.vcproj", "{EF7D84CE-5F76-47C8-A198-B3EF72870920}"

+	ProjectSection(ProjectDependencies) = postProject

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "apriconv", "..\srclib\apr-iconv\apriconv.vcproj", "{DF70B25A-203A-47BB-857F-026F4F625A7D}"

+	ProjectSection(ProjectDependencies) = postProject

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libapriconv", "..\srclib\apr-iconv\libapriconv.vcproj", "{DDCAEED8-D4D0-4C34-A961-FCA7D8EC26B7}"

+	ProjectSection(ProjectDependencies) = postProject

+		{54D9CF8D-ED01-4E87-89C4-F06F9D757EED} = {54D9CF8D-ED01-4E87-89C4-F06F9D757EED}

+	EndProjectSection

+EndProject

+Global

+	GlobalSection(SolutionConfiguration) = preSolution

+		Debug = Debug

+		Release = Release

+	EndGlobalSection

+	GlobalSection(ProjectConfiguration) = postSolution

+		{398AC1EA-8B86-4746-B4F3-D174440703E0}.Debug.ActiveCfg = Debug|Win32

+		{398AC1EA-8B86-4746-B4F3-D174440703E0}.Debug.Build.0 = Debug|Win32

+		{398AC1EA-8B86-4746-B4F3-D174440703E0}.Release.ActiveCfg = Release|Win32

+		{398AC1EA-8B86-4746-B4F3-D174440703E0}.Release.Build.0 = Release|Win32

+		{54C317DF-BBE3-4F62-96CF-A6D7344EB615}.Debug.ActiveCfg = Debug|Win32

+		{54C317DF-BBE3-4F62-96CF-A6D7344EB615}.Debug.Build.0 = Debug|Win32

+		{54C317DF-BBE3-4F62-96CF-A6D7344EB615}.Release.ActiveCfg = Release|Win32

+		{54C317DF-BBE3-4F62-96CF-A6D7344EB615}.Release.Build.0 = Release|Win32

+		{54D9CF8D-ED01-4E87-89C4-F06F9D757EED}.Debug.ActiveCfg = Debug|Win32

+		{54D9CF8D-ED01-4E87-89C4-F06F9D757EED}.Debug.Build.0 = Debug|Win32

+		{54D9CF8D-ED01-4E87-89C4-F06F9D757EED}.Release.ActiveCfg = Release|Win32

+		{54D9CF8D-ED01-4E87-89C4-F06F9D757EED}.Release.Build.0 = Release|Win32

+		{CA59109B-88A7-4547-BDCE-9568AB08DD53}.Debug.ActiveCfg = Debug|Win32

+		{CA59109B-88A7-4547-BDCE-9568AB08DD53}.Debug.Build.0 = Debug|Win32

+		{CA59109B-88A7-4547-BDCE-9568AB08DD53}.Release.ActiveCfg = Release|Win32

+		{CA59109B-88A7-4547-BDCE-9568AB08DD53}.Release.Build.0 = Release|Win32

+		{A58AA747-485E-42FE-880F-15B9A498B75D}.Debug.ActiveCfg = Debug|Win32

+		{A58AA747-485E-42FE-880F-15B9A498B75D}.Debug.Build.0 = Debug|Win32

+		{A58AA747-485E-42FE-880F-15B9A498B75D}.Release.ActiveCfg = Release|Win32

+		{A58AA747-485E-42FE-880F-15B9A498B75D}.Release.Build.0 = Release|Win32

+		{315E11BD-4452-48FE-AF47-A39BF4EDD608}.Debug.ActiveCfg = Debug|Win32

+		{315E11BD-4452-48FE-AF47-A39BF4EDD608}.Debug.Build.0 = Debug|Win32

+		{315E11BD-4452-48FE-AF47-A39BF4EDD608}.Release.ActiveCfg = Release|Win32

+		{315E11BD-4452-48FE-AF47-A39BF4EDD608}.Release.Build.0 = Release|Win32

+		{EF7D84CE-5F76-47C8-A198-B3EF72870920}.Debug.ActiveCfg = Debug|Win32

+		{EF7D84CE-5F76-47C8-A198-B3EF72870920}.Debug.Build.0 = Debug|Win32

+		{EF7D84CE-5F76-47C8-A198-B3EF72870920}.Release.ActiveCfg = Release|Win32

+		{EF7D84CE-5F76-47C8-A198-B3EF72870920}.Release.Build.0 = Release|Win32

+		{DF70B25A-203A-47BB-857F-026F4F625A7D}.Debug.ActiveCfg = Debug|Win32

+		{DF70B25A-203A-47BB-857F-026F4F625A7D}.Debug.Build.0 = Debug|Win32

+		{DF70B25A-203A-47BB-857F-026F4F625A7D}.Release.ActiveCfg = Release|Win32

+		{DF70B25A-203A-47BB-857F-026F4F625A7D}.Release.Build.0 = Release|Win32

+		{DDCAEED8-D4D0-4C34-A961-FCA7D8EC26B7}.Debug.ActiveCfg = Debug|Win32

+		{DDCAEED8-D4D0-4C34-A961-FCA7D8EC26B7}.Debug.Build.0 = Debug|Win32

+		{DDCAEED8-D4D0-4C34-A961-FCA7D8EC26B7}.Release.ActiveCfg = Release|Win32

+		{DDCAEED8-D4D0-4C34-A961-FCA7D8EC26B7}.Release.Build.0 = Release|Win32

+	EndGlobalSection

+	GlobalSection(ExtensibilityGlobals) = postSolution

+	EndGlobalSection

+	GlobalSection(ExtensibilityAddIns) = postSolution

+	EndGlobalSection

+EndGlobal

diff --git a/connectors/ajp/ajplib/test/testajp.c b/connectors/ajp/ajplib/test/testajp.c
new file mode 100644
index 0000000..5e3df16
--- /dev/null
+++ b/connectors/ajp/ajplib/test/testajp.c
@@ -0,0 +1,283 @@
+/* Copyright 2000-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "apr.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "apr_buckets.h"
+#include "apr_md5.h"
+#include "apr_network_io.h"
+#include "apr_pools.h"
+#include "apr_strings.h"
+#include "apr_uri.h"
+#include "apr_date.h"
+#include "apr_fnmatch.h"
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+ 
+#include "apr_hooks.h"
+#include "apr_optional_hooks.h"
+#include "apr_buckets.h"
+
+#include "ajp.h"
+
+#if APR_HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if APR_HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#define TEST_POST_DATA "This document is a proposal of evolution of the current " \
+                       "Apache JServ Protocol version 1.3, also known as ajp13. " \
+                       "I'll not cover here the full protocol but only the add-on from ajp13. " \
+                       "This nth pass include comments from the tomcat-dev list and " \
+                       "misses discovered during developpment."
+
+#define TEST_CASE_URL "http://localhost/servlets-examples/servlet/RequestHeaderExample"
+
+/* Main process */
+static process_rec *main_process;
+/* Default server */
+static server_rec *main_server;
+
+/* This function is part of backend.
+ * The backend return connected socket and conn_rec
+ */
+static apr_status_t connect_to_backend(apr_socket_t **socket, conn_rec **con,
+                                       const char *host, apr_uint16_t port,
+                                       server_rec *server, apr_pool_t *pool)
+{
+    apr_status_t rv;
+    apr_sockaddr_t *remote_sa;
+    
+    if ((rv = apr_sockaddr_info_get(&remote_sa, host, APR_UNSPEC,
+                                    port, 0, pool)) != APR_SUCCESS)
+        return rv;
+    if ((rv = apr_socket_create(socket, remote_sa->family, SOCK_STREAM,
+#if (APR_MAJOR_VERSION > 0)
+                                APR_PROTO_TCP,
+#endif
+                                pool)) != APR_SUCCESS)
+        return rv;
+    if ((rv = apr_socket_timeout_set(*socket,
+                               apr_time_from_sec(3))) != APR_SUCCESS)  
+        return rv;
+    if ((rv = apr_socket_connect(*socket, remote_sa)) != APR_SUCCESS)
+        return rv;
+
+    if (!(*con = ap_run_create_connection(pool, server, *socket,
+                                          0, NULL, NULL)))
+        return APR_EGENERAL;
+
+    return APR_SUCCESS;
+}
+
+#if APR_HAS_THREADS
+
+#define TEST_THREAD_COUNT   10
+static apr_thread_t *threads[TEST_THREAD_COUNT];
+
+static void * APR_THREAD_FUNC thread_worker_func(apr_thread_t *thd, void *data)
+{
+    request_rec *r;
+    conn_rec *c = (conn_rec *)data;
+
+    /* Create an empty request */
+    if (!(r = ap_wrap_create_request(c)))
+        goto cleanup;
+
+    /* TODO: do something usefull */
+    apr_sleep(apr_time_from_sec(1));
+
+    /* Clean up the request */
+    apr_pool_destroy(r->pool);
+
+    apr_thread_exit(thd, APR_SUCCESS);
+    return NULL;
+cleanup:
+    apr_thread_exit(thd, APR_EGENERAL);
+    return NULL;
+}
+
+static int create_threads(server_rec *server)
+{
+    int i;
+    apr_status_t rv;
+
+    for (i = 0; i < TEST_THREAD_COUNT; i++) {
+        conn_rec *c;
+        /* Create a single client connection. The dummy one of course. */
+        if (!(c = ap_run_create_connection(server->process->pool, server,
+                                       NULL, 0, NULL, NULL)))
+            return -1;
+
+        if ((rv = apr_thread_create(&threads[i], NULL,
+                    thread_worker_func, (void *)c,
+                    server->process->pool)) != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_INFO, rv, NULL, "apr_create_thread failed");
+            return -1;
+        }
+    }
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "Created %d worker threads", i);
+    
+    return 0;
+}
+
+static int join_threads()
+{
+    int i, rc = 0;
+    apr_status_t rv;
+
+    for (i = 0; i < TEST_THREAD_COUNT; i++) {
+        apr_thread_join(&rv, threads[i]);
+        if (rv != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "Worker thread %d failed", i);
+            rc = -1;
+        }
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "All (%d) worker threads joined", i);
+    
+    return rc;
+}
+
+#endif
+
+int main(int argc, const char * const * argv, const char * const *env)
+{
+    int rv = 0;
+    apr_status_t rc;
+    conn_rec *c, *con;
+    request_rec *r;
+    apr_socket_t *sock;
+    ajp_msg_t *msg;
+    char *buf;
+    apr_size_t len;
+
+    apr_app_initialize(&argc, &argv, &env);
+
+    /* This is done in httpd.conf using LogLevel debug directive.
+     * We are setting this directly.
+     */
+    ap_default_loglevel = APLOG_DEBUG;
+
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "Testing ajp...");
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "Creating main server...");
+
+    main_process = ap_wrap_create_process(argc, argv);
+    /* Create the main server_rec */
+    main_server  = ap_wrap_create_server(main_process, main_process->pool);    
+    /* Create a single client connection. The dummy one of course. */
+    if (!(c = ap_run_create_connection(main_process->pool, main_server,
+                                       NULL, 0, NULL, NULL))) {
+        rv = -1;
+        goto finished;
+    }    
+    /* ... and a empty request */
+    if (!(r = ap_wrap_create_request(c))) {
+        rv = -1;
+        goto finished;
+    }
+
+    
+    /* 0. Fill in the request data          */
+    if (ap_wrap_make_request(r, TEST_CASE_URL,
+                             "POST",
+                             NULL, //"application/x-www-form-urlencoded",
+                             NULL,
+                             sizeof(TEST_POST_DATA) - 1,
+                             TEST_POST_DATA) != APR_SUCCESS) {
+        goto finished;
+    }
+    /*
+     * Up to here HTTPD created that for each request.
+     * From now on, we have a server_rec, conn_rec, and request_rec
+     * Who will ever need something else :)
+     */
+
+    /* 1. Obtain a connection to backend    */
+    if ((rc = connect_to_backend(&sock, &con, AJP13_DEF_HOST, AJP13_DEF_PORT,
+            main_server, r->pool)) != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rc, NULL, "connect_to_backend");
+        rv = -1;
+        goto finished;
+    }
+
+    /* 2. Create AJP message                */
+    if ((rc = ajp_send_header(sock, r)) != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rc, NULL, "ajp_send_header");
+        rv = -1;
+        goto finished;
+    }
+    /* 3. Send AJP message                  */
+
+    ajp_alloc_data_msg(r, &buf, &len, &msg);
+
+    /* Send the initial POST BODY */
+    if (ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                   "Error ajp_marshal_into_msgb - "
+                   "Can't setting client block");    
+    }
+    if (ap_should_client_block(r)) {
+        len = ap_get_client_block(r, buf, len);
+    }
+    else
+        len = ap_get_client_block(r, buf, len);
+
+
+    ajp_send_data_msg(sock, r, msg, len);
+
+    /* 4. Read AJP response                 */
+    if ((rc = ajp_read_header(sock, r, &msg)) != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rc, NULL, "ajp_read_header");
+        rv = -1;
+        goto finished;
+    }
+
+    ajp_msg_create(r->pool, &msg);
+    ajp_msg_reset(msg);
+    ajp_ilink_receive(sock, msg);
+
+    /* 5. Display results                   */
+#if 1
+    ajp_msg_dump(msg, "");
+#endif
+    {
+        apr_uint16_t blen;
+        ajp_parse_data(r, msg, &blen, &buf);
+        fputs(buf, stdout);
+    }
+    /* 6. Release the connection            */
+
+
+#if APR_HAS_THREADS_remove_this
+    /* or make the few requests in paralel 
+     * to test the threading.
+     * the upper 7 steps will go to thread_worker_func
+     */
+    if ((rv = create_threads(main_server)))
+        goto finished;
+    
+    if ((rv = join_threads()))
+        goto finished;
+#endif
+
+finished:
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "%s", rv == 0 ? "OK" : "FAILED");
+    apr_terminate();
+
+    return rv;
+}
diff --git a/connectors/ajp/ajplib/test/testajp.vcproj b/connectors/ajp/ajplib/test/testajp.vcproj
new file mode 100644
index 0000000..b7ee982
--- /dev/null
+++ b/connectors/ajp/ajplib/test/testajp.vcproj
@@ -0,0 +1,161 @@
+<?xml version="1.0" encoding="windows-1250"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="7.10"

+	Name="testajp"

+	ProjectGUID="{398AC1EA-8B86-4746-B4F3-D174440703E0}"

+	Keyword="Win32Proj">

+	<Platforms>

+		<Platform

+			Name="Win32"/>

+	</Platforms>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="Debug"

+			IntermediateDirectory="Debug"

+			ConfigurationType="1"

+			CharacterSet="2">

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="../srclib/apr/include;../srclib/apr-util/include"

+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;AJP_USE_HTTPD_WRAP"

+				MinimalRebuild="TRUE"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="3"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="TRUE"

+				DebugInformationFormat="4"/>

+			<Tool

+				Name="VCCustomBuildTool"/>

+			<Tool

+				Name="VCLinkerTool"

+				OutputFile="$(OutDir)/testajp.exe"

+				LinkIncremental="2"

+				GenerateDebugInformation="TRUE"

+				ProgramDatabaseFile="$(OutDir)/testajp.pdb"

+				SubSystem="1"

+				TargetMachine="1"/>

+			<Tool

+				Name="VCMIDLTool"/>

+			<Tool

+				Name="VCPostBuildEventTool"/>

+			<Tool

+				Name="VCPreBuildEventTool"/>

+			<Tool

+				Name="VCPreLinkEventTool"/>

+			<Tool

+				Name="VCResourceCompilerTool"/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"/>

+			<Tool

+				Name="VCWebDeploymentTool"/>

+			<Tool

+				Name="VCManagedWrapperGeneratorTool"/>

+			<Tool

+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="Release"

+			IntermediateDirectory="Release"

+			ConfigurationType="1"

+			CharacterSet="2">

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="../srclib/apr/include;../srclib/apr-util/include"

+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;AJP_USE_HTTPD_WRAP"

+				RuntimeLibrary="2"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="TRUE"

+				DebugInformationFormat="3"/>

+			<Tool

+				Name="VCCustomBuildTool"/>

+			<Tool

+				Name="VCLinkerTool"

+				OutputFile="$(OutDir)/testajp.exe"

+				LinkIncremental="1"

+				GenerateDebugInformation="TRUE"

+				SubSystem="1"

+				OptimizeReferences="2"

+				EnableCOMDATFolding="2"

+				TargetMachine="1"/>

+			<Tool

+				Name="VCMIDLTool"/>

+			<Tool

+				Name="VCPostBuildEventTool"/>

+			<Tool

+				Name="VCPreBuildEventTool"/>

+			<Tool

+				Name="VCPreLinkEventTool"/>

+			<Tool

+				Name="VCResourceCompilerTool"/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"/>

+			<Tool

+				Name="VCWebDeploymentTool"/>

+			<Tool

+				Name="VCManagedWrapperGeneratorTool"/>

+			<Tool

+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"

+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">

+			<File

+				RelativePath=".\ajp_header.c">

+			</File>

+			<File

+				RelativePath=".\ajp_link.c">

+			</File>

+			<File

+				RelativePath=".\ajp_logon.c">

+			</File>

+			<File

+				RelativePath=".\ajp_msg.c">

+			</File>

+			<File

+				RelativePath=".\httpd_wrap.c">

+			</File>

+			<File

+				RelativePath=".\testajp.c">

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl;inc;xsd"

+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">

+			<File

+				RelativePath=".\ajp.h">

+			</File>

+			<File

+				RelativePath=".\ajp_header.h">

+			</File>

+			<File

+				RelativePath=".\ajp_logon.h">

+			</File>

+			<File

+				RelativePath=".\httpd_wrap.h">

+			</File>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"

+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">

+		</Filter>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/connectors/ajp/proxy/.indent.pro b/connectors/ajp/proxy/.indent.pro
new file mode 100644
index 0000000..e2cd357
--- /dev/null
+++ b/connectors/ajp/proxy/.indent.pro
@@ -0,0 +1,58 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tapr_bucket_brigade
+-Tapr_pool_t
+-Tap_filter_t
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
+-Tproxy_server_conf
diff --git a/connectors/ajp/proxy/CHANGES b/connectors/ajp/proxy/CHANGES
new file mode 100644
index 0000000..cca99a9
--- /dev/null
+++ b/connectors/ajp/proxy/CHANGES
@@ -0,0 +1,223 @@
+******************************************
+* PLEASE NOTE: Now that development for  *
+* mod_proxy has been folded back into    *
+* the httpd-2.1 tree, this file has      *
+* been depreciated. Proxy changes should *
+* be noted in httpd-2.1's CHANGES file.  *
+* This file exists for historical        *
+* purposes.                              *
+******************************************
+
+mod_proxy changes for httpd 2.0.29-dev
+  *) don't do keepalives for sub-requests. [Ian Holsman]
+  
+  *) fix up proxypass handling [Ian Holsman]
+
+  *) don't send If-Modified-Since, Cache-Control, or If-None-Match on 
+     a subrequest [Ian Holsman]
+
+mod_proxy changes for httpd 2.0.26-dev
+  *) Add New option 'HTTPProxyOverrideReturnedErrors'. By Turning the
+     Flag on, you will mask the error pages returned by the proxied
+     server, and will it will be handled as if your server generated
+     the error. This change was put in so that a 404 on a included
+     r-proxied component will act in the same manner as a 404 on a 
+     included file. [Ian Holsman <ianh@cnet.com>]
+
+mod_proxy changes for httpd 2.0.25-dev
+
+  *) Split proxy: space using <Proxy[Match] > directive blocks from
+     the <Directory[Match] > and <Files[Match] > blocks.  Mod_proxy
+     now bypasses the directory and files testing phase (and skips 
+     the http TRACE default handler on it's own, as well).  Note that 
+     <Location > blocks continue to be processed for proxy: requests.
+     [William Rowe <wrowe@covalent.net>]
+
+  *) apr_uri type/function namespace changes in apr_uri functions
+     [Doug MacEachern <dougm@covalent.net>]
+
+mod_proxy changes for httpd 2.0.23-dev
+
+  *) break the proxy_http_handler into multiple smaller functions.
+     [John Barbee <barbee@veribox.net>]
+
+  *) Fix the proxy when the origin server sends back a 100
+     Continue response.  [John Barbee <barbee@veribox.net>]
+
+  *) Change 'readbytes' from apr_size_t to apr_off_t due to change
+     in ap_get_brigade's parameters [John Barbee <barbee@veribox.net>]
+
+mod_proxy changes for httpd 2.0.20-dev
+  *) Timeout added for backend connections.
+     [Victor Orlikowski <v.j.orlikowski@gte.net>]
+
+  *) Fix abort code path in proxy_http.c, similar to FTP fix.
+     [Chuck Murcko <chuck@topsail.org>]
+
+  *) Fix FTP ABOR command execution path.
+     [Victor Orlikowski <v.j.orlikowski@gte.net>]
+
+  *) FTP return code variable cleanup; fixed problem in login
+     [Chuck Murcko <chuck@topsail.org>]
+
+  *) Get PORT working again in the ftp proxy.
+     [Victor Orlikowski <v.j.orlikowski@gte.net>]
+
+  *) Return result code check for FTP QUIT, after fixing
+     problems with passive connection handling.
+     [Victor Orlikowski <v.j.orlikowski@gte.net>]
+
+  *) Reorganize ap_proxy_string_read() internally to not process eos
+     buckets.
+     [Chuck Murcko <chuck@topsail.org>]
+     [Victor Orlikowski <v.j.orlikowski@gte.net>]
+
+  *) Remove result code check for FTP QUIT command. Some servers send
+     nothing at all back in response to QUIT.
+     [Chuck Murcko <chuck@topsail.org>]
+     [Victor Orlikowski <v.j.orlikowski@gte.net>]
+
+mod_proxy changes for httpd 2.0.19
+
+  *) Reverse previous patch since the core reverted.
+     [Chuck Murcko <chuck@topsail.org>]
+
+  *) Remove indirection on number of bytes to read for input filters.
+     [Chuck Murcko <chuck@topsail.org>]
+
+  *) Fixed a problem with directory listing corruption in the
+     PROXY_DIR filter.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) mod_proxy and the proxy submodules now build properly as DSOs.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Stopped the HTTP proxy from trying to read entity bodies when there
+     wasn't one (response was 1xx, 204, 205 or 304).
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Made sure dates were canonicalised correctly when passed to the client
+     browser through the HTTP proxy.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Split each individual proxy protocol into separate modules.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Added Max-Forwards support for all request types so as to prevent
+     loops.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Fix warnings about byte count type on Darwin (connect handler).
+     [Chuck Murcko <chuck@topsail.org>]
+
+mod_proxy changes for httpd 2.0.18
+
+  *) IPV6 EPSV support for IPV6 in FTP proxy.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) FTP directory filter works now.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Fixed some thread-safety issues with the HTTP proxy in mod_proxy.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) PASV FTP works now.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Reworked the line-at-a-time read from the control connection to
+     workaround a stray empty bucket returned by the HTTP_IN filter.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Stopped the CORE filter from sending off an HTTP response when a
+     CONNECT tunnel was closed.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Fixed the poll() loop in proxy_connect.c -> it works now!!!
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Converted send_dir() to ap_proxy_send_dir_filter() in proxy_ftp.c.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+mod_proxy changes for httpd 2.0.17
+
+  *) Major rework of ap_proxy_ftp_handler() to use filters (begone foul
+     BUFF!!!). It compiles, but is untested, and the build environment needs
+     to be fixed to include proxy_ftp.c.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Cleanup of dead functions within proxy_util.c.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Reworked the storage of the client socket between keepalive connections
+     to fix some nasty problems with the socket lasting longer than the
+     memory pool it was allocated from.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Fixed bug where a hostname without a "." in it (such as "localhost")
+     would not trigger an IP address check with ProxyBlock.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+mod_proxy changes for httpd 2.0.16
+
+  *) Fixed ProxyBlock bugs with ap_proxy_http_handler() and
+     ap_proxy_connect_handler().
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Updated ap_proxy_connect_handler() to support APR, while
+     moving some common code between http_handler and connect_handler
+     to proxy_util.c.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Updated mod_proxy.html docs to include v2.0 configuration.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Fixed problem where responses without entity bodies would cause
+     the directly following proxy keepalive request to fail.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+mod_proxy changes for httpd 2.0.15
+
+  *) Added support for downstream keepalives in mod_proxy.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Changed mod_proxy ap_proxy_http_handler() to support APR properly.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Fix problem where incoming response headers were not being returned
+     to the client in mod_proxy.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Added X-Forwarded-For, X-Forwarded-Host and X-Forwarded-Server to
+     reverse proxied request headers in mod_proxy.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) replace INADDR_NONE with APR_INADDR_NONE [Ian Holsman <IanH@cnet.com>]
+
+  *) Fix problem with proxy configuration where globally set
+     configuration options were overridden inside virtual hosts.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Fix ProxyReceiveBufferSize where default value was left
+     uninitialised.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+  *) Some small changes:
+     - Ensured hop-by-hop headers were stripped as per
+       RFC2616 13.5.1.
+     - Upgraded version code to HTTP/1.1.
+     - Added Connection: close until Keepalives come.
+     - Some cosmetic fixes and commenting.
+     [Graham Leggett <minfrin@sharp.fm>]
+
+mod_proxy changes for httpd 2.0.14
+
+  *) removed ProxyNoCache and ProxyCacheForceCompletion config directives,
+     since we no longer directly cache from this module
+     [Chuck Murcko <chuck@topsail.org>]
+
+  *) removed cache
+     [Chuck Murcko <chuck@topsail.org>]
+
+  *) initial rerebuild for 2.0
+     [Chuck Murcko <chuck@topsail.org>]
+
diff --git a/connectors/ajp/proxy/JAKARTA b/connectors/ajp/proxy/JAKARTA
new file mode 100644
index 0000000..26198e4
--- /dev/null
+++ b/connectors/ajp/proxy/JAKARTA
@@ -0,0 +1,16 @@
+JAKARTA TOMCAT CONNECTORS AJP CHANGELOG FOR MOD_PROXY:  -*-text-*-
+Last modified at [$Date$]
+
+Changes in AJP HEAD:
+    * proxy_util.c: Enable compiling on 2.0-HEAD
+      Use apr_socket_create_ex for 0.9.x
+      [Mladen Turk]
+    * mod_proxy.c: Enable compiling on 2.0-HEAD
+      mod_ssl is not included in 2.0-HEAD
+      [Mladen Turk]
+    * mod_proxy.h: Change the proxy_module declaration so that we
+      can compile on WIN32
+      [Mladen Turk]      
+    * Imported proxy sources from 2.1-HEAD
+      Date: 2004/08/03 10:05:52
+      [Mladen Turk] 
diff --git a/connectors/ajp/proxy/Makefile.in b/connectors/ajp/proxy/Makefile.in
new file mode 100644
index 0000000..7c5c149
--- /dev/null
+++ b/connectors/ajp/proxy/Makefile.in
@@ -0,0 +1,3 @@
+# a modules Makefile has no explicit targets -- they will be defined by
+# whatever modules are enabled. just grab special.mk to deal with this.
+include $(top_srcdir)/build/special.mk
diff --git a/connectors/ajp/proxy/NWGNUmakefile b/connectors/ajp/proxy/NWGNUmakefile
new file mode 100644
index 0000000..61842f0
--- /dev/null
+++ b/connectors/ajp/proxy/NWGNUmakefile
@@ -0,0 +1,247 @@
+#
+# Declare the sub-directories to be built here
+#
+
+SUBDIRS = \
+	$(EOLIST) 
+
+#
+# Get the 'head' of the build environment.  This includes default targets and
+# paths to tools
+#
+
+include $(AP_WORK)\build\NWGNUhead.inc
+
+#
+# build this level's files
+
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS	+= \
+			$(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS		+= \
+			$(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES	+= \
+			$(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS		+= \
+			$(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+		   	$(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+		   	$(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+			$(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm.  If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME		=
+
+#
+# This is used by the link '-desc ' directive. 
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION	=
+
+#
+# This is used by the '-threadname' directive.  If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME	=
+
+#
+# If this is specified, it will override VERSION value in 
+# $(AP_WORK)\build\NWGNUenvironment.inc
+#
+NLM_VERSION		=
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE	=
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM	=
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM	=
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM	=
+
+#
+# If these are specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS		=
+
+#
+# If this is specified it will be linked in with the XDCData option in the def 
+# file instead of the default of $(NWOS)/apache.xdc.  XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA         = 
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+	$(OBJDIR)/proxy.nlm \
+	$(OBJDIR)/proxycon.nlm \
+	$(OBJDIR)/proxyftp.nlm \
+	$(OBJDIR)/proxyhtp.nlm \
+	$(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+	$(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+	$(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+	$(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+	$(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+ 
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+	$(EOLIST)
+ 
+#   
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+	$(EOLIST)
+	
+#   
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+		$(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the 
+# correct place.  (See $(AP_WORK)\build\NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+	copy $(OBJDIR)\*.nlm $(INSTALL)\Apache2\modules\*.*
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(AP_WORK)\build\NWGNUtail.inc
+	
diff --git a/connectors/ajp/proxy/NWGNUproxy b/connectors/ajp/proxy/NWGNUproxy
new file mode 100644
index 0000000..d662c03
--- /dev/null
+++ b/connectors/ajp/proxy/NWGNUproxy
@@ -0,0 +1,262 @@
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# Get the 'head' of the build environment if necessary.  This includes default
+# targets and paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(AP_WORK)\build\NWGNUhead.inc
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS	+= \
+			$(AP_WORK)/include \
+			$(NWOS) \
+			$(AP_WORK)/modules/http \
+			$(AP_WORK)/modules/arch/netware \
+			$(AP_WORK)/modules/ssl \
+			$(AP_WORK)/srclib/apr/include \
+			$(AP_WORK)/srclib/apr-util/include \
+			$(AP_WORK)/srclib/apr \
+			$(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS		+= \
+			-prefix pre_nw.h \
+			$(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES	+= \
+			$(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS		+= \
+			$(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+		   	$(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+		   	$(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+			$(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm.  If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME		= proxy
+
+#
+# This is used by the link '-desc ' directive. 
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION	= Apache $(VERSION_STR) Proxy Module
+
+#
+# This is used by the '-threadname' directive.  If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME	= Proxy Module
+
+#
+# If this is specified, it will override VERSION value in 
+# $(AP_WORK)\build\NWGNUenvironment.inc
+#
+NLM_VERSION		=
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE	= 8192
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM	= _LibCPrelude
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM	= _LibCPostlude
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM	=
+
+#
+# If these are specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS		=  AUTOUNLOAD, PSEUDOPREEMPTION
+
+#
+# If this is specified it will be linked in with the XDCData option in the def 
+# file instead of the default of $(NWOS)/apache.xdc.  XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA         = 
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+	$(OBJDIR)/proxy.nlm \
+	$(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+	$(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+	$(OBJDIR)/mod_proxy.o \
+	$(OBJDIR)/proxy_util.o \
+	$(OBJDIR)/libprews.o \
+	$(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+   	libcpre.o \
+	$(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+	aprlib \
+	libc \
+	$(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+ 
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+	@$(APR)/aprlib.imp \
+	@$(NWOS)/httpd.imp \
+	@libc.imp \
+	@ws2nlm.imp \
+	$(EOLIST)
+ 
+#   
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+	proxy_module \
+	proxy_hook_scheme_handler \
+	proxy_hook_canon_handler \
+	ap_proxy_ssl_enable \
+	ap_proxy_ssl_disable \
+	proxy_run_fixups \
+	$(EOLIST)
+	
+#   
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+		$(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the 
+# correct place.  (See $(AP_WORK)\build\NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+vpath %.c ../arch/netware
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(AP_WORK)\build\NWGNUtail.inc
+
diff --git a/connectors/ajp/proxy/NWGNUproxycon b/connectors/ajp/proxy/NWGNUproxycon
new file mode 100644
index 0000000..241ca3f
--- /dev/null
+++ b/connectors/ajp/proxy/NWGNUproxycon
@@ -0,0 +1,256 @@
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# Get the 'head' of the build environment if necessary.  This includes default
+# targets and paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(AP_WORK)\build\NWGNUhead.inc
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS	+= \
+			$(AP_WORK)/include \
+			$(NWOS) \
+			$(AP_WORK)/modules/http \
+			$(AP_WORK)/modules/arch/netware \
+			$(AP_WORK)/srclib/apr/include \
+			$(AP_WORK)/srclib/apr-util/include \
+			$(AP_WORK)/srclib/apr \
+			$(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS		+= \
+			-prefix pre_nw.h \
+			$(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES	+= \
+			$(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS		+= \
+			$(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+		   	$(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+		   	$(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+			$(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm.  If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME		= proxycon
+
+#
+# This is used by the link '-desc ' directive. 
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION	= Apache $(VERSION_STR) Proxy Connection Sub-Module
+
+#
+# This is used by the '-threadname' directive.  If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME	= Proxy Conn Module
+
+#
+# If this is specified, it will override VERSION value in 
+# $(AP_WORK)\build\NWGNUenvironment.inc
+#
+NLM_VERSION		=
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE	= 8192
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM	= _LibCPrelude
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM	= _LibCPostlude
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM	=
+
+#
+# If these are specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS		=  AUTOUNLOAD, PSEUDOPREEMPTION
+
+#
+# If this is specified it will be linked in with the XDCData option in the def 
+# file instead of the default of $(NWOS)/apache.xdc.  XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA         = 
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+	$(OBJDIR)/proxycon.nlm \
+	$(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+	$(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+	$(OBJDIR)/proxy_connect.o \
+	$(OBJDIR)/proxy_util.o \
+	$(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+   	libcpre.o \
+	$(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+	aprlib \
+	libc \
+	proxy \
+	$(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+ 
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+	@$(APR)/aprlib.imp \
+	@$(NWOS)/httpd.imp \
+	@libc.imp \
+	proxy_module \
+	proxy_hook_scheme_handler \
+	proxy_hook_canon_handler \
+	$(EOLIST)
+ 
+#   
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+	proxy_connect_module \
+	$(EOLIST)
+	
+#   
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+		$(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the 
+# correct place.  (See $(AP_WORK)\build\NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(AP_WORK)\build\NWGNUtail.inc
+
diff --git a/connectors/ajp/proxy/NWGNUproxyftp b/connectors/ajp/proxy/NWGNUproxyftp
new file mode 100644
index 0000000..f608b22
--- /dev/null
+++ b/connectors/ajp/proxy/NWGNUproxyftp
@@ -0,0 +1,260 @@
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# Get the 'head' of the build environment if necessary.  This includes default
+# targets and paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(AP_WORK)\build\NWGNUhead.inc
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS	+= \
+			$(AP_WORK)/include \
+			$(NWOS) \
+			$(AP_WORK)/modules/http \
+			$(AP_WORK)/modules/arch/netware \
+			$(AP_WORK)/srclib/apr/include \
+			$(AP_WORK)/srclib/apr-util/include \
+			$(AP_WORK)/srclib/apr \
+			$(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS		+= \
+			-prefix pre_nw.h \
+			$(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES	+= \
+			$(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS		+= \
+			$(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+		   	$(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+		   	$(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+			$(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm.  If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME		= proxyftp
+
+#
+# This is used by the link '-desc ' directive. 
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION	= Apache $(VERSION_STR) Proxy FTP Sub-Module
+
+#
+# This is used by the '-threadname' directive.  If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME	= Proxy FTP Module
+
+#
+# If this is specified, it will override VERSION value in 
+# $(AP_WORK)\build\NWGNUenvironment.inc
+#
+NLM_VERSION		=
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE	= 8192
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM	= _LibCPrelude
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM	= _LibCPostlude
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM	=
+
+#
+# If these are specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS		=  AUTOUNLOAD, PSEUDOPREEMPTION
+
+#
+# If this is specified it will be linked in with the XDCData option in the def 
+# file instead of the default of $(NWOS)/apache.xdc.  XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA         = 
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+	$(OBJDIR)/proxyftp.nlm \
+	$(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+	$(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+	$(OBJDIR)/proxy_ftp.o \
+	$(OBJDIR)/proxy_util.o \
+	$(OBJDIR)/libprews.o \
+	$(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+   	libcpre.o \
+	$(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+	aprlib \
+	libc \
+	proxy \
+	$(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+ 
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+	@$(APR)/aprlib.imp \
+	@$(NWOS)/httpd.imp \
+	@libc.imp \
+	@ws2nlm.imp \
+	proxy_module \
+	proxy_hook_scheme_handler \
+	proxy_hook_canon_handler \
+	$(EOLIST)
+ 
+#   
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+	proxy_ftp_module \
+	$(EOLIST)
+	
+#   
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+		$(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the 
+# correct place.  (See $(AP_WORK)\build\NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+vpath %.c ../arch/netware
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(AP_WORK)\build\NWGNUtail.inc
+
diff --git a/connectors/ajp/proxy/NWGNUproxyhtp b/connectors/ajp/proxy/NWGNUproxyhtp
new file mode 100644
index 0000000..f605a03
--- /dev/null
+++ b/connectors/ajp/proxy/NWGNUproxyhtp
@@ -0,0 +1,263 @@
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# Get the 'head' of the build environment if necessary.  This includes default
+# targets and paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(AP_WORK)\build\NWGNUhead.inc
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS	+= \
+			$(AP_WORK)/include \
+			$(NWOS) \
+			$(AP_WORK)/modules/http \
+			$(AP_WORK)/modules/arch/netware \
+			$(AP_WORK)/srclib/apr/include \
+			$(AP_WORK)/srclib/apr-util/include \
+			$(AP_WORK)/srclib/apr \
+			$(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS		+= \
+			-prefix pre_nw.h \
+			$(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES	+= \
+			$(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS		+= \
+			$(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+		   	$(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+		   	$(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+			$(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm.  If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME		= proxyhtp
+
+#
+# This is used by the link '-desc ' directive. 
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION	= Apache $(VERSION_STR) Proxy HTTP Sub-Module
+
+#
+# This is used by the '-threadname' directive.  If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME	= Proxy HTTP Module
+
+#
+# If this is specified, it will override VERSION value in 
+# $(AP_WORK)\build\NWGNUenvironment.inc
+#
+NLM_VERSION		=
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE	= 8192
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM	= _LibCPrelude
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM	= _LibCPostlude
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM	=
+
+#
+# If these are specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS		=  AUTOUNLOAD, PSEUDOPREEMPTION
+
+#
+# If this is specified it will be linked in with the XDCData option in the def 
+# file instead of the default of $(NWOS)/apache.xdc.  XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA         = 
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+	$(OBJDIR)/proxyhtp.nlm \
+	$(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+	$(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+	$(OBJDIR)/proxy_http.o \
+	$(OBJDIR)/proxy_util.o \
+	$(OBJDIR)/libprews.o \
+	$(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+   	libcpre.o \
+	$(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+	aprlib \
+	libc \
+	proxy \
+	$(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+ 
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+	@$(APR)/aprlib.imp \
+	@$(NWOS)/httpd.imp \
+	@libc.imp \
+	@ws2nlm.imp \
+	proxy_module \
+	proxy_hook_scheme_handler \
+	proxy_hook_canon_handler \
+	proxy_run_fixups \
+	ap_proxy_ssl_enable \
+	ap_proxy_ssl_disable \
+	$(EOLIST)
+ 
+#   
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+	proxy_http_module \
+	$(EOLIST)
+	
+#   
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+		$(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the 
+# correct place.  (See $(AP_WORK)\build\NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+vpath %.c ../arch/netware
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(AP_WORK)\build\NWGNUtail.inc
+
diff --git a/connectors/ajp/proxy/config.m4 b/connectors/ajp/proxy/config.m4
new file mode 100644
index 0000000..d40bcb3
--- /dev/null
+++ b/connectors/ajp/proxy/config.m4
@@ -0,0 +1,40 @@
+dnl modules enabled in this directory by default
+
+APACHE_MODPATH_INIT(proxy)
+
+if test "$enable_proxy" = "shared"; then
+  proxy_mods_enable=shared
+elif test "$enable_proxy" = "yes"; then
+  proxy_mods_enable=yes
+else
+  proxy_mods_enable=no
+fi
+
+proxy_objs="mod_proxy.lo proxy_util.lo"
+APACHE_MODULE(proxy, Apache proxy module, $proxy_objs, , $proxy_mods_enable)
+
+proxy_connect_objs="proxy_connect.lo"
+proxy_ftp_objs="proxy_ftp.lo"
+proxy_http_objs="proxy_http.lo"
+proxy_ajp_objs="proxy_ajp.lo ajp_header.lo ajp_link.lo ajp_msg.lo"
+proxy_balancer_objs="proxy_balancer.lo"
+
+case "$host" in
+  *os2*)
+    # OS/2 DLLs must resolve all symbols at build time and
+    # these sub-modules need some from the main proxy module
+    proxy_connect_objs="$proxy_connect_objs mod_proxy.la"
+    proxy_ftp_objs="$proxy_ftp_objs mod_proxy.la"
+    proxy_http_objs="$proxy_http_objs mod_proxy.la"
+    proxy_balancer_objs="$proxy_balancer_objs mod_proxy.la"
+    ;;
+esac
+
+APR_ADDTO(INCLUDES, [-I../generators])
+APACHE_MODULE(proxy_connect, Apache proxy CONNECT module, $proxy_connect_objs, , $proxy_mods_enable)
+APACHE_MODULE(proxy_ftp, Apache proxy FTP module, $proxy_ftp_objs, , $proxy_mods_enable)
+APACHE_MODULE(proxy_http, Apache proxy HTTP module, $proxy_http_objs, , $proxy_mods_enable)
+APACHE_MODULE(proxy_balancer, Apache proxy BALANCER module, $proxy_balancer_objs, , $proxy_mods_enable)
+APR_ADDTO(INCLUDES, [-I./ajp])
+APACHE_MODULE(proxy_ajp, Apache proxy AJP module, $proxy_ajp_objs, , $proxy_mods_enable)
+APACHE_MODPATH_FINISH
diff --git a/connectors/ajp/proxy/config.m4.patch b/connectors/ajp/proxy/config.m4.patch
new file mode 100644
index 0000000..ab09972
--- /dev/null
+++ b/connectors/ajp/proxy/config.m4.patch
@@ -0,0 +1,23 @@
+Index: config.m4
+===================================================================
+RCS file: /home/cvspublic/httpd-2.0/modules/proxy/config.m4,v
+retrieving revision 1.17
+diff -u -r1.17 config.m4
+--- config.m4	25 Apr 2002 03:16:44 -0000	1.17
++++ config.m4	1 Aug 2004 14:07:06 -0000
+@@ -16,6 +16,7 @@
+ proxy_connect_objs="proxy_connect.lo"
+ proxy_ftp_objs="proxy_ftp.lo"
+ proxy_http_objs="proxy_http.lo"
++proxy_ajp_objs="proxy_ajp.lo ajp_header.lo ajp_link.lo ajp_msg.lo"
+ 
+ case "$host" in
+   *os2*)
+@@ -30,5 +31,6 @@
+ APACHE_MODULE(proxy_connect, Apache proxy CONNECT module, $proxy_connect_objs, , $proxy_mods_enable)
+ APACHE_MODULE(proxy_ftp, Apache proxy FTP module, $proxy_ftp_objs, , $proxy_mods_enable)
+ APACHE_MODULE(proxy_http, Apache proxy HTTP module, $proxy_http_objs, , $proxy_mods_enable)
+-
++APR_ADDTO(INCLUDES, [-I./ajp])
++APACHE_MODULE(proxy_ajp, Apache proxy AJP module, $proxy_ajp_objs, , $proxy_mods_enable)
+ APACHE_MODPATH_FINISH
diff --git a/connectors/ajp/proxy/libproxy.exp b/connectors/ajp/proxy/libproxy.exp
new file mode 100644
index 0000000..a20f237
--- /dev/null
+++ b/connectors/ajp/proxy/libproxy.exp
@@ -0,0 +1 @@
+proxy_module
diff --git a/connectors/ajp/proxy/mod_proxy.c b/connectors/ajp/proxy/mod_proxy.c
new file mode 100644
index 0000000..3069366
--- /dev/null
+++ b/connectors/ajp/proxy/mod_proxy.c
@@ -0,0 +1,1719 @@
+#define FIX_15207
+/* Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define CORE_PRIVATE
+
+#include "mod_proxy.h"
+#include "mod_core.h"
+#include "apr_optional.h"
+#include "mod_status.h"
+
+#if (MODULE_MAGIC_NUMBER_MAJOR > 20020903)
+#include "mod_ssl.h"
+#else
+APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *));
+APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
+#endif
+
+#ifndef MAX
+#define MAX(x,y) ((x) >= (y) ? (x) : (y))
+#endif
+
+/*
+ * A Web proxy module. Stages:
+ *
+ *  translate_name: set filename to proxy:<URL>
+ *  map_to_storage: run proxy_walk (rather than directory_walk/file_walk)
+ *                  can't trust directory_walk/file_walk since these are
+ *                  not in our filesystem.  Prevents mod_http from serving
+ *                  the TRACE request we will set aside to handle later.
+ *  type_checker:   set type to PROXY_MAGIC_TYPE if filename begins proxy:
+ *  fix_ups:        convert the URL stored in the filename to the
+ *                  canonical form.
+ *  handler:        handle proxy requests
+ */
+
+/* -------------------------------------------------------------- */
+/* Translate the URL into a 'filename' */
+
+#ifdef FIX_15207
+/* XXX: EBCDIC safe? --nd */
+#define x2c(x) (((x >= '0') && (x <= '9'))         \
+                   ? (x - '0')                     \
+                   : (((x >= 'a') && (x <= 'f'))   \
+                       ? (10 + x - 'a')            \
+                       : ((x >= 'A') && (x <='F')) \
+                           ? (10 + x - 'A')        \
+                           : 0                     \
+                     )                             \
+               )
+
+static unsigned char hex2c(const char* p) {
+  const char c1 = p[1];
+  const char c2 = p[1] ? p[2]: '\0';
+  int i1 = c1 ? x2c(c1) : 0;
+  int i2 = c2 ? x2c(c2) : 0;
+  unsigned char ret = (i1 << 4) | i2;
+
+  return ret;
+}
+#endif
+
+#define PROXY_COPY_CONF_PARAMS(w, c) \
+    do {                             \
+        (w)->timeout              = (c)->timeout;               \
+        (w)->timeout_set          = (c)->timeout_set;           \
+        (w)->recv_buffer_size     = (c)->recv_buffer_size;      \
+        (w)->recv_buffer_size_set = (c)->recv_buffer_size_set;  \
+        (w)->io_buffer_size       = (c)->io_buffer_size;        \
+        (w)->io_buffer_size_set   = (c)->io_buffer_size_set;    \
+    } while (0)
+
+static const char *set_worker_param(apr_pool_t *p,
+                                    proxy_worker *worker,
+                                    const char *key,
+                                    const char *val)
+{
+
+    int ival;
+    if (!strcasecmp(key, "loadfactor")) {
+        worker->lbfactor = atoi(val);
+        if (worker->lbfactor < 1 || worker->lbfactor > 100)
+            return "LoadFactor must be number between 1..100";
+    }
+    else if (!strcasecmp(key, "retry")) {
+        ival = atoi(val);
+        if (ival < 1)
+            return "Retry must be at least one second";
+        worker->retry = apr_time_from_sec(ival);
+    }
+    else if (!strcasecmp(key, "ttl")) {
+        ival = atoi(val);
+        if (ival < 1)
+            return "TTL must be at least one second";
+        worker->ttl = apr_time_from_sec(ival);
+    }
+    else if (!strcasecmp(key, "min")) {
+        ival = atoi(val);
+        if (ival < 0)
+            return "Min must be a positive number";
+        worker->min = ival;
+    }
+    else if (!strcasecmp(key, "max")) {
+        ival = atoi(val);
+        if (ival < 0)
+            return "Max must be a positive number";
+        worker->hmax = ival;
+    }
+    /* XXX: More inteligent naming needed */
+    else if (!strcasecmp(key, "smax")) {
+        ival = atoi(val);
+        if (ival < 0)
+            return "Smax must be a positive number";
+        worker->smax = ival;
+    }
+    else if (!strcasecmp(key, "acquire")) {
+        ival = atoi(val);
+        if (ival < 1)
+            return "Acquire must be at least one mili second";
+        worker->acquire = apr_time_make(0, ival * 1000);
+        worker->acquire_set = 1;
+    }
+    else if (!strcasecmp(key, "timeout")) {
+        ival = atoi(val);
+        if (ival < 1)
+            return "Timeout must be at least one second";
+        worker->timeout = apr_time_from_sec(ival);
+        worker->timeout_set = 1;
+    }
+    else if (!strcasecmp(key, "iobuffersize")) {
+        long s = atol(val);
+        worker->io_buffer_size = ((s > AP_IOBUFSIZE) ? s : AP_IOBUFSIZE);
+        worker->io_buffer_size_set = 1;
+    }
+    else if (!strcasecmp(key, "receivebuffersize")) {
+        ival = atoi(val);
+        if (ival < 512 && ival != 0) {
+            return "ReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
+        }
+        worker->recv_buffer_size = ival;
+        worker->recv_buffer_size_set = 1;
+    }
+    else if (!strcasecmp(key, "keepalive")) {
+        if (!strcasecmp(val, "on"))
+            worker->keepalive = 1;
+        else if (!strcasecmp(val, "off"))
+            worker->keepalive = 0;
+        else
+            return "KeepAlive must be On|Off";
+        worker->keepalive_set = 1;
+    }    
+    else if (!strcasecmp(key, "route")) {
+        worker->route = apr_pstrdup(p, val);
+    }
+    else if (!strcasecmp(key, "redirect")) {
+        worker->redirect = apr_pstrdup(p, val);
+    }
+    else {
+        return "unknown parameter";
+    }
+    return NULL;
+}
+
+static const char *set_balancer_param(apr_pool_t *p,
+                                      proxy_balancer *balancer,
+                                      const char *key,
+                                      const char *val)
+{
+
+    int ival;
+    if (!strcasecmp(key, "stickysession")) {
+        balancer->sticky = apr_pstrdup(p, val);
+    }
+    else if (!strcasecmp(key, "nofailover")) {
+        if (!strcasecmp(val, "on"))
+            balancer->sticky_force = 1;
+        else if (!strcasecmp(val, "off"))
+            balancer->sticky_force = 0;
+        else
+            return "failover must be On|Off";
+    }
+    else if (!strcasecmp(key, "timeout")) {
+        ival = atoi(val);
+        if (ival < 1)
+            return "timeout must be at least one second";
+        balancer->timeout = apr_time_from_sec(ival);
+    }
+    else {
+        return "unknown parameter";
+    }
+    return NULL;
+}
+
+static int alias_match(const char *uri, const char *alias_fakename)
+{
+    const char *end_fakename = alias_fakename + strlen(alias_fakename);
+    const char *aliasp = alias_fakename, *urip = uri;
+    const char *end_uri = uri + strlen(uri);
+    unsigned char uric, aliasc;
+
+    while (aliasp < end_fakename && urip < end_uri) {
+        if (*aliasp == '/') {
+            /* any number of '/' in the alias matches any number in
+             * the supplied URI, but there must be at least one...
+             */
+            if (*urip != '/')
+                return 0;
+
+            while (*aliasp == '/')
+                ++aliasp;
+            while (*urip == '/')
+                ++urip;
+        }
+        else {
+#ifndef FIX_15207
+            /* Other characters are compared literally */
+            if (*urip++ != *aliasp++)
+                return 0;
+#else
+            /* Other characters are canonicalised and compared literally */
+            if (*urip == '%') {
+                uric = hex2c(urip);
+                urip += 3;
+            } else {
+                uric = (unsigned char)*urip++;
+            }
+            if (*aliasp == '%') {
+                aliasc = hex2c(aliasp);
+                aliasp += 3;
+            } else {
+                aliasc = (unsigned char)*aliasp++;
+            }
+            if (uric != aliasc) {
+                return 0;
+            }
+#endif
+        }
+    }
+
+    /* fixup badly encoded stuff (e.g. % as last character) */
+    if (aliasp > end_fakename) {
+        aliasp = end_fakename;
+    }
+    if (urip > end_uri) {
+        urip = end_uri;
+    }
+
+    /* Check last alias path component matched all the way */
+    if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
+        return 0;
+
+    /* Return number of characters from URI which matched (may be
+     * greater than length of alias, since we may have matched
+     * doubled slashes)
+     */
+
+    return urip - uri;
+}
+
+/* Detect if an absoluteURI should be proxied or not.  Note that we
+ * have to do this during this phase because later phases are
+ * "short-circuiting"... i.e. translate_names will end when the first
+ * module returns OK.  So for example, if the request is something like:
+ *
+ * GET http://othervhost/cgi-bin/printenv HTTP/1.0
+ *
+ * mod_alias will notice the /cgi-bin part and ScriptAlias it and
+ * short-circuit the proxy... just because of the ordering in the
+ * configuration file.
+ */
+static int proxy_detect(request_rec *r)
+{
+    void *sconf = r->server->module_config;
+    proxy_server_conf *conf =
+        (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
+#ifdef FIX_15207
+    int i, len;
+    struct proxy_alias *ent = (struct proxy_alias *)conf->aliases->elts;
+#endif
+
+    /* Ick... msvc (perhaps others) promotes ternary short results to int */
+
+    if (conf->req && r->parsed_uri.scheme) {
+        /* but it might be something vhosted */
+        if (!(r->parsed_uri.hostname
+              && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))
+              && ap_matches_request_vhost(r, r->parsed_uri.hostname,
+                                          (apr_port_t)(r->parsed_uri.port_str ? r->parsed_uri.port 
+                                                       : ap_default_port(r))))) {
+            r->proxyreq = PROXYREQ_PROXY;
+            r->uri = r->unparsed_uri;
+            r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
+            r->handler = "proxy-server";
+        }
+    }
+    /* We need special treatment for CONNECT proxying: it has no scheme part */
+    else if (conf->req && r->method_number == M_CONNECT
+             && r->parsed_uri.hostname
+             && r->parsed_uri.port_str) {
+        r->proxyreq = PROXYREQ_PROXY;
+        r->uri = r->unparsed_uri;
+        r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
+        r->handler = "proxy-server";
+#ifdef FIX_15207
+    } else {
+        /* test for a ProxyPass */
+        for (i = 0; i < conf->aliases->nelts; i++) {
+            len = alias_match(r->unparsed_uri, ent[i].fake);
+            if (len > 0) {
+                r->filename = apr_pstrcat(r->pool, "proxy:", ent[i].real,
+                                          r->unparsed_uri + len, NULL);
+                r->handler = "proxy-server";
+                r->proxyreq = PROXYREQ_REVERSE;
+                r->uri = r->unparsed_uri;
+                break;
+            }
+        }
+#endif
+    }
+    return DECLINED;
+}
+
+static int proxy_trans(request_rec *r)
+{
+#ifndef FIX_15207
+    void *sconf = r->server->module_config;
+    proxy_server_conf *conf =
+    (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
+    int i, len;
+    struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts;
+#endif
+
+    if (r->proxyreq) {
+        /* someone has already set up the proxy, it was possibly ourselves
+         * in proxy_detect
+         */
+        return OK;
+    }
+
+#ifndef FIX_15207
+    /* XXX: since r->uri has been manipulated already we're not really
+     * compliant with RFC1945 at this point.  But this probably isn't
+     * an issue because this is a hybrid proxy/origin server.
+     */
+
+    for (i = 0; i < conf->aliases->nelts; i++) {
+        len = alias_match(r->uri, ent[i].fake);
+
+       if (len > 0) {
+           if ((ent[i].real[0] == '!') && (ent[i].real[1] == 0)) {
+               return DECLINED;
+           }
+
+           r->filename = apr_pstrcat(r->pool, "proxy:", ent[i].real,
+                                     r->uri + len, NULL);
+           r->handler = "proxy-server";
+           r->proxyreq = PROXYREQ_REVERSE;
+           return OK;
+       }
+    }
+#endif
+    return DECLINED;
+}
+
+static int proxy_walk(request_rec *r)
+{
+    proxy_server_conf *sconf = ap_get_module_config(r->server->module_config,
+                                                    &proxy_module);
+    ap_conf_vector_t *per_dir_defaults = r->server->lookup_defaults;
+    ap_conf_vector_t **sec_proxy = (ap_conf_vector_t **) sconf->sec_proxy->elts;
+    ap_conf_vector_t *entry_config;
+    proxy_dir_conf *entry_proxy;
+    int num_sec = sconf->sec_proxy->nelts;
+    /* XXX: shouldn't we use URI here?  Canonicalize it first?
+     * Pass over "proxy:" prefix 
+     */
+    const char *proxyname = r->filename + 6;
+    int j;
+
+    for (j = 0; j < num_sec; ++j) 
+    {
+        entry_config = sec_proxy[j];
+        entry_proxy = ap_get_module_config(entry_config, &proxy_module);
+
+        /* XXX: What about case insensitive matching ???
+         * Compare regex, fnmatch or string as appropriate
+         * If the entry doesn't relate, then continue 
+         */
+        if (entry_proxy->r 
+              ? ap_regexec(entry_proxy->r, proxyname, 0, NULL, 0)
+              : (entry_proxy->p_is_fnmatch
+                   ? apr_fnmatch(entry_proxy->p, proxyname, 0)
+                   : strncmp(proxyname, entry_proxy->p, 
+                                        strlen(entry_proxy->p)))) {
+            continue;
+        }
+        per_dir_defaults = ap_merge_per_dir_configs(r->pool, per_dir_defaults,
+                                                             entry_config);
+    }
+
+    r->per_dir_config = per_dir_defaults;
+
+    return OK;
+}
+
+static int proxy_map_location(request_rec *r)
+{
+    int access_status;
+
+    if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
+        return DECLINED;
+
+    /* Don't let the core or mod_http map_to_storage hooks handle this,
+     * We don't need directory/file_walk, and we want to TRACE on our own.
+     */
+    if ((access_status = proxy_walk(r))) {
+        ap_die(access_status, r);
+        return access_status;
+    }
+
+    return OK;
+}
+#ifndef FIX_15207
+/* -------------------------------------------------------------- */
+/* Fixup the filename */
+
+/*
+ * Canonicalise the URL
+ */
+static int proxy_fixup(request_rec *r)
+{
+    char *url, *p;
+    int access_status;
+
+    if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
+        return DECLINED;
+
+#ifdef FIX_15207
+/* We definitely shouldn't canonicalize a proxy_pass.
+ * But should we really canonicalize a STD_PROXY??? -- Fahree
+ */
+    if (r->proxyreq == PROXYREQ_REVERSE) {
+        return OK;
+    }
+#endif
+
+    /* XXX: Shouldn't we try this before we run the proxy_walk? */
+    url = &r->filename[6];
+
+    /* canonicalise each specific scheme */
+    if ((access_status = proxy_run_canon_handler(r, url))) {
+        return access_status;
+    }
+
+    p = strchr(url, ':');
+    if (p == NULL || p == url)
+        return HTTP_BAD_REQUEST;
+
+    return OK;		/* otherwise; we've done the best we can */
+}
+#endif
+/* Send a redirection if the request contains a hostname which is not */
+/* fully qualified, i.e. doesn't have a domain name appended. Some proxy */
+/* servers like Netscape's allow this and access hosts from the local */
+/* domain in this case. I think it is better to redirect to a FQDN, since */
+/* these will later be found in the bookmarks files. */
+/* The "ProxyDomain" directive determines what domain will be appended */
+static int proxy_needsdomain(request_rec *r, const char *url, const char *domain)
+{
+    char *nuri;
+    const char *ref;
+
+    /* We only want to worry about GETs */
+    if (!r->proxyreq || r->method_number != M_GET || !r->parsed_uri.hostname)
+        return DECLINED;
+
+    /* If host does contain a dot already, or it is "localhost", decline */
+    if (strchr(r->parsed_uri.hostname, '.') != NULL
+     || strcasecmp(r->parsed_uri.hostname, "localhost") == 0)
+        return DECLINED;	/* host name has a dot already */
+
+    ref = apr_table_get(r->headers_in, "Referer");
+
+    /* Reassemble the request, but insert the domain after the host name */
+    /* Note that the domain name always starts with a dot */
+    r->parsed_uri.hostname = apr_pstrcat(r->pool, r->parsed_uri.hostname,
+                                         domain, NULL);
+    nuri = apr_uri_unparse(r->pool,
+                           &r->parsed_uri,
+                           APR_URI_UNP_REVEALPASSWORD);
+
+    apr_table_set(r->headers_out, "Location", nuri);
+    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
+                  "Domain missing: %s sent to %s%s%s", r->uri,
+                  apr_uri_unparse(r->pool, &r->parsed_uri,
+                                  APR_URI_UNP_OMITUSERINFO),
+                  ref ? " from " : "", ref ? ref : "");
+
+    return HTTP_MOVED_PERMANENTLY;
+}
+
+/* -------------------------------------------------------------- */
+/* Invoke handler */
+
+static int proxy_handler(request_rec *r)
+{
+    char *url, *scheme, *p;
+    const char *p2;
+    void *sconf = r->server->module_config;
+    proxy_server_conf *conf = (proxy_server_conf *)
+        ap_get_module_config(sconf, &proxy_module);
+    apr_array_header_t *proxies = conf->proxies;
+    struct proxy_remote *ents = (struct proxy_remote *) proxies->elts;
+    int i, rc, access_status;
+    int direct_connect = 0;
+    const char *str;
+    long maxfwd;
+    proxy_balancer *balancer = NULL;
+    proxy_worker *worker = NULL;
+
+    /* is this for us? */
+    if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
+        return DECLINED;
+
+    /* handle max-forwards / OPTIONS / TRACE */
+    if ((str = apr_table_get(r->headers_in, "Max-Forwards"))) {
+        maxfwd = strtol(str, NULL, 10);
+        if (maxfwd < 1) {
+            switch (r->method_number) {
+            case M_TRACE: {
+                int access_status;
+                r->proxyreq = PROXYREQ_NONE;
+                if ((access_status = ap_send_http_trace(r)))
+                    ap_die(access_status, r);
+                else
+                    ap_finalize_request_protocol(r);
+                return OK;
+            }
+            case M_OPTIONS: {
+                int access_status;
+                r->proxyreq = PROXYREQ_NONE;
+                if ((access_status = ap_send_http_options(r)))
+                    ap_die(access_status, r);
+                else
+                    ap_finalize_request_protocol(r);
+                return OK;
+            }
+            default: {
+                return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                                     "Max-Forwards has reached zero - proxy loop?");
+            }
+            }
+        }
+        maxfwd = (maxfwd > 0) ? maxfwd - 1 : 0;
+    }
+    else {
+        /* set configured max-forwards */
+        maxfwd = conf->maxfwd;
+    }
+    apr_table_set(r->headers_in, "Max-Forwards", 
+                  apr_psprintf(r->pool, "%ld", (maxfwd > 0) ? maxfwd : 0));
+
+    url = r->filename + 6;
+    p = strchr(url, ':');
+    if (p == NULL) {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                      "proxy_handler no URL in %s", r->filename);
+        return HTTP_BAD_REQUEST;
+    }
+
+    /* If the host doesn't have a domain name, add one and redirect. */
+    if (conf->domain != NULL) {
+        rc = proxy_needsdomain(r, url, conf->domain);
+        if (ap_is_HTTP_REDIRECT(rc))
+            return HTTP_MOVED_PERMANENTLY;
+    }
+
+    *p = '\0';
+    scheme = apr_pstrdup(r->pool, url);
+    *p = ':';
+
+    /* Check URI's destination host against NoProxy hosts */
+    /* Bypass ProxyRemote server lookup if configured as NoProxy */
+    /* we only know how to handle communication to a proxy via http */
+    /*if (strcasecmp(scheme, "http") == 0) */
+    {
+        int ii;
+        struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
+
+        for (direct_connect = ii = 0; ii < conf->dirconn->nelts && !direct_connect; ii++) {
+            direct_connect = list[ii].matcher(&list[ii], r);
+        }
+#if DEBUGGING
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+                      (direct_connect) ? "NoProxy for %s" : "UseProxy for %s",
+                      r->uri);
+#endif
+    }
+    
+    /* Try to obtain the most suitable worker */
+    access_status = ap_proxy_pre_request(&worker, &balancer, r, conf, &url);
+    if (access_status != OK)
+        return access_status;
+    
+    /* firstly, try a proxy, unless a NoProxy directive is active */
+    if (!direct_connect) {
+        for (i = 0; i < proxies->nelts; i++) {
+            p2 = ap_strchr_c(ents[i].scheme, ':');  /* is it a partial URL? */
+            if (strcmp(ents[i].scheme, "*") == 0 ||
+                (ents[i].use_regex && ap_regexec(ents[i].regexp, url, 0,NULL, 0)) ||
+                (p2 == NULL && strcasecmp(scheme, ents[i].scheme) == 0) ||
+                (p2 != NULL &&
+                 strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) {
+
+                /* handle the scheme */
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                             "Trying to run scheme_handler against proxy");
+                access_status = proxy_run_scheme_handler(r, worker, conf, url, ents[i].hostname, ents[i].port);
+
+                /* an error or success */
+                if (access_status != DECLINED && access_status != HTTP_BAD_GATEWAY) {
+                    goto cleanup;
+                }
+                /* we failed to talk to the upstream proxy */
+            }
+        }
+    }
+
+    /* otherwise, try it direct */
+    /* N.B. what if we're behind a firewall, where we must use a proxy or
+     * give up??
+     */
+
+    /* handle the scheme */
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "Trying to run scheme_handler");
+    access_status = proxy_run_scheme_handler(r, worker, conf, url, NULL, 0);
+    if (DECLINED == access_status) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+                    "proxy: No protocol handler was valid for the URL %s. "
+                    "If you are using a DSO version of mod_proxy, make sure "
+                    "the proxy submodules are included in the configuration "
+                    "using LoadModule.", r->uri);
+        access_status = HTTP_FORBIDDEN;
+        goto cleanup;
+    }
+
+cleanup:
+    if (balancer) {
+        int post_status = proxy_run_post_request(worker, balancer, r, conf);
+        if (post_status == DECLINED) {
+            post_status = OK; /* no post_request handler available */
+            /* TODO: reclycle direct worker */
+        }
+    }
+    return access_status;
+}
+
+/* -------------------------------------------------------------- */
+/* Setup configurable data */
+
+static void * create_proxy_config(apr_pool_t *p, server_rec *s)
+{
+    proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf));
+
+    ps->sec_proxy = apr_array_make(p, 10, sizeof(ap_conf_vector_t *));
+    ps->proxies = apr_array_make(p, 10, sizeof(struct proxy_remote));
+    ps->aliases = apr_array_make(p, 10, sizeof(struct proxy_alias));
+    ps->raliases = apr_array_make(p, 10, sizeof(struct proxy_alias));
+    ps->cookie_paths = apr_array_make(p, 10, sizeof(struct proxy_alias));
+    ps->cookie_domains = apr_array_make(p, 10, sizeof(struct proxy_alias));
+    ps->cookie_path_str = apr_strmatch_precompile(p, "path=", 0);
+    ps->cookie_domain_str = apr_strmatch_precompile(p, "domain=", 0);
+    ps->noproxies = apr_array_make(p, 10, sizeof(struct noproxy_entry));
+    ps->dirconn = apr_array_make(p, 10, sizeof(struct dirconn_entry));
+    ps->allowed_connect_ports = apr_array_make(p, 10, sizeof(int));
+    ps->workers = apr_array_make(p, 10, sizeof(proxy_worker));
+    ps->balancers = apr_array_make(p, 10, sizeof(proxy_balancer));
+    ps->domain = NULL;
+    ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */
+    ps->viaopt_set = 0; /* 0 means default */
+    ps->req = 0;
+    ps->req_set = 0;
+    ps->recv_buffer_size = 0; /* this default was left unset for some reason */
+    ps->recv_buffer_size_set = 0;
+    ps->io_buffer_size = AP_IOBUFSIZE;
+    ps->io_buffer_size_set = 0;
+    ps->maxfwd = DEFAULT_MAX_FORWARDS;
+    ps->maxfwd_set = 0;
+    ps->error_override = 0; 
+    ps->error_override_set = 0; 
+    ps->preserve_host_set = 0;
+    ps->preserve_host = 0;    
+    ps->timeout = 0;
+    ps->timeout_set = 0;
+    ps->badopt = bad_error;
+    ps->badopt_set = 0;
+    return ps;
+}
+
+static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv)
+{
+    proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf));
+    proxy_server_conf *base = (proxy_server_conf *) basev;
+    proxy_server_conf *overrides = (proxy_server_conf *) overridesv;
+
+    ps->proxies = apr_array_append(p, base->proxies, overrides->proxies);
+    ps->sec_proxy = apr_array_append(p, base->sec_proxy, overrides->sec_proxy);
+    ps->aliases = apr_array_append(p, base->aliases, overrides->aliases);
+    ps->raliases = apr_array_append(p, base->raliases, overrides->raliases);
+    ps->cookie_paths
+        = apr_array_append(p, base->cookie_paths, overrides->cookie_paths);
+    ps->cookie_domains
+        = apr_array_append(p, base->cookie_domains, overrides->cookie_domains);
+    ps->cookie_path_str = base->cookie_path_str;
+    ps->cookie_domain_str = base->cookie_domain_str;
+    ps->noproxies = apr_array_append(p, base->noproxies, overrides->noproxies);
+    ps->dirconn = apr_array_append(p, base->dirconn, overrides->dirconn);
+    ps->allowed_connect_ports = apr_array_append(p, base->allowed_connect_ports, overrides->allowed_connect_ports);
+    ps->workers = apr_array_append(p, base->workers, overrides->workers);
+    ps->balancers = apr_array_append(p, base->balancers, overrides->balancers);
+
+    ps->domain = (overrides->domain == NULL) ? base->domain : overrides->domain;
+    ps->viaopt = (overrides->viaopt_set == 0) ? base->viaopt : overrides->viaopt;
+    ps->req = (overrides->req_set == 0) ? base->req : overrides->req;
+    ps->recv_buffer_size = (overrides->recv_buffer_size_set == 0) ? base->recv_buffer_size : overrides->recv_buffer_size;
+    ps->io_buffer_size = (overrides->io_buffer_size_set == 0) ? base->io_buffer_size : overrides->io_buffer_size;
+    ps->maxfwd = (overrides->maxfwd_set == 0) ? base->maxfwd : overrides->maxfwd;
+    ps->error_override = (overrides->error_override_set == 0) ? base->error_override : overrides->error_override;
+    ps->preserve_host = (overrides->preserve_host_set == 0) ? base->preserve_host : overrides->preserve_host;
+    ps->timeout= (overrides->timeout_set == 0) ? base->timeout : overrides->timeout;
+    ps->badopt = (overrides->badopt_set == 0) ? base->badopt : overrides->badopt;
+
+    return ps;
+}
+
+static void *create_proxy_dir_config(apr_pool_t *p, char *dummy)
+{
+    proxy_dir_conf *new =
+        (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf));
+
+    /* Filled in by proxysection, when applicable */
+
+    return (void *) new;
+}
+
+static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv)
+{
+    proxy_dir_conf *new = (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf));
+    proxy_dir_conf *add = (proxy_dir_conf *) addv;
+
+    new->p = add->p;
+    new->p_is_fnmatch = add->p_is_fnmatch;
+    new->r = add->r;
+    return new;
+}
+
+
+static const char *
+    add_proxy(cmd_parms *cmd, void *dummy, const char *f1, const char *r1, int regex)
+{
+    server_rec *s = cmd->server;
+    proxy_server_conf *conf =
+    (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
+    struct proxy_remote *new;
+    char *p, *q;
+    char *r, *f, *scheme;
+    regex_t *reg = NULL;
+    int port;
+
+    r = apr_pstrdup(cmd->pool, r1);
+    scheme = apr_pstrdup(cmd->pool, r1);
+    f = apr_pstrdup(cmd->pool, f1);
+    p = strchr(r, ':');
+    if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0') {
+        if (regex)
+            return "ProxyRemoteMatch: Bad syntax for a remote proxy server";
+        else
+            return "ProxyRemote: Bad syntax for a remote proxy server";
+    }
+    else {
+        scheme[p-r] = 0;
+    }
+    q = strchr(p + 3, ':');
+    if (q != NULL) {
+        if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) {
+            if (regex)
+                return "ProxyRemoteMatch: Bad syntax for a remote proxy server (bad port number)";
+            else
+                return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)";
+        }
+        *q = '\0';
+    }
+    else
+        port = -1;
+    *p = '\0';
+    if (regex) {
+        reg = ap_pregcomp(cmd->pool, f, REG_EXTENDED);
+        if (!reg)
+            return "Regular expression for ProxyRemoteMatch could not be compiled.";
+    }
+    else
+        if (strchr(f, ':') == NULL)
+            ap_str_tolower(f);		/* lowercase scheme */
+    ap_str_tolower(p + 3);		/* lowercase hostname */
+
+    if (port == -1) {
+        port = apr_uri_port_of_scheme(scheme);
+    }
+
+    new = apr_array_push(conf->proxies);
+    new->scheme = f;
+    new->protocol = r;
+    new->hostname = p + 3;
+    new->port = port;
+    new->regexp = reg;
+    new->use_regex = regex;
+    return NULL;
+}
+
+static const char *
+    add_proxy_noregex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1)
+{
+    return add_proxy(cmd, dummy, f1, r1, 0);
+}
+
+static const char *
+    add_proxy_regex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1)
+{
+    return add_proxy(cmd, dummy, f1, r1, 1);
+}
+
+static const char *
+    add_pass(cmd_parms *cmd, void *dummy, const char *arg)
+{
+    server_rec *s = cmd->server;
+    proxy_server_conf *conf =
+    (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
+    struct proxy_alias *new;
+    char *f = cmd->path;
+    char *r = NULL;
+    char *word;
+    apr_table_t *params = apr_table_make(cmd->pool, 5);
+    const apr_array_header_t *arr;
+    const apr_table_entry_t *elts;
+    int i;
+    
+    while (*arg) {
+        word = ap_getword_conf(cmd->pool, &arg);
+        if (!f)
+            f = word;
+        else if (!r)
+            r = word;
+        else {
+            char *val = strchr(word, '=');
+            if (!val) {
+                if (cmd->path)
+                    return "Invalid ProxyPass parameter. Paramet must be in the form 'key=value'";
+                else
+                    return "ProxyPass can not have a path when defined in a location"; 
+            }
+            else
+                *val++ = '\0';
+            apr_table_setn(params, word, val);
+        }
+    };
+
+    if (r == NULL)
+        return "ProxyPass needs a path when not defined in a location";
+
+    new = apr_array_push(conf->aliases);
+    new->fake = apr_pstrdup(cmd->pool, f);
+    new->real = apr_pstrdup(cmd->pool, r);
+    
+    arr = apr_table_elts(params);
+    elts = (const apr_table_entry_t *)arr->elts;
+    /* Distinguish the balancer from woker */
+    if (strncasecmp(r, "balancer:", 9) == 0) {
+        proxy_balancer *balancer = ap_proxy_get_balancer(cmd->pool, conf, r);
+        if (!balancer) {
+            const char *err = ap_proxy_add_balancer(&balancer,
+                                                    cmd->pool,
+                                                    conf, r);
+            if (err)
+                return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
+        }        
+        for (i = 0; i < arr->nelts; i++) {
+            const char *err = set_balancer_param(cmd->pool, balancer, elts[i].key,
+                                                 elts[i].val);
+            if (err)
+                return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
+        }
+    }
+    else {
+        proxy_worker *worker = ap_proxy_get_worker(cmd->temp_pool, conf, r);
+        if (!worker) {
+            const char *err = ap_proxy_add_worker(&worker, cmd->pool, conf, r);
+            if (err)
+                return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
+        }
+        PROXY_COPY_CONF_PARAMS(worker, conf);
+
+        for (i = 0; i < arr->nelts; i++) {
+            const char *err = set_worker_param(cmd->pool, worker, elts[i].key,
+                                               elts[i].val);
+            if (err)
+                return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
+        }
+    }
+    return NULL;
+}
+
+static const char *
+    add_pass_reverse(cmd_parms *cmd, void *dummy, const char *f, const char *r)
+{
+    server_rec *s = cmd->server;
+    proxy_server_conf *conf;
+    struct proxy_alias *new;
+
+    conf = (proxy_server_conf *)ap_get_module_config(s->module_config, 
+                                                     &proxy_module);
+    if (r!=NULL && cmd->path == NULL ) {
+        new = apr_array_push(conf->raliases);
+        new->fake = f;
+        new->real = r;
+    } else if (r==NULL && cmd->path != NULL) {
+        new = apr_array_push(conf->raliases);
+        new->fake = cmd->path;
+        new->real = f;
+    } else {
+        if ( r == NULL)
+            return "ProxyPassReverse needs a path when not defined in a location";
+        else 
+            return "ProxyPassReverse can not have a path when defined in a location";
+    }
+
+    return NULL;
+}
+static const char*
+    cookie_path(cmd_parms *cmd, void *dummy, const char *f, const char *r)
+{
+    server_rec *s = cmd->server;
+    proxy_server_conf *conf;
+    struct proxy_alias *new;
+
+    conf = (proxy_server_conf *)ap_get_module_config(s->module_config,
+                                                     &proxy_module);
+    new = apr_array_push(conf->cookie_paths);
+    new->fake = f;
+    new->real = r;
+
+    return NULL;
+}
+static const char*
+    cookie_domain(cmd_parms *cmd, void *dummy, const char *f, const char *r)
+{
+    server_rec *s = cmd->server;
+    proxy_server_conf *conf;
+    struct proxy_alias *new;
+
+    conf = (proxy_server_conf *)ap_get_module_config(s->module_config,
+                                                     &proxy_module);
+    new = apr_array_push(conf->cookie_domains);
+    new->fake = f;
+    new->real = r;
+
+    return NULL;
+}
+
+static const char *
+    set_proxy_exclude(cmd_parms *parms, void *dummy, const char *arg)
+{
+    server_rec *s = parms->server;
+    proxy_server_conf *conf =
+    ap_get_module_config(s->module_config, &proxy_module);
+    struct noproxy_entry *new;
+    struct noproxy_entry *list = (struct noproxy_entry *) conf->noproxies->elts;
+    struct apr_sockaddr_t *addr;
+    int found = 0;
+    int i;
+
+    /* Don't duplicate entries */
+    for (i = 0; i < conf->noproxies->nelts; i++) {
+        if (apr_strnatcasecmp(arg, list[i].name) == 0) { /* ignore case for host names */
+            found = 1;
+        }
+    }
+
+    if (!found) {
+        new = apr_array_push(conf->noproxies);
+        new->name = arg;
+        if (APR_SUCCESS == apr_sockaddr_info_get(&addr, new->name, APR_UNSPEC, 0, 0, parms->pool)) {
+            new->addr = addr;
+        }
+        else {
+            new->addr = NULL;
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Set the ports CONNECT can use
+ */
+static const char *
+    set_allowed_ports(cmd_parms *parms, void *dummy, const char *arg)
+{
+    server_rec *s = parms->server;
+    proxy_server_conf *conf =
+        ap_get_module_config(s->module_config, &proxy_module);
+    int *New;
+
+    if (!apr_isdigit(arg[0]))
+        return "AllowCONNECT: port number must be numeric";
+
+    New = apr_array_push(conf->allowed_connect_ports);
+    *New = atoi(arg);
+    return NULL;
+}
+
+/* Similar to set_proxy_exclude(), but defining directly connected hosts,
+ * which should never be accessed via the configured ProxyRemote servers
+ */
+static const char *
+    set_proxy_dirconn(cmd_parms *parms, void *dummy, const char *arg)
+{
+    server_rec *s = parms->server;
+    proxy_server_conf *conf =
+    ap_get_module_config(s->module_config, &proxy_module);
+    struct dirconn_entry *New;
+    struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
+    int found = 0;
+    int i;
+
+    /* Don't duplicate entries */
+    for (i = 0; i < conf->dirconn->nelts; i++) {
+        if (strcasecmp(arg, list[i].name) == 0)
+            found = 1;
+    }
+
+    if (!found) {
+        New = apr_array_push(conf->dirconn);
+        New->name = apr_pstrdup(parms->pool, arg);
+        New->hostaddr = NULL;
+
+	if (ap_proxy_is_ipaddr(New, parms->pool)) {
+#if DEBUGGING
+            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                         "Parsed addr %s", inet_ntoa(New->addr));
+            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                         "Parsed mask %s", inet_ntoa(New->mask));
+#endif
+	}
+	else if (ap_proxy_is_domainname(New, parms->pool)) {
+            ap_str_tolower(New->name);
+#if DEBUGGING
+            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                         "Parsed domain %s", New->name);
+#endif
+        }
+        else if (ap_proxy_is_hostname(New, parms->pool)) {
+            ap_str_tolower(New->name);
+#if DEBUGGING
+            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                         "Parsed host %s", New->name);
+#endif
+        }
+        else {
+            ap_proxy_is_word(New, parms->pool);
+#if DEBUGGING
+            fprintf(stderr, "Parsed word %s\n", New->name);
+#endif
+        }
+    }
+    return NULL;
+}
+
+static const char *
+    set_proxy_domain(cmd_parms *parms, void *dummy, const char *arg)
+{
+    proxy_server_conf *psf =
+    ap_get_module_config(parms->server->module_config, &proxy_module);
+
+    if (arg[0] != '.')
+        return "ProxyDomain: domain name must start with a dot.";
+
+    psf->domain = arg;
+    return NULL;
+}
+
+static const char *
+    set_proxy_req(cmd_parms *parms, void *dummy, int flag)
+{
+    proxy_server_conf *psf =
+    ap_get_module_config(parms->server->module_config, &proxy_module);
+
+    psf->req = flag;
+    psf->req_set = 1;
+    return NULL;
+}
+static const char *
+    set_proxy_error_override(cmd_parms *parms, void *dummy, int flag)
+{
+    proxy_server_conf *psf =
+    ap_get_module_config(parms->server->module_config, &proxy_module);
+
+    psf->error_override = flag;
+    psf->error_override_set = 1;
+    return NULL;
+}
+static const char *
+    set_preserve_host(cmd_parms *parms, void *dummy, int flag)
+{
+    proxy_server_conf *psf =
+    ap_get_module_config(parms->server->module_config, &proxy_module);
+
+    psf->preserve_host = flag;
+    psf->preserve_host_set = 1;
+    return NULL;
+}
+
+static const char *
+    set_recv_buffer_size(cmd_parms *parms, void *dummy, const char *arg)
+{
+    proxy_server_conf *psf =
+    ap_get_module_config(parms->server->module_config, &proxy_module);
+    int s = atoi(arg);
+    if (s < 512 && s != 0) {
+        return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
+    }
+
+    psf->recv_buffer_size = s;
+    psf->recv_buffer_size_set = 1;
+    return NULL;
+}
+
+static const char *
+    set_io_buffer_size(cmd_parms *parms, void *dummy, const char *arg)
+{
+    proxy_server_conf *psf =
+    ap_get_module_config(parms->server->module_config, &proxy_module);
+    long s = atol(arg);
+
+    psf->io_buffer_size = ((s > AP_IOBUFSIZE) ? s : AP_IOBUFSIZE);
+    psf->io_buffer_size_set = 1;
+    return NULL;
+}
+
+static const char *
+    set_max_forwards(cmd_parms *parms, void *dummy, const char *arg)
+{
+    proxy_server_conf *psf =
+    ap_get_module_config(parms->server->module_config, &proxy_module);
+    long s = atol(arg);
+    if (s < 0) {
+        return "ProxyMaxForwards must be greater or equal to zero..";
+    }
+
+    psf->maxfwd = s;
+    psf->maxfwd_set = 1;
+    return NULL;
+}
+static const char*
+    set_proxy_timeout(cmd_parms *parms, void *dummy, const char *arg)
+{
+    proxy_server_conf *psf =
+    ap_get_module_config(parms->server->module_config, &proxy_module);
+    int timeout;
+
+    timeout=atoi(arg);
+    if (timeout<1) {
+        return "Proxy Timeout must be at least 1 second.";
+    }
+    psf->timeout_set=1;
+    psf->timeout=apr_time_from_sec(timeout);
+
+    return NULL;    
+}
+
+static const char*
+    set_via_opt(cmd_parms *parms, void *dummy, const char *arg)
+{
+    proxy_server_conf *psf =
+    ap_get_module_config(parms->server->module_config, &proxy_module);
+
+    if (strcasecmp(arg, "Off") == 0)
+        psf->viaopt = via_off;
+    else if (strcasecmp(arg, "On") == 0)
+        psf->viaopt = via_on;
+    else if (strcasecmp(arg, "Block") == 0)
+        psf->viaopt = via_block;
+    else if (strcasecmp(arg, "Full") == 0)
+        psf->viaopt = via_full;
+    else {
+        return "ProxyVia must be one of: "
+            "off | on | full | block";
+    }
+
+    psf->viaopt_set = 1;
+    return NULL;    
+}
+
+static const char*
+    set_bad_opt(cmd_parms *parms, void *dummy, const char *arg)
+{
+    proxy_server_conf *psf =
+    ap_get_module_config(parms->server->module_config, &proxy_module);
+
+    if (strcasecmp(arg, "IsError") == 0)
+        psf->badopt = bad_error;
+    else if (strcasecmp(arg, "Ignore") == 0)
+        psf->badopt = bad_ignore;
+    else if (strcasecmp(arg, "StartBody") == 0)
+        psf->badopt = bad_body;
+    else {
+        return "ProxyBadHeader must be one of: "
+            "IsError | Ignore | StartBody";
+    }
+
+    psf->badopt_set = 1;
+    return NULL;    
+}
+
+static const char*
+    set_status_opt(cmd_parms *parms, void *dummy, const char *arg)
+{
+    proxy_server_conf *psf =
+    ap_get_module_config(parms->server->module_config, &proxy_module);
+
+    if (strcasecmp(arg, "Off") == 0)
+        psf->proxy_status = status_off;
+    else if (strcasecmp(arg, "On") == 0)
+        psf->proxy_status = status_on;
+    else if (strcasecmp(arg, "Full") == 0)
+        psf->proxy_status = status_full;
+    else {
+        return "ProxyStatus must be one of: "
+            "off | on | block";
+    }
+
+    psf->proxy_status_set = 1;
+    return NULL;    
+}
+
+static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg)
+{
+    server_rec *s = cmd->server;
+    proxy_server_conf *conf =
+    ap_get_module_config(s->module_config, &proxy_module);
+    proxy_balancer *balancer;
+    proxy_worker *worker;
+    char *path = cmd->path;
+    char *name = NULL;
+    char *word;
+    apr_table_t *params = apr_table_make(cmd->pool, 5);
+    const apr_array_header_t *arr;
+    const apr_table_entry_t *elts;
+    int i;
+    
+    if (cmd->path)
+        path = apr_pstrdup(cmd->pool, cmd->path);
+    while (*arg) {
+        word = ap_getword_conf(cmd->pool, &arg);
+        if (!path)
+            path = word;
+        else if (!name)
+            name = word;
+        else {
+            char *val = strchr(word, '=');
+            if (!val)
+                if (cmd->path)
+                    return "BalancerMember can not have a balancer name when defined in a location";
+                else
+                    return "Invalid BalancerMember parameter. Paramet must be in the form 'key=value'";
+            else
+                *val++ = '\0';
+            apr_table_setn(params, word, val);
+        }
+    }
+    if (!path)
+        return "BalancerMember must define balancer name when outside <Proxy > section";
+    if (!name)
+        return "BalancerMember must define remote proxy server";
+    
+    ap_str_tolower(path);	/* lowercase scheme://hostname */
+    ap_str_tolower(name);	/* lowercase scheme://hostname */
+
+    /* Try to find existing worker */
+    worker = ap_proxy_get_worker(cmd->temp_pool, conf, name);
+    if (!worker) {
+        const char *err;
+        if ((err = ap_proxy_add_worker(&worker, cmd->pool, conf, name)) != NULL)
+            return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL); 
+    }
+    PROXY_COPY_CONF_PARAMS(worker, conf);
+    
+    arr = apr_table_elts(params);
+    elts = (const apr_table_entry_t *)arr->elts;
+    for (i = 0; i < arr->nelts; i++) {
+        const char *err = set_worker_param(cmd->pool, worker, elts[i].key,
+                                           elts[i].val);
+        if (err)
+            return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL);
+    }
+    /* Try to find the balancer */
+    balancer = ap_proxy_get_balancer(cmd->temp_pool, conf, path); 
+    if (!balancer) {
+        const char *err = ap_proxy_add_balancer(&balancer,
+                                                cmd->pool,
+                                                conf, path);
+        if (err)
+            return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL);
+    }
+    /* Add the worker to the load balancer */
+    ap_proxy_add_worker_to_balancer(cmd->pool, balancer, worker);
+
+    return NULL;
+}
+
+static const char *
+    set_sticky_session(cmd_parms *cmd, void *dummy, const char *f, const char *r)
+{
+    server_rec *s = cmd->server;
+    proxy_server_conf *conf =
+    ap_get_module_config(s->module_config, &proxy_module);
+    proxy_balancer *balancer;
+    const char *name, *sticky;
+
+    if (r != NULL && cmd->path == NULL ) {
+        name = f;
+        sticky = r;
+    } else if (r == NULL && cmd->path != NULL) {
+        name = cmd->path;
+        sticky = f;
+    } else {
+        if (r == NULL)
+            return "BalancerStickySession needs a path when not defined in a location";
+        else 
+            return "BalancerStickySession can not have a path when defined in a location";
+    }
+    /* Try to find the balancer */
+    balancer = ap_proxy_get_balancer(cmd->temp_pool, conf, name);
+    if (!balancer)
+        return apr_pstrcat(cmd->temp_pool, "BalancerStickySession: can not find a load balancer '",
+                           name, "'", NULL);
+    if (!strcasecmp(sticky, "nofailover"))
+        balancer->sticky_force = 1;   
+    else
+        balancer->sticky = sticky;
+    return NULL;
+}
+
+static void ap_add_per_proxy_conf(server_rec *s, ap_conf_vector_t *dir_config)
+{
+    proxy_server_conf *sconf = ap_get_module_config(s->module_config,
+					            &proxy_module);
+    void **new_space = (void **)apr_array_push(sconf->sec_proxy);
+    
+    *new_space = dir_config;
+}
+
+static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg)
+{
+    const char *errmsg;
+    const char *endp = ap_strrchr_c(arg, '>');
+    int old_overrides = cmd->override;
+    char *old_path = cmd->path;
+    proxy_dir_conf *conf;
+    ap_conf_vector_t *new_dir_conf = ap_create_per_dir_config(cmd->pool);
+    regex_t *r = NULL;
+    const command_rec *thiscmd = cmd->cmd;
+
+    const char *err = ap_check_cmd_context(cmd,
+                                           NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+    if (err != NULL) {
+        return err;
+    }
+
+    if (endp == NULL) {
+        return apr_pstrcat(cmd->pool, cmd->cmd->name,
+                           "> directive missing closing '>'", NULL);
+    }
+
+    arg=apr_pstrndup(cmd->pool, arg, endp-arg);
+
+    if (!arg) {
+        if (thiscmd->cmd_data)
+            return "<ProxyMatch > block must specify a path";
+        else
+            return "<Proxy > block must specify a path";
+    }
+
+    cmd->path = ap_getword_conf(cmd->pool, &arg);
+    cmd->override = OR_ALL|ACCESS_CONF;
+
+    if (!strncasecmp(cmd->path, "proxy:", 6))
+        cmd->path += 6;
+
+    /* XXX Ignore case?  What if we proxy a case-insensitive server?!? 
+     * While we are at it, shouldn't we also canonicalize the entire
+     * scheme?  See proxy_fixup()
+     */
+    if (thiscmd->cmd_data) { /* <ProxyMatch> */
+        r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
+        if (!r) {
+            return "Regex could not be compiled";
+        }
+    }
+    else if (!strcmp(cmd->path, "~")) {
+        cmd->path = ap_getword_conf(cmd->pool, &arg);
+        if (!cmd->path)
+            return "<Proxy ~ > block must specify a path";
+        if (strncasecmp(cmd->path, "proxy:", 6))
+            cmd->path += 6;
+        r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
+        if (!r) {
+            return "Regex could not be compiled";
+        }
+    }
+
+    /* initialize our config and fetch it */
+    conf = ap_set_config_vectors(cmd->server, new_dir_conf, cmd->path,
+                                 &proxy_module, cmd->pool);
+
+    errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_dir_conf);
+    if (errmsg != NULL)
+        return errmsg;
+
+    conf->r = r;
+    conf->p = cmd->path;
+    conf->p_is_fnmatch = apr_fnmatch_test(conf->p);
+
+    ap_add_per_proxy_conf(cmd->server, new_dir_conf);
+
+    if (*arg != '\0') {
+        return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
+                           "> arguments not (yet) supported.", NULL);
+    }
+
+    cmd->path = old_path;
+    cmd->override = old_overrides;
+
+    return NULL;
+}
+
+static const command_rec proxy_cmds[] =
+{
+    AP_INIT_RAW_ARGS("<Proxy", proxysection, NULL, RSRC_CONF, 
+    "Container for directives affecting resources located in the proxied "
+    "location"),
+    AP_INIT_RAW_ARGS("<ProxyMatch", proxysection, (void*)1, RSRC_CONF,
+    "Container for directives affecting resources located in the proxied "
+    "location, in regular expression syntax"),
+    AP_INIT_FLAG("ProxyRequests", set_proxy_req, NULL, RSRC_CONF,
+     "on if the true proxy requests should be accepted"),
+    AP_INIT_TAKE2("ProxyRemote", add_proxy_noregex, NULL, RSRC_CONF,
+     "a scheme, partial URL or '*' and a proxy server"),
+    AP_INIT_TAKE2("ProxyRemoteMatch", add_proxy_regex, NULL, RSRC_CONF,
+     "a regex pattern and a proxy server"),
+    AP_INIT_RAW_ARGS("ProxyPass", add_pass, NULL, RSRC_CONF|ACCESS_CONF,
+     "a virtual path and a URL"),
+    AP_INIT_TAKE12("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF,
+     "a virtual path and a URL for reverse proxy behaviour"),
+    AP_INIT_TAKE2("ProxyPassReverseCookiePath", cookie_path, NULL,
+       RSRC_CONF|ACCESS_CONF, "Path rewrite rule for proxying cookies"),
+    AP_INIT_TAKE2("ProxyPassReverseCookieDomain", cookie_domain, NULL,
+       RSRC_CONF|ACCESS_CONF, "Domain rewrite rule for proxying cookies"),
+    AP_INIT_ITERATE("ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF,
+     "A list of names, hosts or domains to which the proxy will not connect"),
+    AP_INIT_TAKE1("ProxyReceiveBufferSize", set_recv_buffer_size, NULL, RSRC_CONF,
+     "Receive buffer size for outgoing HTTP and FTP connections in bytes"),
+    AP_INIT_TAKE1("ProxyIOBufferSize", set_io_buffer_size, NULL, RSRC_CONF,
+     "IO buffer size for outgoing HTTP and FTP connections in bytes"),
+    AP_INIT_TAKE1("ProxyMaxForwards", set_max_forwards, NULL, RSRC_CONF,
+     "The maximum number of proxies a request may be forwarded through."),
+    AP_INIT_ITERATE("NoProxy", set_proxy_dirconn, NULL, RSRC_CONF,
+     "A list of domains, hosts, or subnets to which the proxy will connect directly"),
+    AP_INIT_TAKE1("ProxyDomain", set_proxy_domain, NULL, RSRC_CONF,
+     "The default intranet domain name (in absence of a domain in the URL)"),
+    AP_INIT_ITERATE("AllowCONNECT", set_allowed_ports, NULL, RSRC_CONF,
+     "A list of ports which CONNECT may connect to"),
+    AP_INIT_TAKE1("ProxyVia", set_via_opt, NULL, RSRC_CONF,
+     "Configure Via: proxy header header to one of: on | off | block | full"),
+    AP_INIT_FLAG("ProxyErrorOverride", set_proxy_error_override, NULL, RSRC_CONF,
+     "use our error handling pages instead of the servers' we are proxying"),
+    AP_INIT_FLAG("ProxyPreserveHost", set_preserve_host, NULL, RSRC_CONF,
+     "on if we should preserve host header while proxying"),
+    AP_INIT_TAKE1("ProxyTimeout", set_proxy_timeout, NULL, RSRC_CONF,
+     "Set the timeout (in seconds) for a proxied connection. "
+     "This overrides the server timeout"),
+    AP_INIT_TAKE1("ProxyBadHeader", set_bad_opt, NULL, RSRC_CONF,
+     "How to handle bad header line in response: IsError | Ignore | StartBody"),
+    AP_INIT_RAW_ARGS("BalancerMember", add_member, NULL, RSRC_CONF|ACCESS_CONF,
+     "A balancer name and scheme with list of params"), 
+    AP_INIT_TAKE12("BalancerStickySession", set_sticky_session, NULL, RSRC_CONF|ACCESS_CONF,
+     "A balancer and sticky session name"),
+    AP_INIT_TAKE1("ProxyStatus", set_status_opt, NULL, RSRC_CONF,
+     "Configure Status: proxy status to one of: on | off | full"),
+    {NULL}
+};
+
+static APR_OPTIONAL_FN_TYPE(ssl_proxy_enable) *proxy_ssl_enable = NULL;
+static APR_OPTIONAL_FN_TYPE(ssl_engine_disable) *proxy_ssl_disable = NULL;
+
+PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c)
+{
+    /* 
+     * if c == NULL just check if the optional function was imported
+     * else run the optional function so ssl filters are inserted
+     */
+    if (proxy_ssl_enable) {
+        return c ? proxy_ssl_enable(c) : 1;
+    }
+
+    return 0;
+}
+
+PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c)
+{
+    if (proxy_ssl_disable) {
+        return proxy_ssl_disable(c);
+    }
+
+    return 0;
+}
+
+static int proxy_post_config(apr_pool_t *pconf, apr_pool_t *plog,
+                             apr_pool_t *ptemp, server_rec *s)
+{
+    proxy_ssl_enable = APR_RETRIEVE_OPTIONAL_FN(ssl_proxy_enable);
+    proxy_ssl_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable);
+
+    return OK;
+}
+
+
+#define KBYTE 1024
+#define MBYTE 1048576L
+#define GBYTE 1073741824L
+
+/* Format the number of bytes nicely */
+static void format_byte_out(request_rec *r, apr_off_t bytes)
+{
+
+    if (bytes < (5 * KBYTE))
+        ap_rprintf(r, "%d B", (int) bytes);
+    else if (bytes < (MBYTE / 2))
+        ap_rprintf(r, "%.1f kB", (float) bytes / KBYTE);
+    else if (bytes < (GBYTE / 2))
+        ap_rprintf(r, "%.1f MB", (float) bytes / MBYTE);
+    else
+        ap_rprintf(r, "%.1f GB", (float) bytes / GBYTE);
+}
+
+/*
+ *  proxy Extension to mod_status
+ */
+static int proxy_status_hook(request_rec *r, int flags)
+{
+    int i, n;
+    void *sconf = r->server->module_config;
+    proxy_server_conf *conf = (proxy_server_conf *)
+        ap_get_module_config(sconf, &proxy_module);
+    proxy_balancer *balancer = NULL;
+    proxy_runtime_worker *worker = NULL;
+
+    if (flags & AP_STATUS_SHORT || conf->balancers->nelts == 0 ||
+        conf->proxy_status == status_off)
+        return OK;
+
+    balancer = (proxy_balancer *)conf->balancers->elts;
+    for (i = 0; i < conf->balancers->nelts; i++) {
+        ap_rputs("<hr />\n<h1>Proxy LoadBalancer Status for ", r);
+        ap_rvputs(r, balancer->name, "</h1>\n\n", NULL);
+        ap_rputs("\n\n<table border=\"0\"><tr>"
+                 "<th>SSes</th><th>Timeout</th>"
+                 "</tr>\n<tr>", r);                
+        ap_rvputs(r, "<td>", balancer->sticky, NULL);
+        ap_rprintf(r, "</td><td>%d</td>\n",
+                   apr_time_sec(balancer->timeout));
+        ap_rputs("</table>\n", r);
+        ap_rputs("\n\n<table border=\"0\"><tr>"
+                 "<th>Sch</th><th>Host</th>"
+                 "<th>Route</th><th>Redir</th>"
+                 "<th>F</th><th>Acc</th><th>Wr</th><th>Rd</th>"
+                 "</tr>\n", r);
+
+        worker = (proxy_runtime_worker *)balancer->workers->elts;
+        for (n = 0; n < balancer->workers->nelts; n++) {
+
+            ap_rvputs(r, "<tr>\n<td>", worker->w->scheme, "</td>", NULL);
+            ap_rvputs(r, "<td>", worker->w->hostname, "</td>", NULL);
+            ap_rvputs(r, "<td>", worker->w->route, NULL);
+            ap_rvputs(r, "</td><td>", worker->w->redirect, NULL);
+            ap_rprintf(r, "</td><td>%.2f</td>", worker->s->lbfactor);
+            ap_rprintf(r, "<td>%d</td><td>", (int)(worker->s->elected));
+            format_byte_out(r, worker->s->transfered);
+            ap_rputs("</td><td>", r);
+            format_byte_out(r, worker->s->transfered);
+            ap_rputs("</td>\n", r);
+
+            /* TODO: Add the rest of dynamic worker data */
+            ap_rputs("</tr>\n", r);
+
+            ++worker;
+        }
+        ap_rputs("</table>\n", r);
+        ++balancer;
+    }
+    ap_rputs("<hr /><table>\n"
+             "<tr><th>SSes</th><td>Sticky session name</td></tr>\n"
+             "<tr><th>Timeout</th><td>Balancer Timeout</td></tr>\n"
+             "<tr><th>Sch</th><td>Connection scheme</td></tr>\n"
+             "<tr><th>Host</th><td>Backend Hostname</td></tr>\n"
+             "<tr><th>Route</th><td>Session Route</td></tr>\n"
+             "<tr><th>Redir</th><td>Session Route Redirection</td></tr>\n"
+             "<tr><th>F</th><td>Load Balancer Factor in %</td></tr>\n"
+             "<tr><th>Acc</th><td>Number of requests</td></tr>\n"
+             "<tr><th>Wr</th><td>Number of bytes transfered</td></tr>\n"
+             "<tr><th>Rd</th><td>Number of bytes readed</td></tr>\n"
+             "</table>", r);
+
+    return OK;
+}
+
+/*
+ * This routine is called before the server processes the configuration
+ * files.  There is no return value.
+ */
+static int proxy_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
+                            apr_pool_t *ptemp)
+{
+    APR_OPTIONAL_HOOK(ap, status_hook, proxy_status_hook, NULL, NULL,
+                      APR_HOOK_MIDDLE);
+    return OK;
+}
+
+static void register_hooks(apr_pool_t *p)
+{
+    /* fixup before mod_rewrite, so that the proxied url will not
+     * escaped accidentally by our fixup.
+     */
+#ifndef FIX_15207
+    static const char * const aszSucc[]={ "mod_rewrite.c", NULL };
+#endif
+
+    /* handler */
+    ap_hook_handler(proxy_handler, NULL, NULL, APR_HOOK_FIRST);
+    /* filename-to-URI translation */
+    ap_hook_translate_name(proxy_trans, NULL, NULL, APR_HOOK_FIRST);
+    /* walk <Proxy > entries and suppress default TRACE behavior */
+    ap_hook_map_to_storage(proxy_map_location, NULL,NULL, APR_HOOK_FIRST);
+#ifndef FIX_15207
+    /* fixups */
+    ap_hook_fixups(proxy_fixup, NULL, aszSucc, APR_HOOK_FIRST);
+#endif
+    /* post read_request handling */
+    ap_hook_post_read_request(proxy_detect, NULL, NULL, APR_HOOK_FIRST);
+    /* pre config handling */
+    ap_hook_pre_config(proxy_pre_config, NULL, NULL, APR_HOOK_MIDDLE); 
+    /* post config handling */
+    ap_hook_post_config(proxy_post_config, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+module AP_MODULE_DECLARE_DATA proxy_module =
+{
+    STANDARD20_MODULE_STUFF,
+    create_proxy_dir_config,    /* create per-directory config structure */
+    merge_proxy_dir_config,     /* merge per-directory config structures */
+    create_proxy_config,	/* create per-server config structure */
+    merge_proxy_config,		/* merge per-server config structures */
+    proxy_cmds,			/* command table */
+    register_hooks
+};
+
+APR_HOOK_STRUCT(
+	APR_HOOK_LINK(scheme_handler)
+	APR_HOOK_LINK(canon_handler)
+	APR_HOOK_LINK(pre_request)
+	APR_HOOK_LINK(post_request)
+)
+
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, scheme_handler, 
+                                     (request_rec *r, proxy_worker *worker,
+                                      proxy_server_conf *conf, 
+                                      char *url, const char *proxyhost, 
+                                      apr_port_t proxyport),(r,worker,conf,
+                                      url,proxyhost,proxyport),DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, canon_handler, 
+                                      (request_rec *r, char *url),(r,
+                                      url),DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, pre_request, (
+                                      proxy_worker **worker,
+                                      proxy_balancer **balancer,
+                                      request_rec *r, 
+                                      proxy_server_conf *conf,
+                                      char **url),(worker,balancer,
+                                      r,conf,url),DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, post_request,
+                                      (proxy_worker *worker,
+                                       proxy_balancer *balancer,
+                                       request_rec *r,
+                                       proxy_server_conf *conf),(worker,
+                                       balancer,r,conf),DECLINED)
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, fixups,
+				    (request_rec *r), (r),
+				    OK, DECLINED)
diff --git a/connectors/ajp/proxy/mod_proxy.dsp b/connectors/ajp/proxy/mod_proxy.dsp
new file mode 100644
index 0000000..46d43cc
--- /dev/null
+++ b/connectors/ajp/proxy/mod_proxy.dsp
@@ -0,0 +1,140 @@
+# Microsoft Developer Studio Project File - Name="mod_proxy" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_proxy - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_proxy.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_proxy.mak" CFG="mod_proxy - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_proxy - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_proxy - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "mod_proxy - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../ssl" /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../generators" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "PROXY_DECLARE_EXPORT" /Fd"Release\mod_proxy_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /machine:I386 /out:"Release/mod_proxy.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:"Release/mod_proxy.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy.so /opt:ref
+
+!ELSEIF  "$(CFG)" == "mod_proxy - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "../ssl" /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "../generators" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "PROXY_DECLARE_EXPORT" /Fd"Debug\mod_proxy_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/mod_proxy.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/mod_proxy.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy.so
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_proxy - Win32 Release"
+# Name "mod_proxy - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=.\mod_proxy.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\proxy_util.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\mod_proxy.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\build\win32\win32ver.awk
+
+!IF  "$(CFG)" == "mod_proxy - Win32 Release"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_proxy.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	awk -f ../../build/win32/win32ver.awk mod_proxy.so "proxy_module for Apache" ../../include/ap_release.h > .\mod_proxy.rc
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "mod_proxy - Win32 Debug"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_proxy.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	awk -f ../../build/win32/win32ver.awk mod_proxy.so "proxy_module for Apache" ../../include/ap_release.h > .\mod_proxy.rc
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# End Target
+# End Project
diff --git a/connectors/ajp/proxy/mod_proxy.h b/connectors/ajp/proxy/mod_proxy.h
new file mode 100644
index 0000000..6d4d295
--- /dev/null
+++ b/connectors/ajp/proxy/mod_proxy.h
@@ -0,0 +1,414 @@
+/* Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MOD_PROXY_H
+#define MOD_PROXY_H 
+
+/*
+ * Main include file for the Apache proxy
+ */
+
+/*
+
+   Also note numerous FIXMEs and CHECKMEs which should be eliminated.
+
+   This code is once again experimental!
+
+   Things to do:
+
+   1. Make it completely work (for FTP too)
+
+   2. HTTP/1.1
+
+   Chuck Murcko <chuck@topsail.org> 02-06-01
+
+ */
+
+#define CORE_PRIVATE
+
+#include "apr_hooks.h"
+#include "apr.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "apr_buckets.h"
+#include "apr_md5.h"
+#include "apr_network_io.h"
+#include "apr_pools.h"
+#include "apr_strings.h"
+#include "apr_uri.h"
+#include "apr_date.h"
+#include "apr_strmatch.h"
+#include "apr_fnmatch.h"
+#include "apr_reslist.h"
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
+#include "httpd.h"
+#include "http_config.h"
+#include "ap_config.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "http_vhost.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "http_connection.h"
+#include "util_filter.h"
+#include "util_ebcdic.h"
+
+#if APR_HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if APR_HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+/* for proxy_canonenc() */
+enum enctype {
+    enc_path, enc_search, enc_user, enc_fpath, enc_parm
+};
+
+#if APR_CHARSET_EBCDIC
+#define CRLF   "\r\n"
+#else /*APR_CHARSET_EBCDIC*/
+#define CRLF   "\015\012"
+#endif /*APR_CHARSET_EBCDIC*/
+
+/* default Max-Forwards header setting */
+#define DEFAULT_MAX_FORWARDS	10
+
+/* static information about a remote proxy */
+struct proxy_remote {
+    const char *scheme;		/* the schemes handled by this proxy, or '*' */
+    const char *protocol;	/* the scheme used to talk to this proxy */
+    const char *hostname;	/* the hostname of this proxy */
+    apr_port_t  port;		/* the port for this proxy */
+    regex_t *regexp;		/* compiled regex (if any) for the remote */
+    int use_regex;		/* simple boolean. True if we have a regex pattern */
+};
+
+struct proxy_alias {
+    const char  *real;
+    const char  *fake;
+};
+
+struct dirconn_entry {
+    char *name;
+    struct in_addr addr, mask;
+    struct apr_sockaddr_t *hostaddr;
+    int (*matcher) (struct dirconn_entry * This, request_rec *r);
+};
+
+struct noproxy_entry {
+    const char *name;
+    struct apr_sockaddr_t *addr;
+};
+
+typedef struct {
+    apr_array_header_t *proxies;
+    apr_array_header_t *sec_proxy;
+    apr_array_header_t *aliases;
+    apr_array_header_t *raliases;
+    apr_array_header_t *noproxies;
+    apr_array_header_t *dirconn;
+    apr_array_header_t *allowed_connect_ports;
+    apr_array_header_t *workers;
+    apr_array_header_t *balancers;
+    const char *domain;		/* domain name to use in absence of a domain name in the request */
+    int req;			/* true if proxy requests are enabled */
+    char req_set;
+    enum {
+      via_off,
+      via_on,
+      via_block,
+      via_full
+    } viaopt;                   /* how to deal with proxy Via: headers */
+    char viaopt_set;
+    apr_size_t recv_buffer_size;
+    char recv_buffer_size_set;
+    apr_size_t io_buffer_size;
+    char io_buffer_size_set;
+    long maxfwd;
+    char maxfwd_set;
+    /** 
+     * the following setting masks the error page
+     * returned from the 'proxied server' and just 
+     * forwards the status code upwards.
+     * This allows the main server (us) to generate
+     * the error page, (so it will look like a error
+     * returned from the rest of the system 
+     */
+    int error_override;
+    int error_override_set;
+    int preserve_host;
+    int preserve_host_set;
+    apr_interval_time_t timeout;
+    char timeout_set;
+    enum {
+      bad_error,
+      bad_ignore,
+      bad_body
+    } badopt;                   /* how to deal with bad headers */
+    char badopt_set;
+/* putting new stuff on the end maximises binary back-compatibility.
+ * the strmatch_patterns are really a const just to have a
+ * case-independent strstr.
+ */
+    apr_array_header_t* cookie_paths;
+    apr_array_header_t* cookie_domains;
+    const apr_strmatch_pattern* cookie_path_str;
+    const apr_strmatch_pattern* cookie_domain_str;
+    enum {
+        status_off,
+        status_on,
+        status_full
+    } proxy_status;             /* Status display options */
+    char proxy_status_set;
+
+} proxy_server_conf;
+
+typedef struct proxy_balancer  proxy_balancer;
+typedef struct proxy_worker    proxy_worker;
+typedef struct proxy_conn_pool proxy_conn_pool;
+
+typedef struct {
+    const char *p;            /* The path */
+    int         p_is_fnmatch; /* Is this path an fnmatch candidate? */
+    regex_t    *r;            /* Is this a regex? */
+} proxy_dir_conf;
+
+typedef struct {
+    conn_rec     *connection;
+    const char   *hostname;
+    apr_port_t   port;
+    int          is_ssl;
+    apr_pool_t   *pool;     /* Subpool used for creating socket */
+    apr_socket_t *sock;     /* Connection socket */
+    apr_uint32_t flags;     /* Conection flags */
+    int          close;     /* Close 'this' connection */
+    int          close_on_recycle; /* Close the connection when returning to pool */
+    proxy_worker *worker;   /* Connection pool this connection belogns to */
+    void         *data;     /* per scheme connection data */
+} proxy_conn_rec;
+
+typedef struct {
+        float cache_completion; /* completion percentage */
+        int content_length; /* length of the content */
+} proxy_completion;
+
+/* Connection pool */
+struct proxy_conn_pool {
+    apr_pool_t     *pool;   /* The pool used in constructor and destructor calls */
+    apr_sockaddr_t *addr;   /* Preparsed remote address info */
+#if APR_HAS_THREADS
+    apr_reslist_t  *res;    /* Connection resource list */
+#endif
+    int            nfree;   /* Balancer free count number */
+    proxy_conn_rec *conn;   /* Single connection for prefork mpm's */
+};
+
+/* woker status flags */
+#define PROXY_WORKER_INITIALIZED    0x0001
+#define PROXY_WORKER_IN_SHUTDOWN    0x0010
+#define PROXY_WORKER_DISABLED       0x0020
+#define PROXY_WORKER_IN_ERROR       0x0040
+
+#define PROXY_WORKER_IS_USABLE(f)   (!((f)->status & 0x00F0))
+
+
+/* Worker configuration */
+struct proxy_worker {
+    int             status;
+    apr_time_t      error_time; /* time of the last error */
+    apr_interval_time_t retry;  /* retry interval */
+    int             retries;    /* number of retries on this worker */
+    int             lbfactor;   /* initial load balancing factor */
+    const char      *name;
+    const char      *scheme;    /* scheme to use ajp|http|https */
+    const char      *hostname;  /* remote backend address */
+    const char      *route;     /* balancing route */
+    const char      *redirect;  /* temporary balancing redirection route */
+    apr_port_t      port;
+    int             min;        /* Desired minimum number of available connections */
+    int             smax;       /* Soft maximum on the total number of connections */
+    int             hmax;       /* Hard maximum on the total number of connections */
+    apr_interval_time_t ttl;    /* maximum amount of time in seconds a connection
+                                 * may be available while exceeding the soft limit */
+    apr_interval_time_t timeout; /* connection timeout */
+    char                timeout_set;
+    apr_interval_time_t acquire; /* acquire timeout when the maximum number of connections is exceeded */
+    char                acquire_set;
+    apr_size_t          recv_buffer_size;
+    char                recv_buffer_size_set;
+    apr_size_t          io_buffer_size;
+    char                io_buffer_size_set;
+    char                keepalive;
+    char                keepalive_set;
+    proxy_conn_pool *cp;        /* Connection pool to use */
+    void            *opaque;    /* per scheme worker data */
+};
+
+/* Runtime worker status informations. Shared in scoreboard */
+typedef struct {
+    int             id;         /* scoreboard id */
+    double          lbstatus;   /* Current lbstatus */
+    double          lbfactor;   /* dynamic lbfactor */
+    apr_size_t      transfered; /* Number of bytes transfered to remote */
+    apr_size_t      readed;     /* Number of bytes readed from remote */
+    apr_size_t      elected;    /* Number of times the worker was elected */
+} proxy_runtime_stat;
+
+/* Runtime worker. */
+typedef struct {
+    proxy_balancer     *b;         /* balancer containing this worker */
+    proxy_worker       *w;
+    proxy_runtime_stat *s;
+} proxy_runtime_worker;
+
+struct proxy_balancer {
+    apr_array_header_t *workers; /* array of proxy_runtime_workers */
+    const char *name;            /* name of the load balancer */
+    const char *sticky;          /* sticky session identifier */
+    int         sticky_force;    /* Disable failover for sticky sessions */
+    apr_interval_time_t timeout; /* Timeout for waiting on free connection */
+    /* XXX: Perhaps we will need the proc mutex too.
+     * Altrough we are only using arithmetic operations
+     * it may lead to a incorrect calculations.
+     * For now use only the thread mutex.
+     */
+#if APR_HAS_THREADS
+    apr_thread_mutex_t  *mutex;  /* Thread lock for updating lb params */
+#endif
+};
+
+/* hooks */
+
+/* Create a set of PROXY_DECLARE(type), PROXY_DECLARE_NONSTD(type) and 
+ * PROXY_DECLARE_DATA with appropriate export and import tags for the platform
+ */
+#if !defined(WIN32)
+#define PROXY_DECLARE(type)            type
+#define PROXY_DECLARE_NONSTD(type)     type
+#define PROXY_DECLARE_DATA
+#elif defined(PROXY_DECLARE_STATIC)
+#define PROXY_DECLARE(type)            type __stdcall
+#define PROXY_DECLARE_NONSTD(type)     type
+#define PROXY_DECLARE_DATA
+#elif defined(PROXY_DECLARE_EXPORT)
+#define PROXY_DECLARE(type)            __declspec(dllexport) type __stdcall
+#define PROXY_DECLARE_NONSTD(type)     __declspec(dllexport) type
+#define PROXY_DECLARE_DATA             __declspec(dllexport)
+#else
+#define PROXY_DECLARE(type)            __declspec(dllimport) type __stdcall
+#define PROXY_DECLARE_NONSTD(type)     __declspec(dllimport) type
+#define PROXY_DECLARE_DATA             __declspec(dllimport)
+#endif
+
+/**
+ * Hook an optional proxy hook.  Unlike static hooks, this uses a macro
+ * instead of a function.
+ */
+#define PROXY_OPTIONAL_HOOK(name,fn,pre,succ,order) \
+        APR_OPTIONAL_HOOK(proxy,name,fn,pre,succ,order)
+
+APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, scheme_handler, (request_rec *r, 
+                          proxy_worker *worker, proxy_server_conf *conf, char *url, 
+                          const char *proxyhost, apr_port_t proxyport))
+APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, canon_handler, (request_rec *r, 
+                          char *url))
+
+APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, create_req, (request_rec *r, request_rec *pr))
+APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, fixups, (request_rec *r)) 
+
+/**
+ * pre request hook.
+ * It will return the most suitable worker at the moment
+ * and coresponding balancer.
+ * The url is rewritten from balancer://cluster/uri to scheme://host:port/uri
+ * and then the scheme_handler is called.
+ *
+ */
+APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, pre_request, (proxy_worker **worker,
+                          proxy_balancer **balancer,
+                          request_rec *r,
+                          proxy_server_conf *conf, char **url))                          
+/**
+ * post request hook.
+ * It is called after request for updating runtime balancer status.
+ */
+APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, post_request, (proxy_worker *worker,
+                          proxy_balancer *balancer, request_rec *r,
+                          proxy_server_conf *conf))
+
+
+/* proxy_util.c */
+
+PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r);
+PROXY_DECLARE(int) ap_proxy_hex2c(const char *x);
+PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x);
+PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t,
+			int isenc);
+PROXY_DECLARE(char *)ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp,
+			 char **passwordp, char **hostp, apr_port_t *port);
+PROXY_DECLARE(const char *)ap_proxy_date_canon(apr_pool_t *p, const char *x);
+PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val);
+PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val);
+PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x);
+PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y);
+PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message);
+PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p);
+PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p);
+PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p);
+PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p);
+PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, apr_sockaddr_t *uri_addr);
+PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r);
+PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb, char *buff, size_t bufflen, int *eos);
+PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key);
+/* DEPRECATED (will be replaced with ap_proxy_connect_backend */
+PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **, const char *, apr_sockaddr_t *, const char *, proxy_server_conf *, server_rec *, apr_pool_t *);
+PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c);
+PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c);
+
+/* Connection pool API */
+PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p, proxy_server_conf *conf, const char *url);
+PROXY_DECLARE(const char *) ap_proxy_add_worker(proxy_worker **worker, apr_pool_t *p, proxy_server_conf *conf, const char *url);
+PROXY_DECLARE(struct proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p, proxy_server_conf *conf, const char *url);
+PROXY_DECLARE(const char *) ap_proxy_add_balancer(proxy_balancer **balancer, apr_pool_t *p, proxy_server_conf *conf, const char *url);
+PROXY_DECLARE(void) ap_proxy_add_worker_to_balancer(apr_pool_t *pool, proxy_balancer *balancer, proxy_worker *worker);
+PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker, proxy_balancer **balancer, request_rec *r, proxy_server_conf *conf, char **url);
+PROXY_DECLARE(int) ap_proxy_determine_connection(apr_pool_t *p, request_rec *r, proxy_server_conf *conf, proxy_worker *worker, proxy_conn_rec *conn,
+                                                 apr_pool_t *ppool, apr_uri_t *uri, char **url, const char *proxyname, apr_port_t proxyport,
+                                                 char *server_portstr, int server_portstr_size);
+PROXY_DECLARE(int) ap_proxy_retry_worker(const char *proxy_function, proxy_worker *worker, server_rec *s);
+PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function, proxy_conn_rec **conn, proxy_worker *worker, server_rec *s);
+PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function, proxy_conn_rec *conn, server_rec *s);
+PROXY_DECLARE(apr_status_t) ap_proxy_close_connection(proxy_conn_rec *conn);
+PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, proxy_conn_rec *conn, proxy_worker *worker, server_rec *s);
+PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function, proxy_conn_rec *conn, conn_rec *c, server_rec *s);
+
+/* Scoreboard */
+#if MODULE_MAGIC_NUMBER_MAJOR > 20020903
+#define PROXY_HAS_SCOREBOARD 1
+#else
+#define PROXY_HAS_SCOREBOARD 0
+#endif
+/* The number of dynamic balancers that can be added */
+#define PROXY_DYNAMIC_BALANCER_LIMIT    16
+PROXY_DECLARE(int) ap_proxy_lb_workers(void);
+
+/* For proxy_util */
+extern module PROXY_DECLARE_DATA proxy_module;
+
+#endif /*MOD_PROXY_H*/
diff --git a/connectors/ajp/proxy/mod_proxy_ajp.dsp b/connectors/ajp/proxy/mod_proxy_ajp.dsp
new file mode 100644
index 0000000..f3e56da
--- /dev/null
+++ b/connectors/ajp/proxy/mod_proxy_ajp.dsp
@@ -0,0 +1,168 @@
+# Microsoft Developer Studio Project File - Name="mod_proxy_ajp" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_proxy_ajp - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_proxy_ajp.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_proxy_ajp.mak" CFG="mod_proxy_ajp - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_proxy_ajp - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_proxy_ajp - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "mod_proxy_ajp - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_ajp_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /machine:I386 /out:"Release/mod_proxy_ajp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ajp.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:"Release/mod_proxy_ajp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ajp.so /opt:ref
+
+!ELSEIF  "$(CFG)" == "mod_proxy_ajp - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_ajp_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/mod_proxy_ajp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ajp.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/mod_proxy_ajp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ajp.so
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_proxy_ajp - Win32 Release"
+# Name "mod_proxy_ajp - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=.\proxy_ajp.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter ".h"
+# Begin Source File
+
+SOURCE=.\mod_proxy.h
+# End Source File
+# End Group
+# Begin Group "Ajp Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\ajp\ajp.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ajp\ajp_header.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ajp\ajp_header.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ajp\ajp_link.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ajp\ajp_logon.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ajp\ajp_logon.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ajp\ajp_msg.c
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\build\win32\win32ver.awk
+
+!IF  "$(CFG)" == "mod_proxy_ajp - Win32 Release"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_proxy_ajp.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	awk -f ../../build/win32/win32ver.awk mod_proxy_ajp.so "proxy_ajp_module for Apache" ../../include/ap_release.h > .\mod_proxy_ajp.rc
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "mod_proxy_ajp - Win32 Debug"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_proxy_ajp.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	awk -f ../../build/win32/win32ver.awk mod_proxy_ajp.so "proxy_ajp_module for Apache" ../../include/ap_release.h > .\mod_proxy_ajp.rc
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# End Target
+# End Project
diff --git a/connectors/ajp/proxy/mod_proxy_balancer.dsp b/connectors/ajp/proxy/mod_proxy_balancer.dsp
new file mode 100644
index 0000000..0bb82bc
--- /dev/null
+++ b/connectors/ajp/proxy/mod_proxy_balancer.dsp
@@ -0,0 +1,136 @@
+# Microsoft Developer Studio Project File - Name="mod_proxy_balancer" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_proxy_balancer - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_proxy_balancer.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_proxy_balancer.mak" CFG="mod_proxy_balancer - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_proxy_balancer - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_proxy_balancer - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "mod_proxy_balancer - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_balancer_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /machine:I386 /out:"Release/mod_proxy_balancer.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_balancer.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Release/mod_proxy_balancer.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_balancer.so /opt:ref
+
+!ELSEIF  "$(CFG)" == "mod_proxy_balancer - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_balancer_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/mod_proxy_balancer.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_balancer.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/mod_proxy_balancer.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_balancer.so
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_proxy_balancer - Win32 Release"
+# Name "mod_proxy_balancer - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=.\proxy_balancer.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter ".h"
+# Begin Source File
+
+SOURCE=.\mod_proxy.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\build\win32\win32ver.awk
+
+!IF  "$(CFG)" == "mod_proxy_balancer - Win32 Release"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_proxy_balancer.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	awk -f ../../build/win32/win32ver.awk mod_proxy_balancer.so "proxy_balancer_module for Apache" ../../include/ap_release.h > .\mod_proxy_balancer.rc
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "mod_proxy_balancer - Win32 Debug"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_proxy_balancer.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	awk -f ../../build/win32/win32ver.awk mod_proxy_balancer.so "proxy_balancer_module for Apache" ../../include/ap_release.h > .\mod_proxy_balancer.rc
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# End Target
+# End Project
diff --git a/connectors/ajp/proxy/mod_proxy_connect.dsp b/connectors/ajp/proxy/mod_proxy_connect.dsp
new file mode 100644
index 0000000..b4b639f
--- /dev/null
+++ b/connectors/ajp/proxy/mod_proxy_connect.dsp
@@ -0,0 +1,136 @@
+# Microsoft Developer Studio Project File - Name="mod_proxy_connect" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_proxy_connect - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_proxy_connect.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_proxy_connect.mak" CFG="mod_proxy_connect - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_proxy_connect - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_proxy_connect - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "mod_proxy_connect - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_connect_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /machine:I386 /out:"Release/mod_proxy_connect.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_connect.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Release/mod_proxy_connect.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_connect.so /opt:ref
+
+!ELSEIF  "$(CFG)" == "mod_proxy_connect - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_connect_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/mod_proxy_connect.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_connect.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/mod_proxy_connect.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_connect.so
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_proxy_connect - Win32 Release"
+# Name "mod_proxy_connect - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=.\proxy_connect.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter ".h"
+# Begin Source File
+
+SOURCE=.\mod_proxy.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\build\win32\win32ver.awk
+
+!IF  "$(CFG)" == "mod_proxy_connect - Win32 Release"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_proxy_connect.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	awk -f ../../build/win32/win32ver.awk mod_proxy_connect.so "proxy_connect_module for Apache" ../../include/ap_release.h > .\mod_proxy_connect.rc
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "mod_proxy_connect - Win32 Debug"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_proxy_connect.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	awk -f ../../build/win32/win32ver.awk mod_proxy_connect.so "proxy_connect_module for Apache" ../../include/ap_release.h > .\mod_proxy_connect.rc
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# End Target
+# End Project
diff --git a/connectors/ajp/proxy/mod_proxy_ftp.dsp b/connectors/ajp/proxy/mod_proxy_ftp.dsp
new file mode 100644
index 0000000..26bdded
--- /dev/null
+++ b/connectors/ajp/proxy/mod_proxy_ftp.dsp
@@ -0,0 +1,136 @@
+# Microsoft Developer Studio Project File - Name="mod_proxy_ftp" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_proxy_ftp - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_proxy_ftp.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_proxy_ftp.mak" CFG="mod_proxy_ftp - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_proxy_ftp - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_proxy_ftp - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "mod_proxy_ftp - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_ftp_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /machine:I386 /out:"Release/mod_proxy_ftp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ftp.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Release/mod_proxy_ftp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ftp.so /opt:ref
+
+!ELSEIF  "$(CFG)" == "mod_proxy_ftp - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_ftp_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/mod_proxy_ftp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ftp.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/mod_proxy_ftp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ftp.so
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_proxy_ftp - Win32 Release"
+# Name "mod_proxy_ftp - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=.\proxy_ftp.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter ".h"
+# Begin Source File
+
+SOURCE=.\mod_proxy.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\build\win32\win32ver.awk
+
+!IF  "$(CFG)" == "mod_proxy_ftp - Win32 Release"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_proxy_ftp.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	awk -f ../../build/win32/win32ver.awk mod_proxy_ftp.so "proxy_ftp_module for Apache" ../../include/ap_release.h > .\mod_proxy_ftp.rc
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "mod_proxy_ftp - Win32 Debug"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_proxy_ftp.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	awk -f ../../build/win32/win32ver.awk mod_proxy_ftp.so "proxy_ftp_module for Apache" ../../include/ap_release.h > .\mod_proxy_ftp.rc
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# End Target
+# End Project
diff --git a/connectors/ajp/proxy/mod_proxy_http.dsp b/connectors/ajp/proxy/mod_proxy_http.dsp
new file mode 100644
index 0000000..8cecf25
--- /dev/null
+++ b/connectors/ajp/proxy/mod_proxy_http.dsp
@@ -0,0 +1,136 @@
+# Microsoft Developer Studio Project File - Name="mod_proxy_http" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_proxy_http - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_proxy_http.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_proxy_http.mak" CFG="mod_proxy_http - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_proxy_http - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_proxy_http - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "mod_proxy_http - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_http_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /machine:I386 /out:"Release/mod_proxy_http.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_http.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Release/mod_proxy_http.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_http.so /opt:ref
+
+!ELSEIF  "$(CFG)" == "mod_proxy_http - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_http_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/mod_proxy_http.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_http.so
+# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/mod_proxy_http.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_http.so
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_proxy_http - Win32 Release"
+# Name "mod_proxy_http - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=.\proxy_http.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter ".h"
+# Begin Source File
+
+SOURCE=.\mod_proxy.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\build\win32\win32ver.awk
+
+!IF  "$(CFG)" == "mod_proxy_http - Win32 Release"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_proxy_http.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	awk -f ../../build/win32/win32ver.awk mod_proxy_http.so "proxy_http_module for Apache" ../../include/ap_release.h > .\mod_proxy_http.rc
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "mod_proxy_http - Win32 Debug"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating Version Resource
+InputPath=..\..\build\win32\win32ver.awk
+
+".\mod_proxy_http.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	awk -f ../../build/win32/win32ver.awk mod_proxy_http.so "proxy_http_module for Apache" ../../include/ap_release.h > .\mod_proxy_http.rc
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# End Target
+# End Project
diff --git a/connectors/ajp/proxy/proxy_ajp.c b/connectors/ajp/proxy/proxy_ajp.c
new file mode 100644
index 0000000..92a4197
--- /dev/null
+++ b/connectors/ajp/proxy/proxy_ajp.c
@@ -0,0 +1,420 @@
+/* Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* AJP routines for Apache proxy */
+
+#include "mod_proxy.h"
+#include "ajp.h"
+
+module AP_MODULE_DECLARE_DATA proxy_ajp_module;
+
+/*
+ * Canonicalise http-like URLs.
+ *  scheme is the scheme for the URL
+ *  url    is the URL starting with the first '/'
+ *  def_port is the default port for this scheme.
+ */
+int ap_proxy_ajp_canon(request_rec *r, char *url)
+{
+    char *host, *path, *search, sport[7];
+    const char *err;
+    const char *scheme;
+    apr_port_t port, def_port;
+
+    ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+             "proxy: AJP: canonicalising URL %s", url);
+
+    /* ap_port_of_scheme() */
+    if (strncasecmp(url, "ajp:", 4) == 0) {
+        url += 4;
+        scheme = "ajp";
+    }    
+    /* XXX This is probably faulty */ 
+    else if (strncasecmp(url, "ajps:", 5) == 0) {
+        url += 5;
+        scheme = "ajps";
+    }
+    else {
+        return DECLINED;
+    }
+    def_port = apr_uri_port_of_scheme(scheme);
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+             "proxy: AJP: canonicalising URL %s", url);
+
+    /* do syntatic check.
+     * We break the URL into host, port, path, search
+     */
+    port = def_port;
+    err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
+    if (err) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                      "error parsing URL %s: %s",
+                      url, err);
+        return HTTP_BAD_REQUEST;
+    }
+
+    /* now parse path/search args, according to rfc1738 */
+    /* N.B. if this isn't a true proxy request, then the URL _path_
+     * has already been decoded.  True proxy requests have r->uri
+     * == r->unparsed_uri, and no others have that property.
+     */
+    if (r->uri == r->unparsed_uri) {
+        search = strchr(url, '?');
+        if (search != NULL)
+            *(search++) = '\0';
+    }
+    else
+        search = r->args;
+
+    /* process path */
+    path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq);
+    if (path == NULL)
+        return HTTP_BAD_REQUEST;
+
+    if (port != def_port)
+        apr_snprintf(sport, sizeof(sport), ":%d", port);
+    else
+        sport[0] = '\0';
+
+    if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
+        host = apr_pstrcat(r->pool, "[", host, "]", NULL);
+    }
+    r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport, 
+            "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
+    return OK;
+}
+ 
+static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r,
+                                proxy_conn_rec *conn, 
+                                conn_rec *origin, 
+                                proxy_server_conf *conf,
+                                apr_uri_t *uri,
+                                char *url, char *server_portstr)
+{
+    apr_status_t status;
+    int result;
+    apr_bucket_brigade *input_brigade;
+
+    /*
+     * Send the AJP request to the remote server
+     */
+
+    /* send request headers */
+    status = ajp_send_header(conn->sock, r);
+    if (status != APR_SUCCESS) {
+        conn->close++;
+        ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+                     "proxy: AJP: request failed to %pI (%s)",
+                     conn->worker->cp->addr,
+                     conn->worker->hostname);
+        return HTTP_SERVICE_UNAVAILABLE;
+    }
+
+    /* read the first bloc of data */
+    input_brigade = apr_brigade_create(p, r->connection->bucket_alloc);
+    status = ap_get_brigade(r->input_filters, input_brigade,
+                            AP_MODE_READBYTES, APR_BLOCK_READ,
+                            AJP13_MAX_SEND_BODY_SZ);
+ 
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: ap_get_brigade failed");
+        apr_brigade_destroy(input_brigade);
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    /* have something */
+    if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: APR_BUCKET_IS_EOS");
+    }
+
+    if (1) { /* XXXX only when something to send ? */
+        ajp_msg_t *msg;
+        apr_size_t bufsiz;
+        char *buff;
+        status = ajp_alloc_data_msg(r, &buff, &bufsiz, &msg);
+        if (status != APR_SUCCESS) {
+            return status;
+        }
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: data to read (max %d at %08x)", bufsiz, buff);
+
+        /* XXXX calls apr_brigade_flatten... */
+        status = apr_brigade_flatten(input_brigade, buff, &bufsiz);
+        if (status != APR_SUCCESS) {
+             ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+                     "proxy: apr_brigade_flatten");
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: got %d byte of data", bufsiz);
+        if (bufsiz > 0) {
+            status = ajp_send_data_msg(conn->sock, r, msg, bufsiz);
+            if (status != APR_SUCCESS) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+                             "proxy: request failed to %pI (%s)",
+                             conn->worker->cp->addr,
+                             conn->worker->hostname);
+                return HTTP_SERVICE_UNAVAILABLE;
+            }
+        }
+    }
+
+    /* read the response */
+    status = ajp_read_header(conn->sock, r,
+                             (ajp_msg_t **)&(conn->data));
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+                     "proxy: request failed to %pI (%s)",
+                     conn->worker->cp->addr,
+                     conn->worker->hostname);
+        return HTTP_SERVICE_UNAVAILABLE;
+    }
+
+    /* parse the reponse */
+    result = ajp_parse_type(r, conn->data);
+    if (result == CMD_AJP13_SEND_HEADERS) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: got response from %pI (%s)",
+                     conn->worker->cp->addr,
+                     conn->worker->hostname);
+        return HTTP_SERVICE_UNAVAILABLE;
+    }
+
+    /* XXXX: need logic to send the rest of the data */
+/*
+    status = ajp_send_data(p_conn->sock,r);
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+                     "proxy: request failed to %pI (%s)",
+                     p_conn->addr, p_conn->name);
+        return status;
+    }
+ */
+
+    return OK;
+}
+
+/*
+ * Process the AJP response, data already contains the first part of it.
+ */
+static int ap_proxy_ajp_process_response(apr_pool_t * p, request_rec *r,
+                                         conn_rec *origin,
+                                         proxy_conn_rec *backend,
+                                         proxy_server_conf *conf,
+                                         char *server_portstr) 
+{
+    conn_rec *c = r->connection;
+    apr_bucket *e;
+    apr_bucket_brigade *bb;
+    int type;
+    apr_status_t status;
+
+    bb = apr_brigade_create(p, c->bucket_alloc);
+    
+    type = ajp_parse_type(r, backend->data);
+    status = APR_SUCCESS;
+    while (type != CMD_AJP13_END_RESPONSE) {
+        if (type == CMD_AJP13_SEND_HEADERS) {
+            /* AJP13_SEND_HEADERS: process them */
+            status = ajp_parse_header(r, backend->data); 
+            if (status != APR_SUCCESS) {
+                break;
+            }
+        } 
+        else if  (type == CMD_AJP13_SEND_BODY_CHUNK) {
+            /* AJP13_SEND_BODY_CHUNK: piece of data */
+            apr_uint16_t size;
+            char *buff;
+
+            status = ajp_parse_data(r, backend->data, &size, &buff);
+            e = apr_bucket_transient_create(buff, size, c->bucket_alloc);
+            APR_BRIGADE_INSERT_TAIL(bb, e);
+        } 
+        else {
+            status = APR_EGENERAL;
+            break;
+        }
+        /* Read the next message */
+        status = ajp_read_header(backend->sock, r,
+                                 (ajp_msg_t **)&(backend->data));
+        if (status != APR_SUCCESS) {
+            break;
+        }
+        type = ajp_parse_type(r, backend->data);
+    }
+    if (status != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                      "proxy: error reading headers from remote "
+                      "server %s:%d",
+                      backend->worker->cp->addr,
+                      backend->worker->hostname);
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             "Error reading from remote server");
+    }
+
+    /* The page is ready give it to the rest of the logic */
+    e = apr_bucket_eos_create(c->bucket_alloc);
+    APR_BRIGADE_INSERT_TAIL(bb, e);
+    if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                      "proxy: error processing body");
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             "Error reading from remote server");
+    } 
+
+    return OK;
+}
+
+/*
+ * This handles http:// URLs, and other URLs using a remote proxy over http
+ * If proxyhost is NULL, then contact the server directly, otherwise
+ * go via the proxy.
+ * Note that if a proxy is used, then URLs other than http: can be accessed,
+ * also, if we have trouble which is clearly specific to the proxy, then
+ * we return DECLINED so that we can try another proxy. (Or the direct
+ * route.)
+ */
+int ap_proxy_ajp_handler(request_rec *r, proxy_worker *worker,
+                         proxy_server_conf *conf,
+                         char *url, const char *proxyname, 
+                         apr_port_t proxyport)
+{
+    int status;
+    char server_portstr[32];
+    conn_rec *origin = NULL;
+    proxy_conn_rec *backend = NULL;
+    int is_ssl = 0;
+    const char *scheme = "AJP";
+
+    /* Note: Memory pool allocation.
+     * A downstream keepalive connection is always connected to the existence
+     * (or not) of an upstream keepalive connection. If this is not done then
+     * load balancing against multiple backend servers breaks (one backend
+     * server ends up taking 100% of the load), and the risk is run of
+     * downstream keepalive connections being kept open unnecessarily. This
+     * keeps webservers busy and ties up resources.
+     *
+     * As a result, we allocate all sockets out of the upstream connection
+     * pool, and when we want to reuse a socket, we check first whether the
+     * connection ID of the current upstream connection is the same as that
+     * of the connection when the socket was opened.
+     */
+    apr_pool_t *p = r->connection->pool;
+    conn_rec *c = r->connection;
+    apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri));
+
+    
+    if (strncasecmp(url, "ajp:", 4) != 0) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: AJP: declining URL %s", url);
+        return DECLINED;
+    }
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+             "proxy: AJP: serving URL %s", url);
+    
+
+    /* only use stored info for top-level pages. Sub requests don't share 
+     * in keepalives
+     */
+#if 0
+    if (!r->main) {
+        backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config,
+                                                      &proxy_ajp_module);
+    }
+#endif
+    /* create space for state information */
+    if (!backend) {
+        status = ap_proxy_acquire_connection(scheme, &backend, worker, r->server);
+        if (status != OK) {
+            if (backend) {
+                backend->close_on_recycle = 1;
+                ap_proxy_release_connection(scheme, backend, r->server);
+            }
+            return status;
+        }
+#if 0
+        if (!r->main) {
+            ap_set_module_config(c->conn_config, &proxy_ajp_module, backend);
+        }
+#endif
+    }
+
+    backend->is_ssl = 0;
+    backend->close_on_recycle = 0;
+
+    /* Step One: Determine Who To Connect To */
+    status = ap_proxy_determine_connection(p, r, conf, worker, backend, c->pool,
+                                           uri, &url, proxyname, proxyport,
+                                           server_portstr,
+                                           sizeof(server_portstr));
+
+    if (status != OK)
+        goto cleanup;
+    /* Step Two: Make the Connection */
+    if (ap_proxy_connect_backend(scheme, backend, worker, r->server)) {
+        status = HTTP_SERVICE_UNAVAILABLE;
+        goto cleanup;
+    }
+#if 0
+    /* XXX: we don't need to create the bound client connection */
+
+    /* Step Three: Create conn_rec */
+    if (!backend->connection) {
+        status = ap_proxy_connection_create(scheme, backend, c, r->server);
+        if (status != OK)
+            goto cleanup;
+    }
+#endif
+   
+   
+    /* Step Four: Send the Request */
+    status = ap_proxy_ajp_request(p, r, backend, origin, conf, uri, url,
+                                  server_portstr);
+    if (status != OK)
+        goto cleanup;
+
+    /* Step Five: Receive the Response */
+    status = ap_proxy_ajp_process_response(p, r, origin, backend,
+                                           conf, server_portstr);
+cleanup:
+#if 0
+    /* Clear the module config */
+    ap_set_module_config(c->conn_config, &proxy_ajp_module, NULL);
+#endif
+    /* Do not close the socket */
+    ap_proxy_release_connection(scheme, backend, r->server);
+    return status;
+}
+
+static void ap_proxy_http_register_hook(apr_pool_t *p)
+{
+    proxy_hook_scheme_handler(ap_proxy_ajp_handler, NULL, NULL, APR_HOOK_FIRST);
+    proxy_hook_canon_handler(ap_proxy_ajp_canon, NULL, NULL, APR_HOOK_FIRST);
+}
+
+module AP_MODULE_DECLARE_DATA proxy_ajp_module = {
+    STANDARD20_MODULE_STUFF,
+    NULL,              /* create per-directory config structure */
+    NULL,              /* merge per-directory config structures */
+    NULL,              /* create per-server config structure */
+    NULL,              /* merge per-server config structures */
+    NULL,              /* command apr_table_t */
+    ap_proxy_http_register_hook/* register hooks */
+};
+
diff --git a/connectors/ajp/proxy/proxy_balancer.c b/connectors/ajp/proxy/proxy_balancer.c
new file mode 100644
index 0000000..04e3ce0
--- /dev/null
+++ b/connectors/ajp/proxy/proxy_balancer.c
@@ -0,0 +1,387 @@
+/* Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Load balancer module for Apache proxy */
+
+#define CORE_PRIVATE
+
+#include "mod_proxy.h"
+#include "ap_mpm.h"
+#include "apr_version.h"
+
+module AP_MODULE_DECLARE_DATA proxy_balancer_module;
+
+#if APR_HAS_THREADS
+#define PROXY_BALANCER_LOCK(b)      apr_thread_mutex_lock((b)->mutex)
+#define PROXY_BALANCER_UNLOCK(b)    apr_thread_mutex_unlock((b)->mutex)
+#else
+#define PROXY_BALANCER_LOCK(b)      APR_SUCCESS
+#define PROXY_BALANCER_UNLOCK(b)    APR_SUCCESS
+#endif
+
+
+/* Retrieve the parameter with the given name                                */
+static char *get_path_param(apr_pool_t *pool, char *url,
+                            const char *name)
+{
+    char *path = NULL;
+    
+    for (path = strstr(url, name); path; path = strstr(path + 1, name)) {
+        path += (strlen(name) + 1);
+        if (*path == '=') {
+            /*
+             * Session path was found, get it's value
+             */
+            ++path;
+            if (strlen(path)) {
+                char *q;
+                path = apr_pstrdup(pool, path);
+                if ((q = strchr(path, '?')))
+                    *q = '\0';
+                return path;
+            }
+        }
+    }
+    return NULL;
+}
+
+static char *get_cookie_param(request_rec *r, const char *name)
+{
+    const char *cookies;
+    const char *start_cookie;
+
+    if ((cookies = apr_table_get(r->headers_in, "Cookie"))) {
+        for (start_cookie = strstr(cookies, name); start_cookie; 
+             start_cookie = strstr(start_cookie + 1, name)) {
+            if (start_cookie == cookies ||
+                start_cookie[-1] == ';' ||
+                start_cookie[-1] == ',' ||
+                isspace(start_cookie[-1])) {
+                
+                start_cookie += strlen(name);
+                while(*start_cookie && isspace(*start_cookie))
+                    ++start_cookie;
+                if (*start_cookie == '=' && start_cookie[1]) {
+                    /*
+                     * Session cookie was found, get it's value
+                     */
+                    char *end_cookie, *cookie;
+                    ++start_cookie;
+                    cookie = apr_pstrdup(r->pool, start_cookie);
+                    if ((end_cookie = strchr(cookie, ';')) != NULL)
+                        *end_cookie = '\0';
+                    if((end_cookie = strchr(cookie, ',')) != NULL)
+                        *end_cookie = '\0';
+                    return cookie;
+                }
+            }
+        }     
+    }
+    return NULL;
+}
+
+static proxy_runtime_worker *find_route_worker(proxy_balancer *balancer,
+                                               const char *route)
+{
+    int i;
+    proxy_runtime_worker *worker = (proxy_runtime_worker *)balancer->workers->elts;
+    for (i = 0; i < balancer->workers->nelts; i++) {
+        if (worker->w->route && strcmp(worker->w->route, route) == 0) {
+            return worker;
+        }
+        worker++;
+    }
+    return NULL;
+}
+
+static proxy_runtime_worker *find_session_route(proxy_balancer *balancer,
+                                                request_rec *r,
+                                                char **route,
+                                                char **url)
+{
+    if (!balancer->sticky)
+        return NULL;
+    /* Try to find the sticky route inside url */
+    *route = get_path_param(r->pool, *url, balancer->sticky);
+    if (!*route)
+        *route = get_cookie_param(r, balancer->sticky);
+    if (*route) {
+        proxy_runtime_worker *worker =  find_route_worker(balancer, *route);
+        /* TODO: make worker status codes */
+        /* See if we have a redirection route */
+        if (worker && !PROXY_WORKER_IS_USABLE(worker->w)) {
+            if (worker->w->redirect)
+                worker = find_route_worker(balancer, worker->w->redirect);
+            /* Check if the redirect worker is usable */
+            if (worker && !PROXY_WORKER_IS_USABLE(worker->w))
+                worker = NULL;
+        }
+        else
+            worker = NULL;
+        return worker;
+    }
+    else
+        return NULL;
+}
+
+static proxy_runtime_worker *find_best_worker(proxy_balancer *balancer,
+                                              request_rec *r)
+{
+    int i;
+    double total_factor = 0.0;
+    proxy_runtime_worker *worker = (proxy_runtime_worker *)balancer->workers->elts;
+    proxy_runtime_worker *candidate = NULL;
+
+    /* First try to see if we have available candidate */
+    for (i = 0; i < balancer->workers->nelts; i++) {
+        /* See if the retry timeout is ellapsed
+         * for the workers flagged as IN_ERROR
+         */
+        if (!PROXY_WORKER_IS_USABLE(worker->w))
+            ap_proxy_retry_worker("BALANCER", worker->w, r->server);
+        /* If the worker is not in error state
+         * or not disabled.
+         */
+        if (PROXY_WORKER_IS_USABLE(worker->w)) {
+            if (!candidate)
+                candidate = worker;
+            else {
+                /* See if the worker has a larger number of free channels */
+                if (worker->w->cp->nfree > candidate->w->cp->nfree)
+                    candidate = worker;
+            }
+            /* Total factor should allways be 100.
+             * This is for cases when worker is in error state.
+             * It will force the even request distribution
+             */
+            total_factor += worker->s->lbfactor;
+        }
+        worker++;
+    }
+    if (!candidate) {
+        /* All the workers are in error state or disabled.
+         * If the balancer has a timeout wait.
+         */
+#if APR_HAS_THREADS
+        if (balancer->timeout) {
+            /* XXX: This can perhaps be build using some 
+             * smarter mechanism, like tread_cond.
+             * But since the statuses can came from 
+             * different childs, use the provided algo. 
+             */
+            apr_interval_time_t timeout = balancer->timeout;
+            apr_interval_time_t step, tval = 0;
+            balancer->timeout = 0;
+            step = timeout / 100;
+            while (tval < timeout) {
+                apr_sleep(step);
+                /* Try again */
+                if ((candidate = find_best_worker(balancer, r)))
+                    break;
+                tval += step;
+            }
+            /* restore the timeout */
+            balancer->timeout = timeout;
+        }
+#endif
+    }
+    else {
+        /* We have at least one candidate that is not in
+         * error state or disabled.
+         * Now calculate the appropriate one 
+         */
+        worker = (proxy_runtime_worker *)balancer->workers->elts;
+        for (i = 0; i < balancer->workers->nelts; i++) {
+            /* If the worker is not error state
+             * or not in disabled mode
+             */
+            if (PROXY_WORKER_IS_USABLE(worker->w)) {
+                /* 1. Find the worker with higher lbstatus.
+                 * Lbstatus is of higher importance then
+                 * the number of empty slots.
+                 */
+                if (worker->s->lbstatus > candidate->s->lbstatus) {
+                    candidate = worker;
+                }
+            }
+            worker++;
+        }
+        worker = (proxy_runtime_worker *)balancer->workers->elts;
+        for (i = 0; i < balancer->workers->nelts; i++) {
+            /* If the worker is not error state
+             * or not in disabled mode
+             */
+            if (PROXY_WORKER_IS_USABLE(worker->w)) {
+                /* XXX: The lbfactor can be update using bytes transfered
+                 * Right now, use the round-robin scheme
+                 */
+                worker->s->lbstatus += worker->s->lbfactor;
+                if (worker->s->lbstatus >= total_factor)
+                    worker->s->lbstatus = worker->s->lbfactor;
+            }
+            worker++;
+        }
+    }
+    return candidate;
+}
+
+static int rewrite_url(request_rec *r, proxy_worker *worker,
+                        char **url)
+{
+    const char *scheme = strstr(*url, "://");
+    const char *path = NULL;
+    
+    if (scheme)
+        path = strchr(scheme + 3, '/');
+
+    /* we break the URL into host, port, uri */
+    if (!worker) {
+        return ap_proxyerror(r, HTTP_BAD_REQUEST, apr_pstrcat(r->pool,
+                             "missing worker. URI cannot be parsed: ", *url,
+                             NULL));
+    }
+
+    *url = apr_pstrcat(r->pool, worker->name, path, NULL);
+   
+    return OK;
+}
+
+static int proxy_balancer_pre_request(proxy_worker **worker,
+                                      proxy_balancer **balancer,
+                                      request_rec *r,
+                                      proxy_server_conf *conf, char **url)
+{
+    int access_status;
+    proxy_runtime_worker *runtime;
+    char *route;
+    apr_status_t rv;
+
+    *worker = NULL;
+    /* Spet 1: check if the url is for us */
+    if (!(*balancer = ap_proxy_get_balancer(r->pool, conf, *url)))
+        return DECLINED;
+    
+    /* Step 2: find the session route */
+    
+    runtime = find_session_route(*balancer, r, &route, url);
+    if (!runtime) {
+        if (route && (*balancer)->sticky_force) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                         "proxy: BALANCER: (%s). All workers are in error state for route (%s)",
+                         (*balancer)->name, route);
+            return HTTP_SERVICE_UNAVAILABLE;
+        }
+    }
+    else {
+        int i;
+        proxy_runtime_worker *workers;
+        /* We have a sticky load balancer */
+        *worker = runtime->w;
+        /* Update the workers status 
+         * so that even session routes get
+         * into account.
+         */
+        workers = (proxy_runtime_worker *)(*balancer)->workers->elts;
+        for (i = 0; i < (*balancer)->workers->nelts; i++) {
+            /* For now assume that all workers are OK */
+            workers->s->lbstatus += workers->s->lbfactor;
+            if (workers->s->lbstatus >= 100.0)
+                workers->s->lbstatus = workers->s->lbfactor;
+            workers++;
+        }
+    }
+    /* Lock the LoadBalancer
+     * XXX: perhaps we need the process lock here
+     */
+    if ((rv = PROXY_BALANCER_LOCK(*balancer)) != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
+                     "proxy: BALANCER: lock");
+        return DECLINED;
+    }
+    if (!*worker) {
+        runtime = find_best_worker(*balancer, r);
+        if (!runtime) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                         "proxy: BALANCER: (%s). All workers are in error state",
+                         (*balancer)->name);
+        
+            PROXY_BALANCER_UNLOCK(*balancer);
+            return HTTP_SERVICE_UNAVAILABLE;
+        }
+        *worker = runtime->w;
+    }
+    /* Decrease the free channels number */
+    if ((*worker)->cp->nfree)
+        --(*worker)->cp->nfree;
+
+    PROXY_BALANCER_UNLOCK(*balancer);
+    
+    access_status = rewrite_url(r, *worker, url);
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "proxy_balancer_pre_request rewriting to %s", *url);
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "proxy_balancer_pre_request worker (%s) free %d",
+                 (*worker)->name,
+                 (*worker)->cp->nfree);
+
+    return access_status;
+} 
+
+static int proxy_balancer_post_request(proxy_worker *worker,
+                                       proxy_balancer *balancer,
+                                       request_rec *r,
+                                       proxy_server_conf *conf)
+{
+    int access_status;
+    if (!balancer)
+        access_status = DECLINED;
+    else { 
+        apr_status_t rv;
+        if ((rv = PROXY_BALANCER_LOCK(balancer)) != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
+                         "proxy: BALANCER: lock");
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+        /* increase the free channels number */
+        if (worker->cp->nfree)
+            worker->cp->nfree++;
+        /* TODO: calculate the bytes transfered */
+
+        /* TODO: update the scoreboard status */
+
+        PROXY_BALANCER_UNLOCK(balancer);        
+        access_status = OK;
+    }
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+             "proxy_balancer_post_request for (%s)", balancer->name);
+
+    return access_status;
+} 
+
+static void ap_proxy_balancer_register_hook(apr_pool_t *p)
+{
+    proxy_hook_pre_request(proxy_balancer_pre_request, NULL, NULL, APR_HOOK_FIRST);    
+    proxy_hook_post_request(proxy_balancer_post_request, NULL, NULL, APR_HOOK_FIRST);    
+}
+
+module AP_MODULE_DECLARE_DATA proxy_balancer_module = {
+    STANDARD20_MODULE_STUFF,
+    NULL,		/* create per-directory config structure */
+    NULL,		/* merge per-directory config structures */
+    NULL,		/* create per-server config structure */
+    NULL,		/* merge per-server config structures */
+    NULL,		/* command apr_table_t */
+    ap_proxy_balancer_register_hook	/* register hooks */
+};
diff --git a/connectors/ajp/proxy/proxy_connect.c b/connectors/ajp/proxy/proxy_connect.c
new file mode 100644
index 0000000..fae03dc
--- /dev/null
+++ b/connectors/ajp/proxy/proxy_connect.c
@@ -0,0 +1,386 @@
+/* Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* CONNECT method for Apache proxy */
+
+#define CORE_PRIVATE
+
+#include "mod_proxy.h"
+#include "apr_poll.h"
+
+module AP_MODULE_DECLARE_DATA proxy_connect_module;
+
+int ap_proxy_connect_canon(request_rec *r, char *url);
+int ap_proxy_connect_handler(request_rec *r, proxy_server_conf *conf, 
+                             char *url, const char *proxyname, 
+                             apr_port_t proxyport);
+
+/*  
+ * This handles Netscape CONNECT method secure proxy requests.
+ * A connection is opened to the specified host and data is
+ * passed through between the WWW site and the browser.
+ *
+ * This code is based on the INTERNET-DRAFT document
+ * "Tunneling SSL Through a WWW Proxy" currently at
+ * http://www.mcom.com/newsref/std/tunneling_ssl.html.
+ *
+ * If proxyhost and proxyport are set, we send a CONNECT to 
+ * the specified proxy..  
+ *
+ * FIXME: this doesn't log the number of bytes sent, but
+ *        that may be okay, since the data is supposed to
+ *        be transparent. In fact, this doesn't log at all
+ *        yet. 8^)
+ * FIXME: doesn't check any headers initally sent from the
+ *        client.
+ * FIXME: should allow authentication, but hopefully the
+ *        generic proxy authentication is good enough.
+ * FIXME: no check for r->assbackwards, whatever that is.
+ */
+
+static int
+allowed_port(proxy_server_conf *conf, int port)
+{
+    int i;
+    int *list = (int *) conf->allowed_connect_ports->elts;
+
+    for(i = 0; i < conf->allowed_connect_ports->nelts; i++) {
+	if(port == list[i])
+	    return 1;
+    }
+    return 0;
+}
+
+/* canonicalise CONNECT URLs. */
+int ap_proxy_connect_canon(request_rec *r, char *url)
+{
+
+    if (r->method_number != M_CONNECT) {
+	return DECLINED;
+    }
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+		 "proxy: CONNECT: canonicalising URL %s", url);
+
+    return OK;
+}
+
+/* CONNECT handler */
+int ap_proxy_connect_handler(request_rec *r, proxy_server_conf *conf, 
+                             char *url, const char *proxyname, 
+                             apr_port_t proxyport)
+{
+    apr_pool_t *p = r->pool;
+    apr_socket_t *sock;
+    apr_status_t err, rv;
+    apr_size_t i, o, nbytes;
+    char buffer[HUGE_STRING_LEN];
+    apr_socket_t *client_socket = ap_get_module_config(r->connection->conn_config, &core_module);
+    int failed;
+    apr_pollset_t *pollset;
+    apr_pollfd_t pollfd;
+    const apr_pollfd_t *signalled;
+    apr_int32_t pollcnt;
+    apr_int16_t pollevent;
+    apr_sockaddr_t *uri_addr, *connect_addr;
+
+    apr_uri_t uri;
+    const char *connectname;
+    int connectport = 0;
+
+    /* is this for us? */
+    if (r->method_number != M_CONNECT) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+		     "proxy: CONNECT: declining URL %s", url);
+	return DECLINED;
+    }
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+		 "proxy: CONNECT: serving URL %s", url);
+
+
+    /*
+     * Step One: Determine Who To Connect To
+     *
+     * Break up the URL to determine the host to connect to
+     */
+
+    /* we break the URL into host, port, uri */
+    if (APR_SUCCESS != apr_uri_parse_hostinfo(p, url, &uri)) {
+	return ap_proxyerror(r, HTTP_BAD_REQUEST,
+			     apr_pstrcat(p, "URI cannot be parsed: ", url, NULL));
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+		 "proxy: CONNECT: connecting %s to %s:%d", url, uri.hostname, uri.port);
+
+    /* do a DNS lookup for the destination host */
+    err = apr_sockaddr_info_get(&uri_addr, uri.hostname, APR_UNSPEC, uri.port, 0, p);
+
+    /* are we connecting directly, or via a proxy? */
+    if (proxyname) {
+	connectname = proxyname;
+	connectport = proxyport;
+        err = apr_sockaddr_info_get(&connect_addr, proxyname, APR_UNSPEC, proxyport, 0, p);
+    }
+    else {
+	connectname = uri.hostname;
+	connectport = uri.port;
+	connect_addr = uri_addr;
+    }
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+		 "proxy: CONNECT: connecting to remote proxy %s on port %d", connectname, connectport);
+
+    /* check if ProxyBlock directive on this host */
+    if (OK != ap_proxy_checkproxyblock(r, conf, uri_addr)) {
+	return ap_proxyerror(r, HTTP_FORBIDDEN,
+			     "Connect to remote machine blocked");
+    }
+
+    /* Check if it is an allowed port */
+    if (conf->allowed_connect_ports->nelts == 0) {
+	/* Default setting if not overridden by AllowCONNECT */
+	switch (uri.port) {
+	    case APR_URI_HTTPS_DEFAULT_PORT:
+	    case APR_URI_SNEWS_DEFAULT_PORT:
+		break;
+	    default:
+                /* XXX can we call ap_proxyerror() here to get a nice log message? */
+		return HTTP_FORBIDDEN;
+	}
+    } else if(!allowed_port(conf, uri.port)) {
+        /* XXX can we call ap_proxyerror() here to get a nice log message? */
+	return HTTP_FORBIDDEN;
+    }
+
+    /*
+     * Step Two: Make the Connection
+     *
+     * We have determined who to connect to. Now make the connection.
+     */
+
+    /* get all the possible IP addresses for the destname and loop through them
+     * until we get a successful connection
+     */
+    if (APR_SUCCESS != err) {
+	return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p,
+                             "DNS lookup failure for: ",
+                             connectname, NULL));
+    }
+
+    /*
+     * At this point we have a list of one or more IP addresses of
+     * the machine to connect to. If configured, reorder this
+     * list so that the "best candidate" is first try. "best
+     * candidate" could mean the least loaded server, the fastest
+     * responding server, whatever.
+     *
+     * For now we do nothing, ie we get DNS round robin.
+     * XXX FIXME
+     */
+    failed = ap_proxy_connect_to_backend(&sock, "CONNECT", connect_addr,
+                                         connectname, conf, r->server,
+                                         r->pool);
+
+    /* handle a permanent error from the above loop */
+    if (failed) {
+        if (proxyname) {
+            return DECLINED;
+        }
+        else {
+            return HTTP_BAD_GATEWAY;
+        }
+    }
+
+    /*
+     * Step Three: Send the Request
+     *
+     * Send the HTTP/1.1 CONNECT request to the remote server
+     */
+
+    /* we are acting as a tunnel - the output filter stack should
+     * be completely empty, because when we are done here we are done completely.
+     * We add the NULL filter to the stack to do this...
+     */
+    r->output_filters = NULL;
+    r->connection->output_filters = NULL;
+
+
+    /* If we are connecting through a remote proxy, we need to pass
+     * the CONNECT request on to it.
+     */
+    if (proxyport) {
+	/* FIXME: Error checking ignored.
+	 */
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+		     "proxy: CONNECT: sending the CONNECT request to the remote proxy");
+        nbytes = apr_snprintf(buffer, sizeof(buffer),
+			      "CONNECT %s HTTP/1.0" CRLF, r->uri);
+        apr_socket_send(sock, buffer, &nbytes);
+        nbytes = apr_snprintf(buffer, sizeof(buffer),
+			      "Proxy-agent: %s" CRLF CRLF, ap_get_server_version());
+        apr_socket_send(sock, buffer, &nbytes);
+    }
+    else {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+		     "proxy: CONNECT: Returning 200 OK Status");
+        nbytes = apr_snprintf(buffer, sizeof(buffer),
+			      "HTTP/1.0 200 Connection Established" CRLF);
+        ap_xlate_proto_to_ascii(buffer, nbytes);
+        apr_socket_send(client_socket, buffer, &nbytes);
+        nbytes = apr_snprintf(buffer, sizeof(buffer),
+			      "Proxy-agent: %s" CRLF CRLF, ap_get_server_version());
+        ap_xlate_proto_to_ascii(buffer, nbytes);
+        apr_socket_send(client_socket, buffer, &nbytes);
+#if 0
+        /* This is safer code, but it doesn't work yet.  I'm leaving it 
+         * here so that I can fix it later.
+         */
+        r->status = HTTP_OK;
+        r->header_only = 1;
+        apr_table_set(r->headers_out, "Proxy-agent: %s", ap_get_server_version());
+        ap_rflush(r);
+#endif
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+		 "proxy: CONNECT: setting up poll()");
+
+    /*
+     * Step Four: Handle Data Transfer
+     *
+     * Handle two way transfer of data over the socket (this is a tunnel).
+     */
+
+/*    r->sent_bodyct = 1;*/
+
+    if ((rv = apr_pollset_create(&pollset, 2, r->pool, 0)) != APR_SUCCESS)
+    {
+	apr_socket_close(sock);
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+            "proxy: CONNECT: error apr_pollset_create()");
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    /* Add client side to the poll */
+    pollfd.p = r->pool;
+    pollfd.desc_type = APR_POLL_SOCKET;
+    pollfd.reqevents = APR_POLLIN;
+    pollfd.desc.s = client_socket;
+    pollfd.client_data = NULL;
+    apr_pollset_add(pollset, &pollfd);
+
+    /* Add the server side to the poll */
+    pollfd.desc.s = sock;
+    apr_pollset_add(pollset, &pollfd);
+
+    while (1) { /* Infinite loop until error (one side closes the connection) */
+        if ((rv = apr_pollset_poll(pollset, -1, &pollcnt, &signalled)) != APR_SUCCESS) {
+	    apr_socket_close(sock);
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "proxy: CONNECT: error apr_poll()");
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+/*	ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: CONNECT: woke from select(), i=%d", pollcnt);*/
+
+        for (i = 0; i < pollcnt; i++) {
+            const apr_pollfd_t *cur = &signalled[i];
+
+            if (cur->desc.s == sock) {
+                pollevent = cur->rtnevents;
+                if (pollevent & APR_POLLIN) {
+/*		ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                             "proxy: CONNECT: sock was set");*/
+                    nbytes = sizeof(buffer);
+                    if (apr_socket_recv(sock, buffer, &nbytes) == APR_SUCCESS) {
+                        o = 0;
+                        i = nbytes;
+                        while(i > 0)
+                        {
+                            nbytes = i;
+    /* This is just plain wrong.  No module should ever write directly
+     * to the client.  For now, this works, but this is high on my list of
+     * things to fix.  The correct line is:
+     * if ((nbytes = ap_rwrite(buffer + o, nbytes, r)) < 0)
+     * rbb
+     */
+                            if (apr_socket_send(client_socket, buffer + o, &nbytes) != APR_SUCCESS)
+                                break;
+                            o += nbytes;
+                            i -= nbytes;
+                        }
+                    }
+                    else
+                        break;
+                }
+                else if ((pollevent & APR_POLLERR) || (pollevent & APR_POLLHUP))
+                    break;
+            }
+            else if (cur->desc.s == client_socket) {
+                pollevent = cur->rtnevents;
+                if (pollevent & APR_POLLIN) {
+/*		ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                             "proxy: CONNECT: client was set");*/
+                    nbytes = sizeof(buffer);
+                    if (apr_socket_recv(client_socket, buffer, &nbytes) == APR_SUCCESS) {
+                        o = 0;
+                        i = nbytes;
+                        while(i > 0)
+                        {
+                            nbytes = i;
+                            if (apr_socket_send(sock, buffer + o, &nbytes) != APR_SUCCESS)
+                                break;
+                            o += nbytes;
+                            i -= nbytes;
+                        }
+                    }
+                    else
+                        break;
+                }
+                else if ((pollevent & APR_POLLERR) || (pollevent & APR_POLLHUP))
+                    break;
+            }
+            else
+                break;
+        }
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+		 "proxy: CONNECT: finished with poll() - cleaning up");
+
+    /*
+     * Step Five: Clean Up
+     *
+     * Close the socket and clean up
+     */
+
+    apr_socket_close(sock);
+
+    return OK;
+}
+
+static void ap_proxy_connect_register_hook(apr_pool_t *p)
+{
+    proxy_hook_scheme_handler(ap_proxy_connect_handler, NULL, NULL, APR_HOOK_MIDDLE);
+    proxy_hook_canon_handler(ap_proxy_connect_canon, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+module AP_MODULE_DECLARE_DATA proxy_connect_module = {
+    STANDARD20_MODULE_STUFF,
+    NULL,		/* create per-directory config structure */
+    NULL,		/* merge per-directory config structures */
+    NULL,		/* create per-server config structure */
+    NULL,		/* merge per-server config structures */
+    NULL,		/* command apr_table_t */
+    ap_proxy_connect_register_hook	/* register hooks */
+};
diff --git a/connectors/ajp/proxy/proxy_ftp.c b/connectors/ajp/proxy/proxy_ftp.c
new file mode 100644
index 0000000..a61c609
--- /dev/null
+++ b/connectors/ajp/proxy/proxy_ftp.c
@@ -0,0 +1,1869 @@
+/* Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* FTP routines for Apache proxy */
+
+#include "mod_proxy.h"
+#if APR_HAVE_TIME_H
+#include <time.h>
+#endif
+#include "apr_version.h"
+
+#if (APR_MAJOR_VERSION < 1)
+#undef apr_socket_create
+#define apr_socket_create apr_socket_create_ex
+#endif
+
+#define AUTODETECT_PWD
+/* Automatic timestamping (Last-Modified header) based on MDTM is used if:
+ * 1) the FTP server supports the MDTM command and
+ * 2) HAVE_TIMEGM (preferred) or HAVE_GMTOFF is available at compile time
+ */
+#define USE_MDTM
+
+
+module AP_MODULE_DECLARE_DATA proxy_ftp_module;
+
+int ap_proxy_ftp_canon(request_rec *r, char *url);
+int ap_proxy_ftp_handler(request_rec *r, proxy_worker *worker, proxy_server_conf *conf,
+                             char *url, const char *proxyhost,
+                             apr_port_t proxyport);
+apr_status_t ap_proxy_send_dir_filter(ap_filter_t * f,
+                                                   apr_bucket_brigade *bb);
+
+
+/*
+ * Decodes a '%' escaped string, and returns the number of characters
+ */
+static int decodeenc(char *x)
+{
+    int i, j, ch;
+
+    if (x[0] == '\0')
+        return 0;               /* special case for no characters */
+    for (i = 0, j = 0; x[i] != '\0'; i++, j++) {
+        /* decode it if not already done */
+        ch = x[i];
+        if (ch == '%' && apr_isxdigit(x[i + 1]) && apr_isxdigit(x[i + 2])) {
+            ch = ap_proxy_hex2c(&x[i + 1]);
+            i += 2;
+        }
+        x[j] = ch;
+    }
+    x[j] = '\0';
+    return j;
+}
+
+/*
+ * Escape the globbing characters in a path used as argument to
+ * the FTP commands (SIZE, CWD, RETR, MDTM, ...).
+ * ftpd assumes '\\' as a quoting character to escape special characters.
+ * Returns: escaped string
+ */
+#define FTP_GLOBBING_CHARS "*?[{~"
+static char *ftp_escape_globbingchars(apr_pool_t *p, const char *path)
+{
+    char *ret = apr_palloc(p, 2*strlen(path)+sizeof(""));
+    char *d;
+    for (d = ret; *path; ++path) {
+        if (strchr(FTP_GLOBBING_CHARS, *path) != NULL)
+            *d++ = '\\';
+        *d++ = *path;
+    }
+    *d = '\0';
+    return ret;
+}
+
+/*
+ * Check for globbing characters in a path used as argument to
+ * the FTP commands (SIZE, CWD, RETR, MDTM, ...).
+ * ftpd assumes '\\' as a quoting character to escape special characters.
+ * Returns: 0 (no globbing chars, or all globbing chars escaped), 1 (globbing chars)
+ */
+static int ftp_check_globbingchars(const char *path)
+{
+    for ( ; *path; ++path) {
+        if (*path == '\\')
+	    ++path;
+        if (path != '\0' && strchr(FTP_GLOBBING_CHARS, *path) != NULL)
+            return TRUE;
+    }
+    return FALSE;
+}
+
+/*
+ * checks an encoded ftp string for bad characters, namely, CR, LF or
+ * non-ascii character
+ */
+static int ftp_check_string(const char *x)
+{
+    int i, ch = 0;
+#if APR_CHARSET_EBCDIC
+    char buf[1];
+#endif
+
+    for (i = 0; x[i] != '\0'; i++) {
+        ch = x[i];
+        if (ch == '%' && apr_isxdigit(x[i + 1]) && apr_isxdigit(x[i + 2])) {
+            ch = ap_proxy_hex2c(&x[i + 1]);
+            i += 2;
+        }
+#if !APR_CHARSET_EBCDIC
+        if (ch == '\015' || ch == '\012' || (ch & 0x80))
+#else                           /* APR_CHARSET_EBCDIC */
+        if (ch == '\r' || ch == '\n')
+            return 0;
+        buf[0] = ch;
+        ap_xlate_proto_to_ascii(buf, 1);
+        if (buf[0] & 0x80)
+#endif                          /* APR_CHARSET_EBCDIC */
+            return 0;
+    }
+    return 1;
+}
+
+/*
+ * Canonicalise ftp URLs.
+ */
+int ap_proxy_ftp_canon(request_rec *r, char *url)
+{
+    char *user, *password, *host, *path, *parms, *strp, sport[7];
+    apr_pool_t *p = r->pool;
+    const char *err;
+    apr_port_t port, def_port;
+
+    /* */
+    if (strncasecmp(url, "ftp:", 4) == 0) {
+        url += 4;
+    }
+    else {
+        return DECLINED;
+    }
+    def_port = apr_uri_port_of_scheme("ftp");
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "proxy: FTP: canonicalising URL %s", url);
+
+    port = def_port;
+    err = ap_proxy_canon_netloc(p, &url, &user, &password, &host, &port);
+    if (err)
+        return HTTP_BAD_REQUEST;
+    if (user != NULL && !ftp_check_string(user))
+        return HTTP_BAD_REQUEST;
+    if (password != NULL && !ftp_check_string(password))
+        return HTTP_BAD_REQUEST;
+
+    /* now parse path/parameters args, according to rfc1738 */
+    /*
+     * N.B. if this isn't a true proxy request, then the URL path (but not
+     * query args) has already been decoded. This gives rise to the problem
+     * of a ; being decoded into the path.
+     */
+    strp = strchr(url, ';');
+    if (strp != NULL) {
+        *(strp++) = '\0';
+        parms = ap_proxy_canonenc(p, strp, strlen(strp), enc_parm,
+                                  r->proxyreq);
+        if (parms == NULL)
+            return HTTP_BAD_REQUEST;
+    }
+    else
+        parms = "";
+
+    path = ap_proxy_canonenc(p, url, strlen(url), enc_path, r->proxyreq);
+    if (path == NULL)
+        return HTTP_BAD_REQUEST;
+    if (!ftp_check_string(path))
+        return HTTP_BAD_REQUEST;
+
+    if (r->proxyreq && r->args != NULL) {
+        if (strp != NULL) {
+            strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_parm, 1);
+            if (strp == NULL)
+                return HTTP_BAD_REQUEST;
+            parms = apr_pstrcat(p, parms, "?", strp, NULL);
+        }
+        else {
+            strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_fpath, 1);
+            if (strp == NULL)
+                return HTTP_BAD_REQUEST;
+            path = apr_pstrcat(p, path, "?", strp, NULL);
+        }
+        r->args = NULL;
+    }
+
+/* now, rebuild URL */
+
+    if (port != def_port)
+        apr_snprintf(sport, sizeof(sport), ":%d", port);
+    else
+        sport[0] = '\0';
+
+    if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
+        host = apr_pstrcat(p, "[", host, "]", NULL);
+    }
+    r->filename = apr_pstrcat(p, "proxy:ftp://", (user != NULL) ? user : "",
+                              (password != NULL) ? ":" : "",
+                              (password != NULL) ? password : "",
+                          (user != NULL) ? "@" : "", host, sport, "/", path,
+                              (parms[0] != '\0') ? ";" : "", parms, NULL);
+
+    return OK;
+}
+
+/* we chop lines longer than 80 characters */
+#define MAX_LINE_LEN 80
+
+/*
+ * Reads response lines, returns both the ftp status code and
+ * remembers the response message in the supplied buffer
+ */
+static int ftp_getrc_msg(conn_rec *ftp_ctrl, apr_bucket_brigade *bb, char *msgbuf, int msglen)
+{
+    int status;
+    char response[MAX_LINE_LEN];
+    char buff[5];
+    char *mb = msgbuf, *me = &msgbuf[msglen];
+    apr_status_t rv;
+    int eos;
+
+    if (APR_SUCCESS != (rv = ap_proxy_string_read(ftp_ctrl, bb, response, sizeof(response), &eos))) {
+        return -1;
+    }
+/*
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+                 "proxy: <FTP: %s", response);
+*/
+    if (!apr_isdigit(response[0]) || !apr_isdigit(response[1]) ||
+    !apr_isdigit(response[2]) || (response[3] != ' ' && response[3] != '-'))
+        status = 0;
+    else
+        status = 100 * response[0] + 10 * response[1] + response[2] - 111 * '0';
+
+    mb = apr_cpystrn(mb, response + 4, me - mb);
+
+    if (response[3] == '-') {
+        memcpy(buff, response, 3);
+        buff[3] = ' ';
+        do {
+            if (APR_SUCCESS != (rv = ap_proxy_string_read(ftp_ctrl, bb, response, sizeof(response), &eos))) {
+                return -1;
+            }
+            mb = apr_cpystrn(mb, response + (' ' == response[0] ? 1 : 4), me - mb);
+        } while (memcmp(response, buff, 4) != 0);
+    }
+
+    return status;
+}
+
+/* this is a filter that turns a raw ASCII directory listing into pretty HTML */
+
+/* ideally, mod_proxy should simply send the raw directory list up the filter
+ * stack to mod_autoindex, which in theory should turn the raw ascii into
+ * pretty html along with all the bells and whistles it provides...
+ *
+ * all in good time...! :)
+ */
+
+typedef struct {
+    apr_bucket_brigade *in;
+    char buffer[MAX_STRING_LEN];
+    enum {
+        HEADER, BODY, FOOTER
+    }    state;
+}      proxy_dir_ctx_t;
+
+/* fallback regex for ls -s1;  ($0..$2) == 3 */
+#define LS_REG_PATTERN "^ *([0-9]+) +([^ ]+)$"
+#define LS_REG_MATCH   3
+
+apr_status_t ap_proxy_send_dir_filter(ap_filter_t *f, apr_bucket_brigade *in)
+{
+    request_rec *r = f->r;
+    conn_rec *c = r->connection;
+    apr_pool_t *p = r->pool;
+    apr_bucket_brigade *out = apr_brigade_create(p, c->bucket_alloc);
+    apr_status_t rv;
+
+    register int n;
+    char *dir, *path, *reldir, *site, *str, *type;
+
+    const char *pwd = apr_table_get(r->notes, "Directory-PWD");
+    const char *readme = apr_table_get(r->notes, "Directory-README");
+
+    proxy_dir_ctx_t *ctx = f->ctx;
+
+    if (!ctx) {
+        f->ctx = ctx = apr_pcalloc(p, sizeof(*ctx));
+        ctx->in = apr_brigade_create(p, c->bucket_alloc);
+        ctx->buffer[0] = 0;
+        ctx->state = HEADER;
+    }
+
+    /* combine the stored and the new */
+    APR_BRIGADE_CONCAT(ctx->in, in);
+
+    if (HEADER == ctx->state) {
+
+        /* basedir is either "", or "/%2f" for the "squid %2f hack" */
+        const char *basedir = "";  /* By default, path is relative to the $HOME dir */
+        char *wildcard = NULL;
+
+        /* Save "scheme://site" prefix without password */
+        site = apr_uri_unparse(p, &f->r->parsed_uri, APR_URI_UNP_OMITPASSWORD | APR_URI_UNP_OMITPATHINFO);
+        /* ... and path without query args */
+        path = apr_uri_unparse(p, &f->r->parsed_uri, APR_URI_UNP_OMITSITEPART | APR_URI_UNP_OMITQUERY);
+
+        /* If path began with /%2f, change the basedir */
+        if (strncasecmp(path, "/%2f", 4) == 0) {
+            basedir = "/%2f";
+        }
+
+        /* Strip off a type qualifier. It is ignored for dir listings */
+        if ((type = strstr(path, ";type=")) != NULL)
+            *type++ = '\0';
+
+        (void)decodeenc(path);
+
+        while (path[1] == '/') /* collapse multiple leading slashes to one */
+            ++path;
+
+        reldir = strrchr(path, '/');
+        if (reldir != NULL && ftp_check_globbingchars(reldir)) {
+            wildcard = &reldir[1];
+            reldir[0] = '\0'; /* strip off the wildcard suffix */
+        }
+
+        /* Copy path, strip (all except the last) trailing slashes */
+        /* (the trailing slash is needed for the dir component loop below) */
+        path = dir = apr_pstrcat(p, path, "/", NULL);
+        for (n = strlen(path); n > 1 && path[n - 1] == '/' && path[n - 2] == '/'; --n)
+            path[n - 1] = '\0';
+
+        /* Add a link to the root directory (if %2f hack was used) */
+        str = (basedir[0] != '\0') ? "<a href=\"/%2f/\">%2f</a>/" : "";
+
+        /* print "ftp://host/" */
+        str = apr_psprintf(p, DOCTYPE_HTML_3_2
+                "<html>\n <head>\n  <title>%s%s%s</title>\n"
+                "  <base href=\"%s%s%s\">\n </head>\n"
+                " <body>\n  <h2>Directory of "
+                "<a href=\"/\">%s</a>/%s",
+                site, basedir, ap_escape_html(p, path),
+                site, basedir, ap_escape_uri(p, path),
+                site, str);
+
+        APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str),
+                                                          p, c->bucket_alloc));
+
+        for (dir = path+1; (dir = strchr(dir, '/')) != NULL; )
+        {
+            *dir = '\0';
+            if ((reldir = strrchr(path+1, '/'))==NULL) {
+                reldir = path+1;
+            }
+            else
+                ++reldir;
+            /* print "path/" component */
+            str = apr_psprintf(p, "<a href=\"%s%s/\">%s</a>/", basedir,
+                        ap_escape_uri(p, path),
+                        ap_escape_html(p, reldir));
+            *dir = '/';
+            while (*dir == '/')
+              ++dir;
+            APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str,
+                                                           strlen(str), p,
+                                                           c->bucket_alloc));
+        }
+        if (wildcard != NULL) {
+            APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(wildcard,
+                                                           strlen(wildcard), p,
+                                                           c->bucket_alloc));
+        }
+
+        /* If the caller has determined the current directory, and it differs */
+        /* from what the client requested, then show the real name */
+        if (pwd == NULL || strncmp(pwd, path, strlen(pwd)) == 0) {
+            str = apr_psprintf(p, "</h2>\n\n  <hr />\n\n<pre>");
+        }
+        else {
+            str = apr_psprintf(p, "</h2>\n\n(%s)\n\n  <hr />\n\n<pre>",
+                               ap_escape_html(p, pwd));
+        }
+        APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str),
+                                                           p, c->bucket_alloc));
+
+        /* print README */
+        if (readme) {
+            str = apr_psprintf(p, "%s\n</pre>\n\n<hr />\n\n<pre>\n",
+                               ap_escape_html(p, readme));
+
+            APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str,
+                                                           strlen(str), p,
+                                                           c->bucket_alloc));
+        }
+
+        /* make sure page intro gets sent out */
+        APR_BRIGADE_INSERT_TAIL(out, apr_bucket_flush_create(c->bucket_alloc));
+        if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) {
+            return rv;
+        }
+        apr_brigade_cleanup(out);
+
+        ctx->state = BODY;
+    }
+
+    /* loop through each line of directory */
+    while (BODY == ctx->state) {
+        char *filename;
+        int found = 0;
+        int eos = 0;
+
+        regex_t *re = NULL;
+        regmatch_t re_result[LS_REG_MATCH];
+
+        /* Compile the output format of "ls -s1" as a fallback for non-unix ftp listings */
+        re = ap_pregcomp(p, LS_REG_PATTERN, REG_EXTENDED);
+        ap_assert(re != NULL);
+
+        /* get a complete line */
+        /* if the buffer overruns - throw data away */
+        while (!found && !APR_BRIGADE_EMPTY(ctx->in)) {
+            char *pos, *response;
+            apr_size_t len, max;
+            apr_bucket *e;
+
+            e = APR_BRIGADE_FIRST(ctx->in);
+            if (APR_BUCKET_IS_EOS(e)) {
+                eos = 1;
+                break;
+            }
+            if (APR_SUCCESS != (rv = apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ))) {
+                return rv;
+            }
+            pos = memchr(response, APR_ASCII_LF, len);
+            if (pos != NULL) {
+                if ((response + len) != (pos + 1)) {
+                    len = pos - response + 1;
+                    apr_bucket_split(e, pos - response + 1);
+                }
+                found = 1;
+            }
+            max = sizeof(ctx->buffer) - strlen(ctx->buffer) - 1;
+            if (len > max) {
+                len = max;
+            }
+
+            /* len+1 to leave space for the trailing nil char */
+            apr_cpystrn(ctx->buffer+strlen(ctx->buffer), response, len+1);
+
+            APR_BUCKET_REMOVE(e);
+            apr_bucket_destroy(e);
+        }
+
+        /* EOS? jump to footer */
+        if (eos) {
+            ctx->state = FOOTER;
+            break;
+        }
+
+        /* not complete? leave and try get some more */
+        if (!found) {
+            return APR_SUCCESS;
+        }
+
+        {
+            apr_size_t n = strlen(ctx->buffer);
+            if (ctx->buffer[n-1] == CRLF[1])  /* strip trailing '\n' */
+                ctx->buffer[--n] = '\0';
+            if (ctx->buffer[n-1] == CRLF[0])  /* strip trailing '\r' if present */
+                ctx->buffer[--n] = '\0';
+        }
+
+        /* a symlink? */
+        if (ctx->buffer[0] == 'l' && (filename = strstr(ctx->buffer, " -> ")) != NULL) {
+            char *link_ptr = filename;
+
+            do {
+                filename--;
+            } while (filename[0] != ' ' && filename > ctx->buffer);
+            if (filename > ctx->buffer)
+                *(filename++) = '\0';
+            *(link_ptr++) = '\0';
+            str = apr_psprintf(p, "%s <a href=\"%s\">%s %s</a>\n",
+                               ap_escape_html(p, ctx->buffer),
+                               ap_escape_uri(p, filename),
+                               ap_escape_html(p, filename),
+                               ap_escape_html(p, link_ptr));
+        }
+
+        /* a directory/file? */
+        else if (ctx->buffer[0] == 'd' || ctx->buffer[0] == '-' || ctx->buffer[0] == 'l' || apr_isdigit(ctx->buffer[0])) {
+            int searchidx = 0;
+            char *searchptr = NULL;
+            int firstfile = 1;
+            if (apr_isdigit(ctx->buffer[0])) {  /* handle DOS dir */
+                searchptr = strchr(ctx->buffer, '<');
+                if (searchptr != NULL)
+                    *searchptr = '[';
+                searchptr = strchr(ctx->buffer, '>');
+                if (searchptr != NULL)
+                    *searchptr = ']';
+            }
+
+            filename = strrchr(ctx->buffer, ' ');
+            *(filename++) = '\0';
+
+            /* handle filenames with spaces in 'em */
+            if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) {
+                firstfile = 0;
+                searchidx = filename - ctx->buffer;
+            }
+            else if (searchidx != 0 && ctx->buffer[searchidx] != 0) {
+                *(--filename) = ' ';
+                ctx->buffer[searchidx - 1] = '\0';
+                filename = &ctx->buffer[searchidx];
+            }
+
+            /* Append a slash to the HREF link for directories */
+            if (!strcmp(filename, ".") || !strcmp(filename, "..") || ctx->buffer[0] == 'd') {
+                str = apr_psprintf(p, "%s <a href=\"%s/\">%s</a>\n",
+                                   ap_escape_html(p, ctx->buffer),
+                                   ap_escape_uri(p, filename),
+                                   ap_escape_html(p, filename));
+            }
+            else {
+                str = apr_psprintf(p, "%s <a href=\"%s\">%s</a>\n",
+                                   ap_escape_html(p, ctx->buffer),
+                                   ap_escape_uri(p, filename),
+                                   ap_escape_html(p, filename));
+            }
+        }
+        /* Try a fallback for listings in the format of "ls -s1" */
+        else if (0 == ap_regexec(re, ctx->buffer, LS_REG_MATCH, re_result, 0)) {
+
+            filename = apr_pstrndup(p, &ctx->buffer[re_result[2].rm_so], re_result[2].rm_eo - re_result[2].rm_so);
+
+            str = apr_pstrcat(p, ap_escape_html(p, apr_pstrndup(p, ctx->buffer, re_result[2].rm_so)),
+                              "<a href=\"", ap_escape_uri(p, filename), "\">",
+                              ap_escape_html(p, filename), "</a>\n", NULL);
+        }
+        else {
+            strcat(ctx->buffer, "\n"); /* re-append the newline */
+            str = ap_escape_html(p, ctx->buffer);
+        }
+
+        /* erase buffer for next time around */
+        ctx->buffer[0] = 0;
+
+        APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str), p,
+                                                            c->bucket_alloc));
+        APR_BRIGADE_INSERT_TAIL(out, apr_bucket_flush_create(c->bucket_alloc));
+        if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) {
+            return rv;
+        }
+        apr_brigade_cleanup(out);
+
+    }
+
+    if (FOOTER == ctx->state) {
+        str = apr_psprintf(p, "</pre>\n\n  <hr />\n\n  %s\n\n </body>\n</html>\n", ap_psignature("", r));
+        APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str), p,
+                                                            c->bucket_alloc));
+        APR_BRIGADE_INSERT_TAIL(out, apr_bucket_flush_create(c->bucket_alloc));
+        APR_BRIGADE_INSERT_TAIL(out, apr_bucket_eos_create(c->bucket_alloc));
+        if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) {
+            return rv;
+        }
+        apr_brigade_destroy(out);
+    }
+
+    return APR_SUCCESS;
+}
+
+/*
+ * Generic "send FTP command to server" routine, using the control socket.
+ * Returns the FTP returncode (3 digit code)
+ * Allows for tracing the FTP protocol (in LogLevel debug)
+ */
+static int
+proxy_ftp_command(const char *cmd, request_rec *r, conn_rec *ftp_ctrl,
+                  apr_bucket_brigade *bb, char **pmessage)
+{
+    char *crlf;
+    int rc;
+    char message[HUGE_STRING_LEN];
+
+    /* If cmd == NULL, we retrieve the next ftp response line */
+    if (cmd != NULL) {
+        conn_rec *c = r->connection;
+        APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(cmd, strlen(cmd), r->pool, c->bucket_alloc));
+        APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_flush_create(c->bucket_alloc));
+        ap_pass_brigade(ftp_ctrl->output_filters, bb);
+
+        /* strip off the CRLF for logging */
+        apr_cpystrn(message, cmd, sizeof(message));
+        if ((crlf = strchr(message, '\r')) != NULL ||
+            (crlf = strchr(message, '\n')) != NULL)
+            *crlf = '\0';
+        if (strncmp(message,"PASS ", 5) == 0)
+            strcpy(&message[5], "****");
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy:>FTP: %s", message);
+    }
+
+    rc = ftp_getrc_msg(ftp_ctrl, bb, message, sizeof message);
+    if (rc == -1 || rc == 421)
+        strcpy(message,"<unable to read result>");
+    if ((crlf = strchr(message, '\r')) != NULL ||
+        (crlf = strchr(message, '\n')) != NULL)
+        *crlf = '\0';
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "proxy:<FTP: %3.3u %s", rc, message);
+
+    if (pmessage != NULL)
+        *pmessage = apr_pstrdup(r->pool, message);
+
+    return rc;
+}
+
+/* Set ftp server to TYPE {A,I,E} before transfer of a directory or file */
+static int ftp_set_TYPE(char xfer_type, request_rec *r, conn_rec *ftp_ctrl,
+                  apr_bucket_brigade *bb, char **pmessage)
+{
+    char old_type[2] = { 'A', '\0' }; /* After logon, mode is ASCII */
+    int ret = HTTP_OK;
+    int rc;
+
+    /* set desired type */
+    old_type[0] = xfer_type;
+
+    rc = proxy_ftp_command(apr_pstrcat(r->pool, "TYPE ", old_type, CRLF, NULL),
+                           r, ftp_ctrl, bb, pmessage);
+/* responses: 200, 421, 500, 501, 504, 530 */
+    /* 200 Command okay. */
+    /* 421 Service not available, closing control connection. */
+    /* 500 Syntax error, command unrecognized. */
+    /* 501 Syntax error in parameters or arguments. */
+    /* 504 Command not implemented for that parameter. */
+    /* 530 Not logged in. */
+    if (rc == -1 || rc == 421) {
+        ret = ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             "Error reading from remote server");
+    }
+    else if (rc != 200 && rc != 504) {
+        ret = ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             "Unable to set transfer type");
+    }
+/* Allow not implemented */
+    else if (rc == 504)
+        /* ignore it silently */;
+
+    return ret;
+}
+
+
+/* Return the current directory which we have selected on the FTP server, or NULL */
+static char *ftp_get_PWD(request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb)
+{
+    char *cwd = NULL;
+    char *ftpmessage = NULL;
+
+    /* responses: 257, 500, 501, 502, 421, 550 */
+    /* 257 "<directory-name>" <commentary> */
+    /* 421 Service not available, closing control connection. */
+    /* 500 Syntax error, command unrecognized. */
+    /* 501 Syntax error in parameters or arguments. */
+    /* 502 Command not implemented. */
+    /* 550 Requested action not taken. */
+    switch (proxy_ftp_command("PWD" CRLF, r, ftp_ctrl, bb, &ftpmessage)) {
+        case -1:
+        case 421:
+        case 550:
+            ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             "Failed to read PWD on ftp server");
+            break;
+
+        case 257: {
+            const char *dirp = ftpmessage;
+            cwd = ap_getword_conf(r->pool, &dirp);
+        }
+    }
+    return cwd;
+}
+
+
+/* Common routine for failed authorization (i.e., missing or wrong password)
+ * to an ftp service. This causes most browsers to retry the request
+ * with username and password (which was presumably queried from the user)
+ * supplied in the Authorization: header.
+ * Note that we "invent" a realm name which consists of the
+ * ftp://user@host part of the reqest (sans password -if supplied but invalid-)
+ */
+static int ftp_unauthorized(request_rec *r, int log_it)
+{
+    r->proxyreq = PROXYREQ_NONE;
+    /*
+     * Log failed requests if they supplied a password (log username/password
+     * guessing attempts)
+     */
+    if (log_it)
+        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
+                      "proxy: missing or failed auth to %s",
+                      apr_uri_unparse(r->pool,
+                                 &r->parsed_uri, APR_URI_UNP_OMITPATHINFO));
+
+    apr_table_setn(r->err_headers_out, "WWW-Authenticate",
+                   apr_pstrcat(r->pool, "Basic realm=\"",
+                               apr_uri_unparse(r->pool, &r->parsed_uri,
+                       APR_URI_UNP_OMITPASSWORD | APR_URI_UNP_OMITPATHINFO),
+                               "\"", NULL));
+
+    return HTTP_UNAUTHORIZED;
+}
+
+static
+apr_status_t proxy_ftp_cleanup(request_rec *r, proxy_conn_rec *backend)
+{
+
+    backend->close_on_recycle = 1;
+    ap_set_module_config(r->connection->conn_config, &proxy_ftp_module, NULL);
+    ap_proxy_release_connection("FTP", backend, r->server);    
+
+    return OK;
+}
+
+static ftp_proxyerror(request_rec *r, proxy_conn_rec *conn, int statuscode, const char *message)
+{
+    proxy_ftp_cleanup(r, conn);
+    return ap_proxyerror(r, statuscode, message);
+}
+/*
+ * Handles direct access of ftp:// URLs
+ * Original (Non-PASV) version from
+ * Troy Morrison <spiffnet@zoom.com>
+ * PASV added by Chuck
+ * Filters by [Graham Leggett <minfrin@sharp.fm>]
+ */
+int ap_proxy_ftp_handler(request_rec *r, proxy_worker *worker, proxy_server_conf *conf,
+                             char *url, const char *proxyhost,
+                             apr_port_t proxyport)
+{
+    apr_pool_t *p = r->pool;
+    conn_rec *c = r->connection;
+    proxy_conn_rec *backend;
+    apr_socket_t *sock, *local_sock, *data_sock = NULL;
+    apr_sockaddr_t *connect_addr;
+    apr_status_t rv;
+    conn_rec *origin, *data = NULL;
+    apr_status_t err = APR_SUCCESS;
+    apr_bucket_brigade *bb = apr_brigade_create(p, c->bucket_alloc);
+    char *buf, *connectname;
+    apr_port_t connectport;
+    char buffer[MAX_STRING_LEN];
+    char *ftpmessage = NULL;
+    char *path, *strp, *type_suffix, *cwd = NULL;
+    apr_uri_t uri; 
+    char *user = NULL;
+/*    char *account = NULL; how to supply an account in a URL? */
+    const char *password = NULL;
+    int len, rc;
+    int one = 1;
+    char *size = NULL;
+    apr_socket_t *origin_sock = NULL;
+    char xfer_type = 'A'; /* after ftp login, the default is ASCII */
+    int  dirlisting = 0;
+#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF))
+    apr_time_t mtime = 0L;
+#endif
+
+    /* stuff for PASV mode */
+    int connect = 0, use_port = 0;
+    char dates[APR_RFC822_DATE_LEN];
+    int status;
+
+    /* is this for us? */
+    if (proxyhost) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: FTP: declining URL %s - proxyhost %s specified:", url, proxyhost);
+        return DECLINED;        /* proxy connections are via HTTP */
+    }
+    if (strncasecmp(url, "ftp:", 4)) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: FTP: declining URL %s - not ftp:", url);
+        return DECLINED;        /* only interested in FTP */
+    }
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "proxy: FTP: serving URL %s", url);
+
+
+    /*
+     * I: Who Do I Connect To? -----------------------
+     *
+     * Break up the URL to determine the host to connect to
+     */
+
+    /* we only support GET and HEAD */
+    if (r->method_number != M_GET)
+        return HTTP_NOT_IMPLEMENTED;
+
+    /* We break the URL into host, port, path-search */
+    if (r->parsed_uri.hostname == NULL) {
+        if (APR_SUCCESS != apr_uri_parse(p, url, &uri)) {
+            return ap_proxyerror(r, HTTP_BAD_REQUEST,
+                apr_psprintf(p, "URI cannot be parsed: %s", url));
+        }
+        connectname = uri.hostname;
+        connectport = uri.port;
+        path = apr_pstrdup(p, uri.path);
+    }
+    else {
+        connectname = r->parsed_uri.hostname;
+        connectport = r->parsed_uri.port;
+        path = apr_pstrdup(p, r->parsed_uri.path);
+    }
+    if (connectport == 0) {
+        connectport = apr_uri_port_of_scheme("ftp");
+    }
+    path = (path != NULL && path[0] != '\0') ? &path[1] : "";
+
+    type_suffix = strchr(path, ';');
+    if (type_suffix != NULL)
+        *(type_suffix++) = '\0';
+
+    if (type_suffix != NULL && strncmp(type_suffix, "type=", 5) == 0
+        && apr_isalpha(type_suffix[5])) {
+        /* "type=d" forces a dir listing.
+         * The other types (i|a|e) are directly used for the ftp TYPE command
+         */
+        if ( ! (dirlisting = (apr_tolower(type_suffix[5]) == 'd')))
+            xfer_type = apr_toupper(type_suffix[5]);
+
+        /* Check valid types, rather than ignoring invalid types silently: */
+        if (strchr("AEI", xfer_type) == NULL)
+            return ap_proxyerror(r, HTTP_BAD_REQUEST, apr_pstrcat(r->pool,
+                                    "ftp proxy supports only types 'a', 'i', or 'e': \"",
+                                    type_suffix, "\" is invalid.", NULL));
+    }
+    else {
+        /* make binary transfers the default */
+        xfer_type = 'I';
+    }
+
+
+    /*
+     * The "Authorization:" header must be checked first. We allow the user
+     * to "override" the URL-coded user [ & password ] in the Browsers'
+     * User&Password Dialog. NOTE that this is only marginally more secure
+     * than having the password travel in plain as part of the URL, because
+     * Basic Auth simply uuencodes the plain text password. But chances are
+     * still smaller that the URL is logged regularly.
+     */
+    if ((password = apr_table_get(r->headers_in, "Authorization")) != NULL
+        && strcasecmp(ap_getword(r->pool, &password, ' '), "Basic") == 0
+        && (password = ap_pbase64decode(r->pool, password))[0] != ':') {
+        /*
+         * Note that this allocation has to be made from r->connection->pool
+         * because it has the lifetime of the connection.  The other
+         * allocations are temporary and can be tossed away any time.
+         */
+        user = ap_getword_nulls(r->connection->pool, &password, ':');
+        r->ap_auth_type = "Basic";
+        r->user = r->parsed_uri.user = user;
+    }
+    else if ((user = r->parsed_uri.user) != NULL) {
+        user = apr_pstrdup(p, user);
+        decodeenc(user);
+        if ((password = r->parsed_uri.password) != NULL) {
+            char *tmp = apr_pstrdup(p, password);
+            decodeenc(tmp);
+            password = tmp;
+        }
+    }
+    else {
+        user = "anonymous";
+        password = "apache-proxy@";
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+       "proxy: FTP: connecting %s to %s:%d", url, connectname, connectport);
+
+    /* do a DNS lookup for the destination host */
+    if (!worker->cp->addr)
+        err = apr_sockaddr_info_get(&(worker->cp->addr),
+                                    connectname, APR_UNSPEC,
+                                    connectport, 0,
+                                    worker->cp->pool);
+    /*
+     * get all the possible IP addresses for the destname and loop through
+     * them until we get a successful connection
+     */
+    if (APR_SUCCESS != err) {
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p,
+                                                 "DNS lookup failure for: ",
+                                                        connectname, NULL));
+    }
+
+    /* check if ProxyBlock directive on this host */
+    if (OK != ap_proxy_checkproxyblock(r, conf, worker->cp->addr)) {
+        return ap_proxyerror(r, HTTP_FORBIDDEN,
+                             "Connect to remote machine blocked");
+    }
+
+    /* create space for state information */
+    backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config, &proxy_ftp_module);
+    if (!backend) {
+        status = ap_proxy_acquire_connection("FTP", &backend, worker, r->server);
+        if (status != OK) {
+            if (backend) {
+                backend->close_on_recycle = 1;
+                ap_proxy_release_connection("FTP", backend, r->server);
+            }
+            return status;
+        }
+        ap_set_module_config(c->conn_config, &proxy_ftp_module, backend);
+    }
+
+
+    /*
+     * II: Make the Connection -----------------------
+     *
+     * We have determined who to connect to. Now make the connection.
+     */
+
+
+    if (ap_proxy_connect_backend("FTP", backend, worker, r->server)) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: FTP: an error occurred creating a new connection to %pI (%s)",
+                     worker->cp->addr, connectname);
+        proxy_ftp_cleanup(r, backend);
+        return HTTP_SERVICE_UNAVAILABLE;
+    }
+
+    if (!backend->connection) {
+        status = ap_proxy_connection_create("FTP", backend, c, r->server);
+        if (status != OK) {
+            proxy_ftp_cleanup(r, backend);
+            return status;
+        }
+    }
+
+    /* Use old naming */
+    origin = backend->connection;
+    connect_addr = worker->cp->addr;
+    sock = backend->sock;
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "proxy: FTP: control connection complete");
+
+
+    /*
+     * III: Send Control Request -------------------------
+     *
+     * Log into the ftp server, send the username & password, change to the
+     * correct directory...
+     */
+
+
+    /* possible results: */
+    /* 120 Service ready in nnn minutes. */
+    /* 220 Service ready for new user. */
+    /* 421 Service not available, closing control connection. */
+    rc = proxy_ftp_command(NULL, r, origin, bb, &ftpmessage);
+    if (rc == -1 || rc == 421) {
+        return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, "Error reading from remote server");
+    }
+    if (rc == 120) {
+        /*
+         * RFC2616 states: 14.37 Retry-After
+         *
+         * The Retry-After response-header field can be used with a 503 (Service
+         * Unavailable) response to indicate how long the service is expected
+         * to be unavailable to the requesting client. [...] The value of
+         * this field can be either an HTTP-date or an integer number of
+         * seconds (in decimal) after the time of the response. Retry-After
+         * = "Retry-After" ":" ( HTTP-date | delta-seconds )
+         */
+        char *secs_str = ftpmessage;
+        time_t secs;
+
+        /* Look for a number, preceded by whitespace */
+        while (*secs_str)
+            if ((secs_str==ftpmessage || apr_isspace(secs_str[-1])) &&
+                apr_isdigit(secs_str[0]))
+                break;
+        if (*secs_str != '\0') {
+            secs = atol(secs_str);
+            apr_table_add(r->headers_out, "Retry-After",
+                          apr_psprintf(p, "%lu", (unsigned long)(60 * secs)));
+        }
+        return ftp_proxyerror(r, backend, HTTP_SERVICE_UNAVAILABLE, ftpmessage);
+    }
+    if (rc != 220) {
+        return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
+    }
+
+    rc = proxy_ftp_command(apr_pstrcat(p, "USER ", user, CRLF, NULL),
+                           r, origin, bb, &ftpmessage);
+    /* possible results; 230, 331, 332, 421, 500, 501, 530 */
+    /* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */
+    /* 230 User logged in, proceed. */
+    /* 331 User name okay, need password. */
+    /* 332 Need account for login. */
+    /* 421 Service not available, closing control connection. */
+    /* 500 Syntax error, command unrecognized. */
+    /* (This may include errors such as command line too long.) */
+    /* 501 Syntax error in parameters or arguments. */
+    /* 530 Not logged in. */
+    if (rc == -1 || rc == 421) {
+        return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, "Error reading from remote server");
+    }
+    if (rc == 530) {
+        proxy_ftp_cleanup(r, backend);
+        return ftp_unauthorized(r, 1);  /* log it: user name guessing
+                                         * attempt? */
+    }
+    if (rc != 230 && rc != 331) {
+        return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
+    }
+
+    if (rc == 331) {            /* send password */
+        if (password == NULL) {
+            proxy_ftp_cleanup(r, backend);
+            return ftp_unauthorized(r, 0);
+        }
+
+        rc = proxy_ftp_command(apr_pstrcat(p, "PASS ", password, CRLF, NULL),
+                           r, origin, bb, &ftpmessage);
+        /* possible results 202, 230, 332, 421, 500, 501, 503, 530 */
+        /* 230 User logged in, proceed. */
+        /* 332 Need account for login. */
+        /* 421 Service not available, closing control connection. */
+        /* 500 Syntax error, command unrecognized. */
+        /* 501 Syntax error in parameters or arguments. */
+        /* 503 Bad sequence of commands. */
+        /* 530 Not logged in. */
+        if (rc == -1 || rc == 421) {
+            return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
+                                  "Error reading from remote server");
+        }
+        if (rc == 332) {
+            return ftp_proxyerror(r, backend, HTTP_UNAUTHORIZED,
+                  apr_pstrcat(p, "Need account for login: ", ftpmessage, NULL));
+        }
+        /* @@@ questionable -- we might as well return a 403 Forbidden here */
+        if (rc == 530) {
+            proxy_ftp_cleanup(r, backend);
+            return ftp_unauthorized(r, 1);      /* log it: passwd guessing
+                                                 * attempt? */
+        }
+        if (rc != 230 && rc != 202) {
+            return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
+        }
+    }
+    apr_table_set(r->notes, "Directory-README", ftpmessage);
+
+
+    /* Special handling for leading "%2f": this enforces a "cwd /"
+     * out of the $HOME directory which was the starting point after login
+     */
+    if (strncasecmp(path, "%2f", 3) == 0) {
+        path += 3;
+        while (*path == '/') /* skip leading '/' (after root %2f) */
+            ++path;
+
+        rc = proxy_ftp_command("CWD /" CRLF, r, origin, bb, &ftpmessage);
+        if (rc == -1 || rc == 421)
+            return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
+                                  "Error reading from remote server");
+    }
+
+    /*
+     * set the directory (walk directory component by component): this is
+     * what we must do if we don't know the OS type of the remote machine
+     */
+    for (;;) {
+        strp = strchr(path, '/');
+        if (strp == NULL)
+            break;
+        *strp = '\0';
+
+        len = decodeenc(path); /* Note! This decodes a %2f -> "/" */
+
+        if (strchr(path, '/')) { /* are there now any '/' characters? */
+            return ftp_proxyerror(r, backend, HTTP_BAD_REQUEST,
+                                  "Use of /%2f is only allowed at the base directory");
+        }
+
+        /* NOTE: FTP servers do globbing on the path.
+         * So we need to escape the URI metacharacters.
+         * We use a special glob-escaping routine to escape globbing chars.
+         * We could also have extended gen_test_char.c with a special T_ESCAPE_FTP_PATH
+         */
+        rc = proxy_ftp_command(apr_pstrcat(p, "CWD ",
+                           ftp_escape_globbingchars(p, path), CRLF, NULL),
+                           r, origin, bb, &ftpmessage);
+        *strp = '/';
+        /* responses: 250, 421, 500, 501, 502, 530, 550 */
+        /* 250 Requested file action okay, completed. */
+        /* 421 Service not available, closing control connection. */
+        /* 500 Syntax error, command unrecognized. */
+        /* 501 Syntax error in parameters or arguments. */
+        /* 502 Command not implemented. */
+        /* 530 Not logged in. */
+        /* 550 Requested action not taken. */
+        if (rc == -1 || rc == 421) {
+            return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
+                                  "Error reading from remote server");
+        }
+        if (rc == 550) {
+            return ftp_proxyerror(r, backend, HTTP_NOT_FOUND, ftpmessage);
+        }
+        if (rc != 250) {
+            return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
+        }
+
+        path = strp + 1;
+    }
+
+    /*
+     * IV: Make Data Connection? -------------------------
+     *
+     * Try EPSV, if that fails... try PASV, if that fails... try PORT.
+     */
+/* this temporarily switches off EPSV/PASV */
+/*goto bypass;*/
+
+    /* set up data connection - EPSV */
+    {
+        apr_sockaddr_t *data_addr;
+        char *data_ip;
+        apr_port_t data_port;
+
+        /*
+         * The EPSV command replaces PASV where both IPV4 and IPV6 is
+         * supported. Only the port is returned, the IP address is always the
+         * same as that on the control connection. Example: Entering Extended
+         * Passive Mode (|||6446|)
+         */
+        rc = proxy_ftp_command("EPSV" CRLF,
+                           r, origin, bb, &ftpmessage);
+        /* possible results: 227, 421, 500, 501, 502, 530 */
+        /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
+        /* 421 Service not available, closing control connection. */
+        /* 500 Syntax error, command unrecognized. */
+        /* 501 Syntax error in parameters or arguments. */
+        /* 502 Command not implemented. */
+        /* 530 Not logged in. */
+        if (rc == -1 || rc == 421) {
+            return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
+                                  "Error reading from remote server");
+        }
+        if (rc != 229 && rc != 500 && rc != 501 && rc != 502) {
+            return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
+        }
+        else if (rc == 229) {
+            char *pstr;
+            char *tok_cntx;
+
+            pstr = ftpmessage;
+            pstr = apr_strtok(pstr, " ", &tok_cntx);    /* separate result code */
+            if (pstr != NULL) {
+                if (*(pstr + strlen(pstr) + 1) == '=') {
+                    pstr += strlen(pstr) + 2;
+                }
+                else {
+                    pstr = apr_strtok(NULL, "(", &tok_cntx);    /* separate address &
+                                                                 * port params */
+                    if (pstr != NULL)
+                        pstr = apr_strtok(NULL, ")", &tok_cntx);
+                }
+            }
+
+            if (pstr) {
+                apr_sockaddr_t *epsv_addr;
+                data_port = atoi(pstr + 3);
+
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                       "proxy: FTP: EPSV contacting remote host on port %d",
+                             data_port);
+
+                if ((rv = apr_socket_create(&data_sock, connect_addr->family, SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) {
+                    ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                                  "proxy: FTP: error creating EPSV socket");
+                    proxy_ftp_cleanup(r, backend);
+                    return HTTP_INTERNAL_SERVER_ERROR;
+                }
+
+#if !defined (TPF) && !defined(BEOS)
+                if (conf->recv_buffer_size > 0 
+                        && (rv = apr_socket_opt_set(data_sock, APR_SO_RCVBUF,
+                                                    conf->recv_buffer_size))) {
+                    ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                                  "proxy: FTP: apr_socket_opt_set(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
+                }
+#endif
+
+                /* make the connection */
+                apr_socket_addr_get(&data_addr, APR_REMOTE, sock);
+                apr_sockaddr_ip_get(&data_ip, data_addr);
+                apr_sockaddr_info_get(&epsv_addr, data_ip, connect_addr->family, data_port, 0, p);
+                rv = apr_socket_connect(data_sock, epsv_addr);
+                if (rv != APR_SUCCESS) {
+                    ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
+                                 "proxy: FTP: EPSV attempt to connect to %pI failed - Firewall/NAT?", epsv_addr);
+                    return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, apr_psprintf(r->pool,
+                                                                           "EPSV attempt to connect to %pI failed - firewall/NAT?", epsv_addr));
+                }
+                else {
+                    connect = 1;
+                }
+            }
+            else {
+                /* and try the regular way */
+                apr_socket_close(data_sock);
+            }
+        }
+    }
+
+    /* set up data connection - PASV */
+    if (!connect) {
+        rc = proxy_ftp_command("PASV" CRLF,
+                           r, origin, bb, &ftpmessage);
+        /* possible results: 227, 421, 500, 501, 502, 530 */
+        /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
+        /* 421 Service not available, closing control connection. */
+        /* 500 Syntax error, command unrecognized. */
+        /* 501 Syntax error in parameters or arguments. */
+        /* 502 Command not implemented. */
+        /* 530 Not logged in. */
+        if (rc == -1 || rc == 421) {
+            return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
+                                  "Error reading from remote server");
+        }
+        if (rc != 227 && rc != 502) {
+            return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
+        }
+        else if (rc == 227) {
+            unsigned int h0, h1, h2, h3, p0, p1;
+            char *pstr;
+            char *tok_cntx;
+
+/* FIXME: Check PASV against RFC1123 */
+
+            pstr = ftpmessage;
+            pstr = apr_strtok(pstr, " ", &tok_cntx);    /* separate result code */
+            if (pstr != NULL) {
+                if (*(pstr + strlen(pstr) + 1) == '=') {
+                    pstr += strlen(pstr) + 2;
+                }
+                else {
+                    pstr = apr_strtok(NULL, "(", &tok_cntx);    /* separate address &
+                                                                 * port params */
+                    if (pstr != NULL)
+                        pstr = apr_strtok(NULL, ")", &tok_cntx);
+                }
+            }
+
+/* FIXME: Only supports IPV4 - fix in RFC2428 */
+
+            if (pstr != NULL && (sscanf(pstr,
+                 "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) {
+
+                apr_sockaddr_t *pasv_addr;
+                apr_port_t pasvport = (p1 << 8) + p0;
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                          "proxy: FTP: PASV contacting host %d.%d.%d.%d:%d",
+                             h3, h2, h1, h0, pasvport);
+
+                if ((rv = apr_socket_create(&data_sock, connect_addr->family, SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) {
+                    ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                                  "proxy: error creating PASV socket");
+                    proxy_ftp_cleanup(r, backend);
+                    return HTTP_INTERNAL_SERVER_ERROR;
+                }
+
+#if !defined (TPF) && !defined(BEOS)
+                if (conf->recv_buffer_size > 0 
+                        && (rv = apr_socket_opt_set(data_sock, APR_SO_RCVBUF,
+                                                    conf->recv_buffer_size))) {
+                    ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                                  "proxy: FTP: apr_socket_opt_set(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
+                }
+#endif
+
+                /* make the connection */
+                apr_sockaddr_info_get(&pasv_addr, apr_psprintf(p, "%d.%d.%d.%d", h3, h2, h1, h0), connect_addr->family, pasvport, 0, p);
+                rv = apr_socket_connect(data_sock, pasv_addr);
+                if (rv != APR_SUCCESS) {
+                    ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
+                                 "proxy: FTP: PASV attempt to connect to %pI failed - Firewall/NAT?", pasv_addr);
+                    return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, apr_psprintf(r->pool,
+                                                                           "PASV attempt to connect to %pI failed - firewall/NAT?", pasv_addr));
+                }
+                else {
+                    connect = 1;
+                }
+            }
+            else {
+                /* and try the regular way */
+                apr_socket_close(data_sock);
+            }
+        }
+    }
+/*bypass:*/
+
+    /* set up data connection - PORT */
+    if (!connect) {
+        apr_sockaddr_t *local_addr;
+        char *local_ip;
+        apr_port_t local_port;
+        unsigned int h0, h1, h2, h3, p0, p1;
+
+        if ((rv = apr_socket_create(&local_sock, connect_addr->family, SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                          "proxy: FTP: error creating local socket");
+            proxy_ftp_cleanup(r, backend);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+        apr_socket_addr_get(&local_addr, APR_LOCAL, sock);
+        local_port = local_addr->port;
+        apr_sockaddr_ip_get(&local_ip, local_addr);
+
+        if ((rv = apr_socket_opt_set(local_sock, APR_SO_REUSEADDR, one)) 
+                != APR_SUCCESS) {
+#ifndef _OSD_POSIX              /* BS2000 has this option "always on" */
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                          "proxy: FTP: error setting reuseaddr option");
+            proxy_ftp_cleanup(r, backend);
+            return HTTP_INTERNAL_SERVER_ERROR;
+#endif                          /* _OSD_POSIX */
+        }
+
+        apr_sockaddr_info_get(&local_addr, local_ip, APR_UNSPEC, local_port, 0, r->pool);
+
+        if ((rv = apr_socket_bind(local_sock, local_addr)) != APR_SUCCESS) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+            "proxy: FTP: error binding to ftp data socket %pI", local_addr);
+            proxy_ftp_cleanup(r, backend);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        /* only need a short queue */
+        if ((rv = apr_socket_listen(local_sock, 2)) != APR_SUCCESS) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                          "proxy: FTP: error listening to ftp data socket %pI", local_addr);
+            proxy_ftp_cleanup(r, backend);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+/* FIXME: Sent PORT here */
+
+        if (local_ip && (sscanf(local_ip,
+                                "%d.%d.%d.%d", &h3, &h2, &h1, &h0) == 4)) {
+            p1 = (local_port >> 8);
+            p0 = (local_port & 0xFF);
+
+            rc = proxy_ftp_command(apr_psprintf(p, "PORT %d,%d,%d,%d,%d,%d" CRLF, h3, h2, h1, h0, p1, p0),
+                           r, origin, bb, &ftpmessage);
+            /* possible results: 200, 421, 500, 501, 502, 530 */
+            /* 200 Command okay. */
+            /* 421 Service not available, closing control connection. */
+            /* 500 Syntax error, command unrecognized. */
+            /* 501 Syntax error in parameters or arguments. */
+            /* 502 Command not implemented. */
+            /* 530 Not logged in. */
+            if (rc == -1 || rc == 421) {
+                return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
+                                      "Error reading from remote server");
+            }
+            if (rc != 200) {
+                return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, buffer);
+            }
+
+            /* signal that we must use the EPRT/PORT loop */
+            use_port = 1;
+        }
+        else {
+/* IPV6 FIXME:
+ * The EPRT command replaces PORT where both IPV4 and IPV6 is supported. The first
+ * number (1,2) indicates the protocol type. Examples:
+ *   EPRT |1|132.235.1.2|6275|
+ *   EPRT |2|1080::8:800:200C:417A|5282|
+ */
+            return ftp_proxyerror(r, backend, HTTP_NOT_IMPLEMENTED,
+                                  "Connect to IPV6 ftp server using EPRT not supported. Enable EPSV.");
+        }
+    }
+
+
+    /*
+     * V: Set The Headers -------------------
+     *
+     * Get the size of the request, set up the environment for HTTP.
+     */
+
+    /* set request; "path" holds last path component */
+    len = decodeenc(path);
+
+    if (strchr(path, '/')) { /* are there now any '/' characters? */
+       return ftp_proxyerror(r, backend, HTTP_BAD_REQUEST,
+                             "Use of /%2f is only allowed at the base directory");
+    }
+
+    /* If len == 0 then it must be a directory (you can't RETR nothing)
+     * Also, don't allow to RETR by wildcard. Instead, create a dirlisting
+     */
+    if (len == 0 || ftp_check_globbingchars(path)) {
+        dirlisting = 1;
+    }
+    else {
+        /* (from FreeBSD ftpd):
+         * SIZE is not in RFC959, but Postel has blessed it and
+         * it will be in the updated RFC.
+         *
+         * Return size of file in a format suitable for
+         * using with RESTART (we just count bytes).
+         */
+        /* from draft-ietf-ftpext-mlst-14.txt:
+         * This value will
+         * change depending on the current STRUcture, MODE and TYPE of the data
+         * connection, or a data connection which would be created were one
+         * created now.  Thus, the result of the SIZE command is dependent on
+         * the currently established STRU, MODE and TYPE parameters.
+         */
+        /* Therefore: switch to binary if the user did not specify ";type=a" */
+        ftp_set_TYPE(xfer_type, r, origin, bb, &ftpmessage);
+        rc = proxy_ftp_command(apr_pstrcat(p, "SIZE ",
+                           ftp_escape_globbingchars(p, path), CRLF, NULL),
+                           r, origin, bb, &ftpmessage);
+        if (rc == -1 || rc == 421) {
+            return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
+                                  "Error reading from remote server");
+        }
+        else if (rc == 213) {/* Size command ok */
+            int j;
+            for (j = 0; apr_isdigit(ftpmessage[j]); j++)
+                ;
+            ftpmessage[j] = '\0';
+            if (ftpmessage[0] != '\0')
+                 size = ftpmessage; /* already pstrdup'ed: no copy necessary */
+        }
+        else if (rc == 550) {    /* Not a regular file */
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                             "proxy: FTP: SIZE shows this is a directory");
+            dirlisting = 1;
+            rc = proxy_ftp_command(apr_pstrcat(p, "CWD ", 
+                           ftp_escape_globbingchars(p, path), CRLF, NULL),
+                           r, origin, bb, &ftpmessage);
+            /* possible results: 250, 421, 500, 501, 502, 530, 550 */
+            /* 250 Requested file action okay, completed. */
+            /* 421 Service not available, closing control connection. */
+            /* 500 Syntax error, command unrecognized. */
+            /* 501 Syntax error in parameters or arguments. */
+            /* 502 Command not implemented. */
+            /* 530 Not logged in. */
+            /* 550 Requested action not taken. */
+            if (rc == -1 || rc == 421) {
+                return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
+                                      "Error reading from remote server");
+            }
+            if (rc == 550) {
+                return ftp_proxyerror(r, backend, HTTP_NOT_FOUND, ftpmessage);
+            }
+            if (rc != 250) {
+                return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
+            }
+            path = "";
+            len = 0;
+        }
+    }
+
+    cwd = ftp_get_PWD(r, origin, bb);
+    if (cwd != NULL) {
+        apr_table_set(r->notes, "Directory-PWD", cwd);
+    }
+
+    if (dirlisting) {
+        ftp_set_TYPE('A', r, origin, bb, NULL);
+        /* If the current directory contains no slash, we are talking to
+         * a non-unix ftp system. Try LIST instead of "LIST -lag", it
+         * should return a long listing anyway (unlike NLST).
+         * Some exotic FTP servers might choke on the "-lag" switch.
+         */
+        /* Note that we do not escape the path here, to allow for
+         * queries like: ftp://user@host/apache/src/server/http_*.c
+         */
+        if (len != 0)
+            buf = apr_pstrcat(p, "LIST ", path, CRLF, NULL);
+        else if (cwd == NULL || strchr(cwd, '/') != NULL)
+            buf = apr_pstrcat(p, "LIST -lag", CRLF, NULL);
+        else
+            buf = "LIST" CRLF;
+    }
+    else {
+        /* switch to binary if the user did not specify ";type=a" */
+        ftp_set_TYPE(xfer_type, r, origin, bb, &ftpmessage);
+#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF))
+        /* from draft-ietf-ftpext-mlst-14.txt:
+         *   The FTP command, MODIFICATION TIME (MDTM), can be used to determine
+         *   when a file in the server NVFS was last modified.     <..>
+         *   The syntax of a time value is:
+         *           time-val       = 14DIGIT [ "." 1*DIGIT ]      <..>
+         *     Symbolically, a time-val may be viewed as
+         *           YYYYMMDDHHMMSS.sss
+         *     The "." and subsequent digits ("sss") are optional. <..>
+         *     Time values are always represented in UTC (GMT)
+         */
+        rc = proxy_ftp_command(apr_pstrcat(p, "MDTM ", ftp_escape_globbingchars(p, path), CRLF, NULL),
+                               r, origin, bb, &ftpmessage);
+        /* then extract the Last-Modified time from it (YYYYMMDDhhmmss or YYYYMMDDhhmmss.xxx GMT). */
+        if (rc == 213) {
+	    struct {
+	        char YYYY[4+1];
+		char MM[2+1];
+		char DD[2+1];
+		char hh[2+1];
+		char mm[2+1];
+		char ss[2+1];
+	    } time_val;
+	    if (6 == sscanf(ftpmessage, "%4[0-9]%2[0-9]%2[0-9]%2[0-9]%2[0-9]%2[0-9]",
+	        time_val.YYYY, time_val.MM, time_val.DD, time_val.hh, time_val.mm, time_val.ss)) {
+                struct tm tms;
+		memset (&tms, '\0', sizeof tms);
+		tms.tm_year = atoi(time_val.YYYY) - 1900;
+		tms.tm_mon  = atoi(time_val.MM)   - 1;
+		tms.tm_mday = atoi(time_val.DD);
+		tms.tm_hour = atoi(time_val.hh);
+		tms.tm_min  = atoi(time_val.mm);
+		tms.tm_sec  = atoi(time_val.ss);
+#ifdef HAVE_TIMEGM /* Does system have timegm()? */
+		mtime = timegm(&tms);
+		mtime *= APR_USEC_PER_SEC;
+#elif HAVE_GMTOFF /* does struct tm have a member tm_gmtoff? */
+                /* mktime will subtract the local timezone, which is not what we want.
+		 * Add it again because the MDTM string is GMT
+		 */
+		mtime = mktime(&tms);
+		mtime += tms.tm_gmtoff;
+		mtime *= APR_USEC_PER_SEC;
+#else
+		mtime = 0L;
+#endif
+            }
+	}
+#endif /* USE_MDTM */
+/* FIXME: Handle range requests - send REST */
+        buf = apr_pstrcat(p, "RETR ", ftp_escape_globbingchars(p, path), CRLF, NULL);
+    }
+    rc = proxy_ftp_command(buf, r, origin, bb, &ftpmessage);
+    /* rc is an intermediate response for the LIST or RETR commands */
+
+    /*
+     * RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530,
+     * 550 NLST: 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 502,
+     * 530
+     */
+    /* 110 Restart marker reply. */
+    /* 125 Data connection already open; transfer starting. */
+    /* 150 File status okay; about to open data connection. */
+    /* 226 Closing data connection. */
+    /* 250 Requested file action okay, completed. */
+    /* 421 Service not available, closing control connection. */
+    /* 425 Can't open data connection. */
+    /* 426 Connection closed; transfer aborted. */
+    /* 450 Requested file action not taken. */
+    /* 451 Requested action aborted. Local error in processing. */
+    /* 500 Syntax error, command unrecognized. */
+    /* 501 Syntax error in parameters or arguments. */
+    /* 530 Not logged in. */
+    /* 550 Requested action not taken. */
+    if (rc == -1 || rc == 421) {
+        return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
+                              "Error reading from remote server");
+    }
+    if (rc == 550) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: FTP: RETR failed, trying LIST instead");
+
+        /* Directory Listings should always be fetched in ASCII mode */
+        dirlisting = 1;
+        ftp_set_TYPE('A', r, origin, bb, NULL);
+
+        rc = proxy_ftp_command(apr_pstrcat(p, "CWD ",
+                               ftp_escape_globbingchars(p, path), CRLF, NULL),
+                               r, origin, bb, &ftpmessage);
+        /* possible results: 250, 421, 500, 501, 502, 530, 550 */
+        /* 250 Requested file action okay, completed. */
+        /* 421 Service not available, closing control connection. */
+        /* 500 Syntax error, command unrecognized. */
+        /* 501 Syntax error in parameters or arguments. */
+        /* 502 Command not implemented. */
+        /* 530 Not logged in. */
+        /* 550 Requested action not taken. */
+        if (rc == -1 || rc == 421) {
+            return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
+                                  "Error reading from remote server");
+        }
+        if (rc == 550) {
+            return ftp_proxyerror(r, backend, HTTP_NOT_FOUND, ftpmessage);
+        }
+        if (rc != 250) {
+            return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
+        }
+
+        /* Update current directory after CWD */
+        cwd = ftp_get_PWD(r, origin, bb);
+        if (cwd != NULL) {
+            apr_table_set(r->notes, "Directory-PWD", cwd);
+        }
+
+        /* See above for the "LIST" vs. "LIST -lag" discussion. */
+        rc = proxy_ftp_command((cwd == NULL || strchr(cwd, '/') != NULL)
+                               ? "LIST -lag" CRLF : "LIST" CRLF,
+                               r, origin, bb, &ftpmessage);
+
+        /* rc is an intermediate response for the LIST command (125 transfer starting, 150 opening data connection) */
+        if (rc == -1 || rc == 421)
+            return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY,
+                                  "Error reading from remote server");
+    }
+    if (rc != 125 && rc != 150 && rc != 226 && rc != 250) {
+        return ftp_proxyerror(r, backend, HTTP_BAD_GATEWAY, ftpmessage);
+    }
+
+    r->status = HTTP_OK;
+    r->status_line = "200 OK";
+
+    apr_rfc822_date(dates, r->request_time);
+    apr_table_setn(r->headers_out, "Date", dates);
+    apr_table_setn(r->headers_out, "Server", ap_get_server_version());
+
+    /* set content-type */
+    if (dirlisting) {
+        ap_set_content_type(r, "text/html");
+    }
+    else {
+        if (r->content_type) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: FTP: Content-Type set to %s", r->content_type);
+        }
+        else {
+            ap_set_content_type(r, ap_default_type(r));
+        }
+        if (xfer_type != 'A' && size != NULL) {
+            /* We "trust" the ftp server to really serve (size) bytes... */
+            apr_table_setn(r->headers_out, "Content-Length", size);
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                         "proxy: FTP: Content-Length set to %s", size);
+        }
+    }
+    apr_table_setn(r->headers_out, "Content-Type", r->content_type);
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "proxy: FTP: Content-Type set to %s", r->content_type);
+
+#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF))
+    if (mtime != 0L) {
+        char datestr[APR_RFC822_DATE_LEN];
+        apr_rfc822_date(datestr, mtime);
+        apr_table_set(r->headers_out, "Last-Modified", datestr);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "proxy: FTP: Last-Modified set to %s", datestr);
+    }
+#endif /* USE_MDTM */
+
+    /* If an encoding has been set by mistake, delete it.
+     * @@@ FIXME (e.g., for ftp://user@host/file*.tar.gz,
+     * @@@        the encoding is currently set to x-gzip)
+     */
+    if (dirlisting && r->content_encoding != NULL)
+        r->content_encoding = NULL;
+
+    /* set content-encoding (not for dir listings, they are uncompressed)*/
+    if (r->content_encoding != NULL && r->content_encoding[0] != '\0') {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+             "proxy: FTP: Content-Encoding set to %s", r->content_encoding);
+        apr_table_setn(r->headers_out, "Content-Encoding", r->content_encoding);
+    }
+
+    /* wait for connection */
+    if (use_port) {
+        for (;;) {
+            rv = apr_socket_accept(&data_sock, local_sock, r->pool);
+            if (rv == APR_EINTR) {
+                continue;
+            }
+            else if (rv == APR_SUCCESS) {
+                break;
+            }
+            else {
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
+                            "proxy: FTP: failed to accept data connection");
+                proxy_ftp_cleanup(r, backend);
+                return HTTP_BAD_GATEWAY;
+            }
+        }
+    }
+
+    /* the transfer socket is now open, create a new connection */
+    data = ap_run_create_connection(p, r->server, data_sock, r->connection->id,
+                                    r->connection->sbh, c->bucket_alloc);
+    if (!data) {
+        /*
+         * the peer reset the connection already; ap_run_create_connection() closed
+         * the socket
+         */
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+          "proxy: FTP: an error occurred creating the transfer connection");
+        proxy_ftp_cleanup(r, backend);
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    /* set up the connection filters */
+    ap_run_pre_connection(data, data_sock);
+
+    /*
+     * VI: Receive the Response ------------------------
+     *
+     * Get response from the remote ftp socket, and pass it up the filter chain.
+     */
+
+    /* send response */
+    r->sent_bodyct = 1;
+
+    if (dirlisting) {
+        /* insert directory filter */
+        ap_add_output_filter("PROXY_SEND_DIR", NULL, r, r->connection);
+    }
+
+    /* send body */
+    if (!r->header_only) {
+        apr_bucket *e;
+        int finish = FALSE;
+
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: FTP: start body send");
+
+        /* read the body, pass it to the output filters */
+        while (ap_get_brigade(data->input_filters, 
+                              bb, 
+                              AP_MODE_READBYTES, 
+                              APR_BLOCK_READ, 
+                              conf->io_buffer_size) == APR_SUCCESS) {
+#if DEBUGGING
+            {
+                apr_off_t readbytes;
+                apr_brigade_length(bb, 0, &readbytes);
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
+                             r->server, "proxy (PID %d): readbytes: %#x",
+                             getpid(), readbytes);
+            }
+#endif
+            /* sanity check */
+            if (APR_BRIGADE_EMPTY(bb)) {
+                apr_brigade_cleanup(bb);
+                break;
+            }
+
+            /* found the last brigade? */
+            if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
+                /* if this is the last brigade, cleanup the
+                 * backend connection first to prevent the
+                 * backend server from hanging around waiting
+                 * for a slow client to eat these bytes
+                 */
+                ap_flush_conn(data);
+                apr_socket_close(data_sock);
+                data_sock = NULL;
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                             "proxy: FTP: data connection closed");
+                /* signal that we must leave */
+                finish = TRUE;
+            }
+
+            /* if no EOS yet, then we must flush */
+            if (FALSE == finish) {
+                e = apr_bucket_flush_create(c->bucket_alloc);
+                APR_BRIGADE_INSERT_TAIL(bb, e);
+            }
+
+            /* try send what we read */
+            if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS) {
+                /* Ack! Phbtt! Die! User aborted! */
+                finish = TRUE;
+            }
+
+            /* make sure we always clean up after ourselves */
+            apr_brigade_cleanup(bb);
+
+            /* if we are done, leave */
+            if (TRUE == finish) {
+                break;
+            }
+        }
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: FTP: end body send");
+
+    }
+    if (data_sock) {
+        ap_flush_conn(data);
+        apr_socket_close(data_sock);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: FTP: data connection closed");
+    }
+
+    /* Retrieve the final response for the RETR or LIST commands */
+    rc = proxy_ftp_command(NULL, r, origin, bb, &ftpmessage);
+    apr_brigade_cleanup(bb);
+
+    /*
+     * VII: Clean Up -------------
+     *
+     * If there are no KeepAlives, or if the connection has been signalled to
+     * close, close the socket and clean up
+     */
+
+    /* finish */
+    rc = proxy_ftp_command("QUIT" CRLF,
+                           r, origin, bb, &ftpmessage);
+    /* responses: 221, 500 */
+    /* 221 Service closing control connection. */
+    /* 500 Syntax error, command unrecognized. */
+    ap_flush_conn(origin);
+    proxy_ftp_cleanup(r, backend);
+
+    apr_brigade_destroy(bb);
+    return OK;
+}
+
+static void ap_proxy_ftp_register_hook(apr_pool_t *p)
+{
+    /* hooks */
+    proxy_hook_scheme_handler(ap_proxy_ftp_handler, NULL, NULL, APR_HOOK_MIDDLE);
+    proxy_hook_canon_handler(ap_proxy_ftp_canon, NULL, NULL, APR_HOOK_MIDDLE);
+    /* filters */
+    ap_register_output_filter("PROXY_SEND_DIR", ap_proxy_send_dir_filter,
+                              NULL, AP_FTYPE_RESOURCE);
+}
+
+module AP_MODULE_DECLARE_DATA proxy_ftp_module = {
+    STANDARD20_MODULE_STUFF,
+    NULL,                       /* create per-directory config structure */
+    NULL,                       /* merge per-directory config structures */
+    NULL,                       /* create per-server config structure */
+    NULL,                       /* merge per-server config structures */
+    NULL,                       /* command apr_table_t */
+    ap_proxy_ftp_register_hook  /* register hooks */
+};
diff --git a/connectors/ajp/proxy/proxy_http.c b/connectors/ajp/proxy/proxy_http.c
new file mode 100644
index 0000000..b8ce262
--- /dev/null
+++ b/connectors/ajp/proxy/proxy_http.c
@@ -0,0 +1,1262 @@
+/* Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* HTTP routines for Apache proxy */
+
+#include "mod_proxy.h"
+
+module AP_MODULE_DECLARE_DATA proxy_http_module;
+
+int ap_proxy_http_canon(request_rec *r, char *url);
+int ap_proxy_http_handler(request_rec *r, proxy_worker *worker,
+                          proxy_server_conf *conf,
+                          char *url, const char *proxyname, 
+                          apr_port_t proxyport);
+
+static apr_status_t ap_proxy_http_cleanup(const char *scheme,
+                                          request_rec *r,
+                                          proxy_conn_rec *backend);
+
+/*
+ * Canonicalise http-like URLs.
+ *  scheme is the scheme for the URL
+ *  url    is the URL starting with the first '/'
+ *  def_port is the default port for this scheme.
+ */
+int ap_proxy_http_canon(request_rec *r, char *url)
+{
+    char *host, *path, *search, sport[7];
+    const char *err;
+    const char *scheme;
+    apr_port_t port, def_port;
+
+    /* ap_port_of_scheme() */
+    if (strncasecmp(url, "http:", 5) == 0) {
+        url += 5;
+        scheme = "http";
+    }
+    else if (strncasecmp(url, "https:", 6) == 0) {
+        url += 6;
+        scheme = "https";
+    }
+    else {
+        return DECLINED;
+    }
+    def_port = apr_uri_port_of_scheme(scheme);
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+             "proxy: HTTP: canonicalising URL %s", url);
+
+    /* do syntatic check.
+     * We break the URL into host, port, path, search
+     */
+    port = def_port;
+    err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
+    if (err) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                      "error parsing URL %s: %s",
+                      url, err);
+        return HTTP_BAD_REQUEST;
+    }
+
+    /* now parse path/search args, according to rfc1738 */
+    /* N.B. if this isn't a true proxy request, then the URL _path_
+     * has already been decoded.  True proxy requests have r->uri
+     * == r->unparsed_uri, and no others have that property.
+     */
+    if (r->uri == r->unparsed_uri) {
+        search = strchr(url, '?');
+        if (search != NULL)
+            *(search++) = '\0';
+    }
+    else
+        search = r->args;
+
+    /* process path */
+    path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq);
+    if (path == NULL)
+        return HTTP_BAD_REQUEST;
+
+    if (port != def_port)
+        apr_snprintf(sport, sizeof(sport), ":%d", port);
+    else
+        sport[0] = '\0';
+
+    if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
+        host = apr_pstrcat(r->pool, "[", host, "]", NULL);
+    }
+    r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport, 
+            "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
+    return OK;
+}
+ 
+static const char *ap_proxy_location_reverse_map(request_rec *r, proxy_server_conf *conf, const char *url)
+{
+    struct proxy_alias *ent;
+    int i, l1, l2;
+    char *u;
+
+    /* XXX FIXME: Make sure this handled the ambiguous case of the :80
+     * after the hostname */
+
+    l1 = strlen(url);
+    ent = (struct proxy_alias *)conf->raliases->elts;
+    for (i = 0; i < conf->raliases->nelts; i++) {
+        l2 = strlen(ent[i].real);
+        if (l1 >= l2 && strncasecmp(ent[i].real, url, l2) == 0) {
+            u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
+            return ap_construct_url(r->pool, u, r);
+        }
+    }
+    return url;
+}
+/* cookies are a bit trickier to match: we've got two substrings to worry
+ * about, and we can't just find them with strstr 'cos of case.  Regexp
+ * matching would be an easy fix, but for better consistency with all the
+ * other matches we'll refrain and use apr_strmatch to find path=/domain=
+ * and stick to plain strings for the config values.
+ */
+static const char *proxy_cookie_reverse_map(request_rec *r,
+                          proxy_server_conf *conf, const char *str)
+{
+    struct proxy_alias *ent;
+    size_t len = strlen(str);
+    const char* newpath = NULL ;
+    const char* newdomain = NULL ;
+    const char* pathp ;
+    const char* domainp ;
+    const char* pathe = NULL;
+    const char* domaine = NULL;
+    size_t l1, l2, poffs = 0, doffs = 0 ;
+    int i;
+    int ddiff = 0 ;
+    int pdiff = 0 ;
+    char* ret ;
+
+/* find the match and replacement, but save replacing until we've done
+   both path and domain so we know the new strlen
+*/
+    if ( pathp = apr_strmatch(conf->cookie_path_str, str, len) , pathp ) {
+        pathp += 5 ;
+        poffs = pathp - str ;
+        pathe = ap_strchr_c(pathp, ';') ;
+        l1 = pathe ? (pathe-pathp) : strlen(pathp) ;
+        pathe = pathp + l1 ;
+        ent = (struct proxy_alias *)conf->cookie_paths->elts;
+        for (i = 0; i < conf->cookie_paths->nelts; i++) {
+            l2 = strlen(ent[i].fake);
+            if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) {
+                newpath = ent[i].real ;
+                pdiff = strlen(newpath) - l1 ;
+                break ;
+            }
+        }
+    }
+    if ( domainp = apr_strmatch(conf->cookie_domain_str, str, len) , domainp ) {
+        domainp += 7 ;
+        doffs = domainp - str ;
+        domaine = ap_strchr_c(domainp, ';') ;
+        l1 = domaine ? (domaine-domainp) : strlen(domainp) ;
+        domaine = domainp + l1 ;
+        ent = (struct proxy_alias *)conf->cookie_domains->elts;
+        for (i = 0; i < conf->cookie_domains->nelts; i++) {
+            l2 = strlen(ent[i].fake);
+            if (l1 >= l2 && strncasecmp(ent[i].fake, domainp, l2) == 0) {
+                newdomain = ent[i].real ;
+                ddiff = strlen(newdomain) - l1 ;
+                break ;
+            }
+        }
+    }
+    if ( newpath ) {
+        ret = apr_palloc(r->pool, len+pdiff+ddiff+1) ;
+        l1 = strlen(newpath) ;
+        if ( newdomain ) {
+            l2 = strlen(newdomain) ;
+            if ( doffs > poffs ) {
+                memcpy(ret, str, poffs) ;
+                memcpy(ret+poffs, newpath, l1) ;
+                memcpy(ret+poffs+l1, pathe, domainp-pathe) ;
+                memcpy(ret+doffs+pdiff, newdomain, l2) ;
+                strcpy(ret+doffs+pdiff+l2, domaine) ;
+            } else {
+                memcpy(ret, str, doffs) ;
+                memcpy(ret+doffs, newdomain, l2) ;
+                memcpy(ret+doffs+l2, domaine, pathp-domaine) ;
+                memcpy(ret+poffs+ddiff, newpath, l1) ;
+                strcpy(ret+poffs+ddiff+l1, pathe) ;
+            }
+        } else {
+            memcpy(ret, str, poffs) ;
+            memcpy(ret+poffs, newpath, l1) ;
+            strcpy(ret+poffs+l1, pathe) ;
+        }
+    } else {
+        if ( newdomain ) {
+            ret = apr_palloc(r->pool, len+pdiff+ddiff+1) ;
+            l2 = strlen(newdomain) ;
+            memcpy(ret, str, doffs) ;
+            memcpy(ret+doffs, newdomain, l2) ;
+            strcpy(ret+doffs+l2, domaine) ;
+        } else {
+            ret = (char*) str ;        /* no change */
+        }
+    }
+    return ret ;
+}
+
+/* Clear all connection-based headers from the incoming headers table */
+static void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers)
+{
+    const char *name;
+    char *next = apr_pstrdup(p, apr_table_get(headers, "Connection"));
+
+    apr_table_unset(headers, "Proxy-Connection");
+    if (!next)
+        return;
+
+    while (*next) {
+        name = next;
+        while (*next && !apr_isspace(*next) && (*next != ',')) {
+            ++next;
+        }
+        while (*next && (apr_isspace(*next) || (*next == ','))) {
+            *next = '\0';
+            ++next;
+        }
+        apr_table_unset(headers, name);
+    }
+    apr_table_unset(headers, "Connection");
+}
+
+static
+apr_status_t ap_proxy_http_request(apr_pool_t *p, request_rec *r,
+                                   proxy_conn_rec *conn, conn_rec *origin, 
+                                   proxy_server_conf *conf,
+                                   apr_uri_t *uri,
+                                   char *url, char *server_portstr)
+{
+    conn_rec *c = r->connection;
+    char *buf;
+    apr_bucket *e, *last_header_bucket = NULL;
+    const apr_array_header_t *headers_in_array;
+    const apr_table_entry_t *headers_in;
+    int counter, seen_eos, send_chunks;
+    apr_status_t status;
+    apr_bucket_brigade *header_brigade, *body_brigade, *input_brigade;
+
+    header_brigade = apr_brigade_create(p, origin->bucket_alloc);
+    body_brigade = apr_brigade_create(p, origin->bucket_alloc);
+    input_brigade = apr_brigade_create(p, origin->bucket_alloc);
+
+    /*
+     * Send the HTTP/1.1 request to the remote server
+     */
+
+    /* strip connection listed hop-by-hop headers from the request */
+    /* even though in theory a connection: close coming from the client
+     * should not affect the connection to the server, it's unlikely
+     * that subsequent client requests will hit this thread/process, so
+     * we cancel server keepalive if the client does.
+     */
+    conn->close += ap_proxy_liststr(apr_table_get(r->headers_in,
+                                                  "Connection"), "close");
+
+    /* sub-requests never use keepalives */
+    if (r->main) {
+        conn->close++;
+    }
+
+    ap_proxy_clear_connection(p, r->headers_in);
+    if (conn->close) {
+        apr_table_setn(r->headers_in, "Connection", "close");
+        origin->keepalive = AP_CONN_CLOSE;
+    }
+
+    /* By default, we can not send chunks. That means we must buffer
+     * the entire request before sending it along to ensure we have
+     * the correct Content-Length attached.
+     */
+    send_chunks = 0;
+
+    if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) {
+        buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL);
+    } else {
+        buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL);
+        if (apr_table_get(r->subprocess_env, "proxy-sendchunks")) {
+            send_chunks = 1;
+        }
+    }
+    if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) {
+        apr_table_unset(r->headers_in, "Connection");
+        origin->keepalive = AP_CONN_CLOSE;
+    }
+    ap_xlate_proto_to_ascii(buf, strlen(buf));
+    e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
+    APR_BRIGADE_INSERT_TAIL(header_brigade, e);
+    if (conf->preserve_host == 0) {
+        if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) {
+            buf = apr_pstrcat(p, "Host: ", uri->hostname, ":", uri->port_str,
+                              CRLF, NULL);
+        } else {
+            buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL);
+        }
+    } 
+    else {
+        /* don't want to use r->hostname, as the incoming header might have a 
+         * port attached 
+         */
+        const char* hostname = apr_table_get(r->headers_in,"Host");        
+        if (!hostname) {
+            hostname =  r->server->server_hostname;
+            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+                          "proxy: no HTTP 0.9 request (with no host line) "
+                          "on incoming request and preserve host set "
+                          "forcing hostname to be %s for uri %s", 
+                          hostname, 
+                          r->uri );
+        }
+        buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL);
+    }
+    ap_xlate_proto_to_ascii(buf, strlen(buf));
+    e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);        
+    APR_BRIGADE_INSERT_TAIL(header_brigade, e);
+
+    /* handle Via */
+    if (conf->viaopt == via_block) {
+        /* Block all outgoing Via: headers */
+        apr_table_unset(r->headers_in, "Via");
+    } else if (conf->viaopt != via_off) {
+        const char *server_name = ap_get_server_name(r);
+        /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
+         * then the server name returned by ap_get_server_name() is the
+         * origin server name (which does make too much sense with Via: headers)
+         * so we use the proxy vhost's name instead.
+         */
+        if (server_name == r->hostname)
+            server_name = r->server->server_hostname;
+        /* Create a "Via:" request header entry and merge it */
+        /* Generate outgoing Via: header with/without server comment: */
+        apr_table_mergen(r->headers_in, "Via",
+                         (conf->viaopt == via_full)
+                         ? apr_psprintf(p, "%d.%d %s%s (%s)",
+                                        HTTP_VERSION_MAJOR(r->proto_num),
+                                        HTTP_VERSION_MINOR(r->proto_num),
+                                        server_name, server_portstr,
+                                        AP_SERVER_BASEVERSION)
+                         : apr_psprintf(p, "%d.%d %s%s",
+                                        HTTP_VERSION_MAJOR(r->proto_num),
+                                        HTTP_VERSION_MINOR(r->proto_num),
+                                        server_name, server_portstr)
+        );
+    }
+
+    /* X-Forwarded-*: handling
+     *
+     * XXX Privacy Note:
+     * -----------------
+     *
+     * These request headers are only really useful when the mod_proxy
+     * is used in a reverse proxy configuration, so that useful info
+     * about the client can be passed through the reverse proxy and on
+     * to the backend server, which may require the information to
+     * function properly.
+     *
+     * In a forward proxy situation, these options are a potential
+     * privacy violation, as information about clients behind the proxy
+     * are revealed to arbitrary servers out there on the internet.
+     *
+     * The HTTP/1.1 Via: header is designed for passing client
+     * information through proxies to a server, and should be used in
+     * a forward proxy configuation instead of X-Forwarded-*. See the
+     * ProxyVia option for details.
+     */
+
+    if (PROXYREQ_REVERSE == r->proxyreq) {
+        const char *buf;
+
+        /* Add X-Forwarded-For: so that the upstream has a chance to
+         * determine, where the original request came from.
+         */
+        apr_table_mergen(r->headers_in, "X-Forwarded-For",
+                       r->connection->remote_ip);
+
+        /* Add X-Forwarded-Host: so that upstream knows what the
+         * original request hostname was.
+         */
+        if ((buf = apr_table_get(r->headers_in, "Host"))) {
+            apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf);
+        }
+
+        /* Add X-Forwarded-Server: so that upstream knows what the
+         * name of this proxy server is (if there are more than one)
+         * XXX: This duplicates Via: - do we strictly need it?
+         */
+        apr_table_mergen(r->headers_in, "X-Forwarded-Server",
+                       r->server->server_hostname);
+    }
+
+    /* send request headers */
+    proxy_run_fixups(r);
+    headers_in_array = apr_table_elts(r->headers_in);
+    headers_in = (const apr_table_entry_t *) headers_in_array->elts;
+    for (counter = 0; counter < headers_in_array->nelts; counter++) {
+        if (headers_in[counter].key == NULL || headers_in[counter].val == NULL
+
+        /* Clear out hop-by-hop request headers not to send
+         * RFC2616 13.5.1 says we should strip these headers
+         */
+                /* Already sent */
+            || !apr_strnatcasecmp(headers_in[counter].key, "Host")
+
+            || !apr_strnatcasecmp(headers_in[counter].key, "Keep-Alive")
+            || !apr_strnatcasecmp(headers_in[counter].key, "TE")
+            || !apr_strnatcasecmp(headers_in[counter].key, "Trailer")
+            || !apr_strnatcasecmp(headers_in[counter].key, "Transfer-Encoding")
+            || !apr_strnatcasecmp(headers_in[counter].key, "Upgrade")
+
+            /* We have no way of knowing whether this Content-Length will
+             * be accurate, so we must not include it.
+             */
+            || !apr_strnatcasecmp(headers_in[counter].key, "Content-Length")
+        /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be 
+         * suppressed if THIS server requested the authentication,
+         * not when a frontend proxy requested it!
+         *
+         * The solution to this problem is probably to strip out
+         * the Proxy-Authorisation header in the authorisation
+         * code itself, not here. This saves us having to signal
+         * somehow whether this request was authenticated or not.
+         */
+            || !apr_strnatcasecmp(headers_in[counter].key,"Proxy-Authorization")
+            || !apr_strnatcasecmp(headers_in[counter].key,"Proxy-Authenticate")) {
+            continue;
+        }
+        /* for sub-requests, ignore freshness/expiry headers */
+        if (r->main) {
+                if (headers_in[counter].key == NULL || headers_in[counter].val == NULL
+                     || !apr_strnatcasecmp(headers_in[counter].key, "If-Match")
+                     || !apr_strnatcasecmp(headers_in[counter].key, "If-Modified-Since")
+                     || !apr_strnatcasecmp(headers_in[counter].key, "If-Range")
+                     || !apr_strnatcasecmp(headers_in[counter].key, "If-Unmodified-Since")                     
+                     || !apr_strnatcasecmp(headers_in[counter].key, "If-None-Match")) {
+                    continue;
+                }
+        }
+
+
+        buf = apr_pstrcat(p, headers_in[counter].key, ": ",
+                          headers_in[counter].val, CRLF,
+                          NULL);
+        ap_xlate_proto_to_ascii(buf, strlen(buf));
+        e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(header_brigade, e);
+    }
+
+    /* If we can send chunks, do so! */
+    if (send_chunks) {
+        const char *te_hdr = "Transfer-Encoding: chunked" CRLF;
+
+        buf = apr_pmemdup(p, te_hdr, sizeof(te_hdr)-1);
+        ap_xlate_proto_to_ascii(buf, sizeof(te_hdr)-1);
+
+        e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(header_brigade, e);
+    }
+    else {
+        last_header_bucket = APR_BRIGADE_LAST(header_brigade);
+    }
+
+    /* add empty line at the end of the headers */
+#if APR_CHARSET_EBCDIC
+    e = apr_bucket_immortal_create("\015\012", 2, c->bucket_alloc);
+#else
+    e = apr_bucket_immortal_create(CRLF, sizeof(CRLF)-1, c->bucket_alloc);
+#endif
+    APR_BRIGADE_INSERT_TAIL(header_brigade, e);
+    e = apr_bucket_flush_create(c->bucket_alloc);
+    APR_BRIGADE_INSERT_TAIL(header_brigade, e);
+
+    if (send_chunks) {
+        status = ap_pass_brigade(origin->output_filters, header_brigade);
+
+        if (status != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+                         "proxy: request failed to %pI (%s)",
+                         conn->worker->cp->addr, conn->hostname);
+            return status;
+        }
+    }
+
+    /* send the request data, if any. */
+    seen_eos = 0;
+    do {
+        char chunk_hdr[20];  /* must be here due to transient bucket. */
+
+        status = ap_get_brigade(r->input_filters, input_brigade,
+                                AP_MODE_READBYTES, APR_BLOCK_READ,
+                                HUGE_STRING_LEN);
+
+        if (status != APR_SUCCESS) {
+            return status;
+        }
+
+        /* If this brigade contain EOS, either stop or remove it. */
+        if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) {
+            seen_eos = 1;
+
+            /* As a shortcut, if this brigade is simply an EOS bucket,
+             * don't send anything down the filter chain.
+             */
+            if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade))) {
+                break;
+            }
+
+            /* We can't pass this EOS to the output_filters. */
+            e = APR_BRIGADE_LAST(input_brigade);
+            apr_bucket_delete(e);
+        }
+
+        if (send_chunks) {
+#define ASCII_CRLF  "\015\012"
+#define ASCII_ZERO  "\060"
+            apr_size_t hdr_len;
+            apr_off_t bytes;
+
+            apr_brigade_length(input_brigade, 1, &bytes);
+
+            hdr_len = apr_snprintf(chunk_hdr, sizeof(chunk_hdr),
+                                   "%" APR_UINT64_T_HEX_FMT CRLF, 
+                                   (apr_uint64_t)bytes);
+
+            ap_xlate_proto_to_ascii(chunk_hdr, hdr_len);
+            e = apr_bucket_transient_create(chunk_hdr, hdr_len,
+                                            body_brigade->bucket_alloc);
+            APR_BRIGADE_INSERT_HEAD(input_brigade, e);
+
+            /*
+             * Append the end-of-chunk CRLF
+             */
+            e = apr_bucket_immortal_create(ASCII_CRLF, 2, c->bucket_alloc);
+            APR_BRIGADE_INSERT_TAIL(input_brigade, e);
+        }
+
+        e = apr_bucket_flush_create(c->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(input_brigade, e);
+
+        APR_BRIGADE_CONCAT(body_brigade, input_brigade);
+
+        if (send_chunks) {
+            status = ap_pass_brigade(origin->output_filters, body_brigade);
+
+            if (status != APR_SUCCESS) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+                             "proxy: pass request data failed to %pI (%s)",
+                         conn->worker->cp->addr, conn->hostname);
+                return status;
+            }
+
+            apr_brigade_cleanup(body_brigade);
+        }
+
+    } while (!seen_eos);
+
+    if (send_chunks) {
+        e = apr_bucket_immortal_create(ASCII_ZERO ASCII_CRLF
+                                       /* <trailers> */
+                                       ASCII_CRLF, 5, c->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(body_brigade, e);
+    }
+
+    if (!send_chunks) {
+        apr_off_t bytes;
+
+        apr_brigade_length(body_brigade, 1, &bytes);
+
+        if (bytes) {
+            const char *cl_hdr = "Content-Length", *cl_val;
+            cl_val = apr_off_t_toa(c->pool, bytes);
+            buf = apr_pstrcat(p, cl_hdr, ": ", cl_val, CRLF, NULL);
+            ap_xlate_proto_to_ascii(buf, strlen(buf));
+            e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc);
+            APR_BUCKET_INSERT_AFTER(last_header_bucket, e);
+        }
+        status = ap_pass_brigade(origin->output_filters, header_brigade);
+        if (status != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+                         "proxy: pass request data failed to %pI (%s)",
+                         conn->worker->cp->addr, conn->hostname);
+            return status;
+        }
+
+        apr_brigade_cleanup(header_brigade);
+    }
+
+    status = ap_pass_brigade(origin->output_filters, body_brigade);
+
+    if (status != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server,
+                     "proxy: pass request data failed to %pI (%s)",
+                      conn->worker->cp->addr, conn->hostname);
+        return status;
+    }
+ 
+    apr_brigade_cleanup(body_brigade);
+
+    return APR_SUCCESS;
+}
+static void process_proxy_header(request_rec* r, proxy_server_conf* c,
+                      const char* key, const char* value)
+{
+    static const char* date_hdrs[]
+        = { "Date", "Expires", "Last-Modified", NULL } ;
+    static const struct {
+        const char* name ;
+        const char* (*func)(request_rec*, proxy_server_conf*, const char*) ;
+    } transform_hdrs[] = {
+        { "Location", ap_proxy_location_reverse_map } ,
+        { "Content-Location", ap_proxy_location_reverse_map } ,
+        { "URI", ap_proxy_location_reverse_map } ,
+        { "Set-Cookie", proxy_cookie_reverse_map } ,
+        { NULL, NULL }
+    } ;
+    int i ;
+    for ( i = 0 ; date_hdrs[i] ; ++i ) {
+        if ( !strcasecmp(date_hdrs[i], key) ) {
+            apr_table_add(r->headers_out, key,
+                ap_proxy_date_canon(r->pool, value)) ;
+            return ;
+        }
+    }
+    for ( i = 0 ; transform_hdrs[i].name ; ++i ) {
+        if ( !strcasecmp(transform_hdrs[i].name, key) ) {
+            apr_table_add(r->headers_out, key,
+                (*transform_hdrs[i].func)(r, c, value)) ;
+            return ;
+       }
+    }
+    apr_table_add(r->headers_out, key, value) ;
+    return ;
+}
+
+static void ap_proxy_read_headers(request_rec *r, request_rec *rr, char *buffer, int size, conn_rec *c)
+{
+    int len;
+    char *value, *end;
+    char field[MAX_STRING_LEN];
+    int saw_headers = 0;
+    void *sconf = r->server->module_config;
+    proxy_server_conf *psc;
+
+    psc = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
+
+    r->headers_out = apr_table_make(r->pool, 20);
+
+    /*
+     * Read header lines until we get the empty separator line, a read error,
+     * the connection closes (EOF), or we timeout.
+     */
+    while ((len = ap_getline(buffer, size, rr, 1)) > 0) {
+
+        if (!(value = strchr(buffer, ':'))) {     /* Find the colon separator */
+
+            /* We may encounter invalid headers, usually from buggy
+             * MS IIS servers, so we need to determine just how to handle
+             * them. We can either ignore them, assume that they mark the
+             * start-of-body (eg: a missing CRLF) or (the default) mark
+             * the headers as totally bogus and return a 500. The sole
+             * exception is an extra "HTTP/1.0 200, OK" line sprinkled
+             * in between the usual MIME headers, which is a favorite
+             * IIS bug.
+             */
+             /* XXX: The mask check is buggy if we ever see an HTTP/1.10 */
+
+            if (!apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
+                if (psc->badopt == bad_error) {
+                    /* Nope, it wasn't even an extra HTTP header. Give up. */
+                    return ;
+                }
+                else if (psc->badopt == bad_body) {
+                    /* if we've already started loading headers_out, then
+                     * return what we've accumulated so far, in the hopes
+                     * that they are useful. Otherwise, we completely bail.
+                     */
+                    /* FIXME: We've already scarfed the supposed 1st line of
+                     * the body, so the actual content may end up being bogus
+                     * as well. If the content is HTML, we may be lucky.
+                     */
+                    if (saw_headers) {
+                        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+                         "proxy: Starting body due to bogus non-header in headers "
+                         "returned by %s (%s)", r->uri, r->method);
+                        return ;
+                    } else {
+                         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+                         "proxy: No HTTP headers "
+                         "returned by %s (%s)", r->uri, r->method);
+                        return ;
+                    }
+                }
+            }
+            /* this is the psc->badopt == bad_ignore case */
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+                         "proxy: Ignoring bogus HTTP header "
+                         "returned by %s (%s)", r->uri, r->method);
+            continue;
+        }
+
+        *value = '\0';
+        ++value;
+        /* XXX: RFC2068 defines only SP and HT as whitespace, this test is
+         * wrong... and so are many others probably.
+         */
+        while (apr_isspace(*value))
+            ++value;            /* Skip to start of value   */
+
+        /* should strip trailing whitespace as well */
+        for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --
+end)
+            *end = '\0';
+
+        /* make sure we add so as not to destroy duplicated headers
+         * Modify headers requiring canonicalisation and/or affected
+         * by ProxyPassReverse and family with process_proxy_header
+         */
+        process_proxy_header(r, psc, buffer, value) ;
+        saw_headers = 1;
+
+        /* the header was too long; at the least we should skip extra data */
+        if (len >= size - 1) {
+            while ((len = ap_getline(field, MAX_STRING_LEN, rr, 1))
+                    >= MAX_STRING_LEN - 1) {
+                /* soak up the extra data */
+            }
+            if (len == 0) /* time to exit the larger loop as well */
+                break;
+        }
+    }
+}
+
+
+
+static int addit_dammit(void *v, const char *key, const char *val)
+{
+    apr_table_addn(v, key, val);
+    return 1;
+}
+
+static
+apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
+                                            proxy_conn_rec *backend,
+                                            conn_rec *origin,
+                                            proxy_server_conf *conf,
+                                            char *server_portstr) {
+    conn_rec *c = r->connection;
+    char buffer[HUGE_STRING_LEN];
+    char keepchar;
+    request_rec *rp;
+    apr_bucket *e;
+    apr_bucket_brigade *bb;
+    int len, backasswards;
+    int interim_response; /* non-zero whilst interim 1xx responses
+                           * are being read. */
+    apr_table_t *save_table;
+
+    bb = apr_brigade_create(p, c->bucket_alloc);
+
+    /* Get response from the remote server, and pass it up the
+     * filter chain
+     */
+
+    rp = ap_proxy_make_fake_req(origin, r);
+    /* In case anyone needs to know, this is a fake request that is really a
+     * response.
+     */
+    rp->proxyreq = PROXYREQ_RESPONSE;
+
+    do {
+        apr_brigade_cleanup(bb);
+
+        len = ap_getline(buffer, sizeof(buffer), rp, 0);
+        if (len == 0) {
+            /* handle one potential stray CRLF */
+            len = ap_getline(buffer, sizeof(buffer), rp, 0);
+        }
+        if (len <= 0) {
+            ap_proxy_http_cleanup(NULL, r, backend);
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                          "proxy: error reading status line from remote "
+                          "server %s", backend->hostname);
+            return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                                 "Error reading from remote server");
+        }
+
+       /* Is it an HTTP/1 response?
+        * This is buggy if we ever see an HTTP/1.10
+        */
+        if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
+            int major, minor;
+
+            if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) {
+                major = 1;
+                minor = 1;
+            }
+            /* If not an HTTP/1 message or
+             * if the status line was > 8192 bytes
+             */
+            else if ((buffer[5] != '1') || (len >= sizeof(buffer)-1)) {
+                ap_proxy_http_cleanup(NULL, r, backend);
+                return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                apr_pstrcat(p, "Corrupt status line returned by remote "
+                            "server: ", buffer, NULL));
+            }
+            backasswards = 0;
+
+            keepchar = buffer[12];
+            buffer[12] = '\0';
+            r->status = atoi(&buffer[9]);
+
+            if (keepchar != '\0') {
+                buffer[12] = keepchar;
+            } else {
+                /* 2616 requires the space in Status-Line; the origin
+                 * server may have sent one but ap_rgetline_core will
+                 * have stripped it. */
+                buffer[12] = ' ';
+                buffer[13] = '\0';
+            }
+            r->status_line = apr_pstrdup(p, &buffer[9]);
+            
+
+            /* read the headers. */
+            /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers*/
+            /* Also, take care with headers with multiple occurences. */
+
+            /* First, tuck away all already existing cookies */
+            save_table = apr_table_make(r->pool, 2);
+            apr_table_do(addit_dammit, save_table, r->headers_out,
+                         "Set-Cookie", NULL);
+
+	    /* shove the headers direct into r->headers_out */
+            ap_proxy_read_headers(r, rp, buffer, sizeof(buffer), origin);
+
+            if (r->headers_out == NULL) {
+                ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
+                             r->server, "proxy: bad HTTP/%d.%d header "
+                             "returned by %s (%s)", major, minor, r->uri,
+                             r->method);
+                backend->close += 1;
+                /*
+                 * ap_send_error relies on a headers_out to be present. we
+                 * are in a bad position here.. so force everything we send out
+                 * to have nothing to do with the incoming packet
+                 */
+                r->headers_out = apr_table_make(r->pool,1);
+                r->status = HTTP_BAD_GATEWAY;
+                r->status_line = "bad gateway";
+                return r->status;
+
+            } else {
+                const char *buf;
+
+                /* Now, add in the just read cookies */
+                apr_table_do(addit_dammit, save_table, r->headers_out,
+        	             "Set-Cookie", NULL);
+
+                /* and now load 'em all in */
+                if (!apr_is_empty_table(save_table)) {
+                    apr_table_unset(r->headers_out, "Set-Cookie");
+                    r->headers_out = apr_table_overlay(r->pool,
+                                                       r->headers_out,
+                                                       save_table);
+                }
+                
+                /* strip connection listed hop-by-hop headers from response */
+                backend->close += ap_proxy_liststr(apr_table_get(r->headers_out,
+                                                                 "Connection"),
+                                                  "close");
+                ap_proxy_clear_connection(p, r->headers_out);
+                if ((buf = apr_table_get(r->headers_out, "Content-Type"))) {
+                    ap_set_content_type(r, apr_pstrdup(p, buf));
+                }            
+                ap_proxy_pre_http_request(origin,rp);
+            }
+
+            /* handle Via header in response */
+            if (conf->viaopt != via_off && conf->viaopt != via_block) {
+                const char *server_name = ap_get_server_name(r);
+                /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host,
+                 * then the server name returned by ap_get_server_name() is the
+                 * origin server name (which does make too much sense with Via: headers)
+                 * so we use the proxy vhost's name instead.
+                 */
+                if (server_name == r->hostname)
+                    server_name = r->server->server_hostname;
+                /* create a "Via:" response header entry and merge it */
+                apr_table_mergen(r->headers_out, "Via",
+                                 (conf->viaopt == via_full)
+                                     ? apr_psprintf(p, "%d.%d %s%s (%s)",
+                                           HTTP_VERSION_MAJOR(r->proto_num),
+                                           HTTP_VERSION_MINOR(r->proto_num),
+                                           server_name,
+                                           server_portstr,
+                                           AP_SERVER_BASEVERSION)
+                                     : apr_psprintf(p, "%d.%d %s%s",
+                                           HTTP_VERSION_MAJOR(r->proto_num),
+                                           HTTP_VERSION_MINOR(r->proto_num),
+                                           server_name,
+                                           server_portstr)
+                );
+            }
+
+            /* cancel keepalive if HTTP/1.0 or less */
+            if ((major < 1) || (minor < 1)) {
+                backend->close += 1;
+                origin->keepalive = AP_CONN_CLOSE;
+            }
+        } else {
+            /* an http/0.9 response */
+            backasswards = 1;
+            r->status = 200;
+            r->status_line = "200 OK";
+            backend->close += 1;
+        }
+
+        interim_response = ap_is_HTTP_INFO(r->status);
+        if (interim_response) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+                         "proxy: HTTP: received interim %d response",
+                         r->status);
+        }
+        /* Moved the fixups of Date headers and those affected by
+         * ProxyPassReverse/etc from here to ap_proxy_read_headers
+         */
+
+        if ((r->status == 401) && (conf->error_override != 0)) {
+            const char *buf;
+            const char *wa = "WWW-Authenticate";
+            if ((buf = apr_table_get(r->headers_out, wa))) {
+                apr_table_set(r->err_headers_out, wa, buf);
+            } else {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                             "proxy: origin server sent 401 without WWW-Authenticate header");
+            }
+        }
+
+        r->sent_bodyct = 1;
+        /* Is it an HTTP/0.9 response? If so, send the extra data */
+        if (backasswards) {
+            apr_ssize_t cntr = len;
+            /*@@@FIXME:
+             * At this point in response processing of a 0.9 response,
+             * we don't know yet whether data is binary or not.
+             * mod_charset_lite will get control later on, so it cannot
+             * decide on the conversion of this buffer full of data.
+             * However, chances are that we are not really talking to an
+             * HTTP/0.9 server, but to some different protocol, therefore
+             * the best guess IMHO is to always treat the buffer as "text/x":
+             */
+            ap_xlate_proto_to_ascii(buffer, len);
+            e = apr_bucket_heap_create(buffer, cntr, NULL, c->bucket_alloc);
+            APR_BRIGADE_INSERT_TAIL(bb, e);
+        }
+
+        /* send body - but only if a body is expected */
+        if ((!r->header_only) &&                   /* not HEAD request */
+            !interim_response &&                   /* not any 1xx response */
+            (r->status != HTTP_NO_CONTENT) &&      /* not 204 */
+            (r->status != HTTP_RESET_CONTENT) &&   /* not 205 */
+            (r->status != HTTP_NOT_MODIFIED)) {    /* not 304 */
+
+            /* We need to copy the output headers and treat them as input
+             * headers as well.  BUT, we need to do this before we remove
+             * TE, so that they are preserved accordingly for
+             * ap_http_filter to know where to end.
+             */
+            rp->headers_in = apr_table_copy(r->pool, r->headers_out);
+
+            apr_table_unset(r->headers_out,"Transfer-Encoding");
+
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                         "proxy: start body send");
+             
+            /*
+             * if we are overriding the errors, we can't put the content
+             * of the page into the brigade
+             */
+            if (conf->error_override == 0 || ap_is_HTTP_SUCCESS(r->status)) {
+
+                /* read the body, pass it to the output filters */
+                int finish = FALSE;
+                while (ap_get_brigade(rp->input_filters, 
+                                      bb, 
+                                      AP_MODE_READBYTES, 
+                                      APR_BLOCK_READ, 
+                                      conf->io_buffer_size) == APR_SUCCESS) {
+#if DEBUGGING
+                    {
+                    apr_off_t readbytes;
+                    apr_brigade_length(bb, 0, &readbytes);
+                    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
+                                 r->server, "proxy (PID %d): readbytes: %#x",
+                                 getpid(), readbytes);
+                    }
+#endif
+                    /* sanity check */
+                    if (APR_BRIGADE_EMPTY(bb)) {
+                        apr_brigade_cleanup(bb);
+                        break;
+                    }
+
+                    /* found the last brigade? */
+                    if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
+                        /* if this is the last brigade, cleanup the
+                         * backend connection first to prevent the
+                         * backend server from hanging around waiting
+                         * for a slow client to eat these bytes
+                         */
+                        backend->close = 1;
+                        /* signal that we must leave */
+                        finish = TRUE;
+                    }
+
+                    /* try send what we read */
+                    if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS) {
+                        /* Ack! Phbtt! Die! User aborted! */
+                        backend->close = 1;  /* this causes socket close below */
+                        finish = TRUE;
+                    }
+
+                    /* make sure we always clean up after ourselves */
+                    apr_brigade_cleanup(bb);
+
+                    /* if we are done, leave */
+                    if (TRUE == finish) {
+                        break;
+                    }
+                }
+            }
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                         "proxy: end body send");
+        } else {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                         "proxy: header only");
+        }
+    } while (interim_response);
+
+    if (conf->error_override) {
+        /* the code above this checks for 'OK' which is what the hook expects */
+        if (ap_is_HTTP_SUCCESS(r->status))
+            return OK;
+        else {
+            /* clear r->status for override error, otherwise ErrorDocument
+             * thinks that this is a recursive error, and doesn't find the
+             * custom error page
+             */
+            int status = r->status;
+            r->status = HTTP_OK;
+            /* Discard body, if one is expected */
+            if ((status != HTTP_NO_CONTENT) && /* not 204 */
+                (status != HTTP_RESET_CONTENT) && /* not 205 */
+                (status != HTTP_NOT_MODIFIED)) { /* not 304 */
+               ap_discard_request_body(rp);
+           }
+            return status;
+        }
+    } else 
+        return OK;
+}
+
+static
+apr_status_t ap_proxy_http_cleanup(const char *scheme, request_rec *r,
+                                   proxy_conn_rec *backend)
+{
+    /* If there are no KeepAlives, or if the connection has been signalled
+     * to close, close the socket and clean up
+     */
+
+    /* if the connection is < HTTP/1.1, or Connection: close,
+     * we close the socket, otherwise we leave it open for KeepAlive support
+     */
+    if (backend->close || (r->proto_num < HTTP_VERSION(1,1))) {
+        backend->close_on_recycle = 1;
+        ap_set_module_config(r->connection->conn_config, &proxy_http_module, NULL);
+        ap_proxy_release_connection(scheme, backend, r->server);    
+    }
+    return OK;
+}
+
+/*
+ * This handles http:// URLs, and other URLs using a remote proxy over http
+ * If proxyhost is NULL, then contact the server directly, otherwise
+ * go via the proxy.
+ * Note that if a proxy is used, then URLs other than http: can be accessed,
+ * also, if we have trouble which is clearly specific to the proxy, then
+ * we return DECLINED so that we can try another proxy. (Or the direct
+ * route.)
+ */
+int ap_proxy_http_handler(request_rec *r, proxy_worker *worker,
+                          proxy_server_conf *conf,
+                          char *url, const char *proxyname, 
+                          apr_port_t proxyport)
+{
+    int status;
+    char server_portstr[32];
+    char *scheme;
+    const char *proxy_function;
+    const char *u;
+    proxy_conn_rec *backend = NULL;
+    int is_ssl = 0;
+
+    /* Note: Memory pool allocation.
+     * A downstream keepalive connection is always connected to the existence
+     * (or not) of an upstream keepalive connection. If this is not done then
+     * load balancing against multiple backend servers breaks (one backend
+     * server ends up taking 100% of the load), and the risk is run of
+     * downstream keepalive connections being kept open unnecessarily. This
+     * keeps webservers busy and ties up resources.
+     *
+     * As a result, we allocate all sockets out of the upstream connection
+     * pool, and when we want to reuse a socket, we check first whether the
+     * connection ID of the current upstream connection is the same as that
+     * of the connection when the socket was opened.
+     */
+    apr_pool_t *p = r->connection->pool;
+    conn_rec *c = r->connection;
+    apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri));
+
+    /* find the scheme */
+    u = strchr(url, ':');
+    if (u == NULL || u[1] != '/' || u[2] != '/' || u[3] == '\0')
+       return DECLINED;
+    if ((u - url) > 14)
+        return HTTP_BAD_REQUEST;
+    scheme = apr_pstrndup(c->pool, url, u - url);
+    /* scheme is lowercase */
+    apr_tolower(scheme);
+    /* is it for us? */
+    if (strcmp(scheme, "https") == 0) {
+        if (!ap_proxy_ssl_enable(NULL)) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                         "proxy: HTTPS: declining URL %s"
+                         " (mod_ssl not configured?)", url);
+            return DECLINED;
+        }
+        is_ssl = 1;
+        proxy_function = "HTTPS";
+    }
+    else if (!(strcmp(scheme, "http") == 0 || (strcmp(scheme, "ftp") == 0 && proxyname))) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: HTTP: declining URL %s", url);
+        return DECLINED; /* only interested in HTTP, or FTP via proxy */
+    }
+    else {
+        if (*scheme == 'h')
+            proxy_function = "HTTP";
+        else
+            proxy_function = "FTP";
+    }
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+             "proxy: HTTP: serving URL %s", url);
+    
+    
+    /* only use stored info for top-level pages. Sub requests don't share 
+     * in keepalives
+     */
+    if (!r->main) {
+        backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config,
+                                                      &proxy_http_module);
+    }
+    /* create space for state information */
+    if (!backend) {
+        status = ap_proxy_acquire_connection(proxy_function, &backend, worker, r->server);
+        if (status != OK) {
+            if (backend) {
+                backend->close_on_recycle = 1;
+                ap_proxy_release_connection(proxy_function, backend, r->server);
+            }
+            return status;
+        }
+        if (!r->main) {
+            ap_set_module_config(c->conn_config, &proxy_http_module, backend);
+        }
+    }
+
+    backend->is_ssl = is_ssl;
+    backend->close_on_recycle = 1;
+
+    /* Step One: Determine Who To Connect To */
+    status = ap_proxy_determine_connection(p, r, conf, worker, backend, c->pool,
+                                           uri, &url, proxyname, proxyport,
+                                           server_portstr,
+                                           sizeof(server_portstr));
+
+    if ( status != OK ) {
+        return status;
+    }
+
+    /* Step Two: Make the Connection */
+    if (ap_proxy_connect_backend(proxy_function, backend, worker, r->server)) {
+        return HTTP_SERVICE_UNAVAILABLE;
+    }
+
+    /* Step Three: Create conn_rec */
+    if (!backend->connection) {
+        status = ap_proxy_connection_create(proxy_function, backend, c, r->server);
+        if (status != OK)
+            return status;
+    }
+   
+    /* Step Four: Send the Request */
+    status = ap_proxy_http_request(p, r, backend, backend->connection, conf, uri, url,
+                                   server_portstr);
+    if ( status != OK ) {
+        return status;
+    }
+
+    /* Step Five: Receive the Response */
+    status = ap_proxy_http_process_response(p, r, backend, backend->connection, conf,
+                                            server_portstr);
+    if (status != OK) {
+        /* clean up even if there is an error */
+        ap_proxy_http_cleanup(proxy_function, r, backend);
+        return status;
+    }
+
+    /* Step Six: Clean Up */
+    status = ap_proxy_http_cleanup(proxy_function, r, backend);
+    if ( status != OK ) {
+        return status;
+    }
+
+    return OK;
+}
+
+static void ap_proxy_http_register_hook(apr_pool_t *p)
+{
+    proxy_hook_scheme_handler(ap_proxy_http_handler, NULL, NULL, APR_HOOK_FIRST);
+    proxy_hook_canon_handler(ap_proxy_http_canon, NULL, NULL, APR_HOOK_FIRST);
+}
+
+module AP_MODULE_DECLARE_DATA proxy_http_module = {
+    STANDARD20_MODULE_STUFF,
+    NULL,              /* create per-directory config structure */
+    NULL,              /* merge per-directory config structures */
+    NULL,              /* create per-server config structure */
+    NULL,              /* merge per-server config structures */
+    NULL,              /* command apr_table_t */
+    ap_proxy_http_register_hook/* register hooks */
+};
+
diff --git a/connectors/ajp/proxy/proxy_util.c b/connectors/ajp/proxy/proxy_util.c
new file mode 100644
index 0000000..e03637b
--- /dev/null
+++ b/connectors/ajp/proxy/proxy_util.c
@@ -0,0 +1,1877 @@
+/* Copyright 1999-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Utility routines for Apache proxy */
+#include "mod_proxy.h"
+#include "ap_mpm.h"
+#include "scoreboard.h"
+#include "apr_version.h"
+
+#if (APR_MAJOR_VERSION < 1)
+#undef apr_socket_create
+#define apr_socket_create apr_socket_create_ex
+#endif
+
+/* Global balancer counter */
+static int lb_workers = 0;
+static int lb_workers_limit = 0;
+
+static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r);
+static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r);
+static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r);
+static int proxy_match_word(struct dirconn_entry *This, request_rec *r);
+
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req, 
+                                   (request_rec *r, request_rec *pr), (r, pr),
+                                   OK, DECLINED)
+
+/* already called in the knowledge that the characters are hex digits */
+PROXY_DECLARE(int) ap_proxy_hex2c(const char *x)
+{
+    int i, ch;
+
+#if !APR_CHARSET_EBCDIC
+    ch = x[0];
+    if (apr_isdigit(ch))
+	i = ch - '0';
+    else if (apr_isupper(ch))
+	i = ch - ('A' - 10);
+    else
+	i = ch - ('a' - 10);
+    i <<= 4;
+
+    ch = x[1];
+    if (apr_isdigit(ch))
+	i += ch - '0';
+    else if (apr_isupper(ch))
+	i += ch - ('A' - 10);
+    else
+	i += ch - ('a' - 10);
+    return i;
+#else /*APR_CHARSET_EBCDIC*/
+    /* we assume that the hex value refers to an ASCII character
+     * so convert to EBCDIC so that it makes sense locally;
+     *
+     * example:
+     *
+     * client specifies %20 in URL to refer to a space char;
+     * at this point we're called with EBCDIC "20"; after turning
+     * EBCDIC "20" into binary 0x20, we then need to assume that 0x20
+     * represents an ASCII char and convert 0x20 to EBCDIC, yielding
+     * 0x40
+     */
+    char buf[1];
+
+    if (1 == sscanf(x, "%2x", &i)) {
+        buf[0] = i & 0xFF;
+        ap_xlate_proto_from_ascii(buf, 1);
+        return buf[0];
+    }
+    else {
+        return 0;
+    }
+#endif /*APR_CHARSET_EBCDIC*/
+}
+
+PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x)
+{
+#if !APR_CHARSET_EBCDIC
+    int i;
+
+    x[0] = '%';
+    i = (ch & 0xF0) >> 4;
+    if (i >= 10)
+	x[1] = ('A' - 10) + i;
+    else
+	x[1] = '0' + i;
+
+    i = ch & 0x0F;
+    if (i >= 10)
+	x[2] = ('A' - 10) + i;
+    else
+	x[2] = '0' + i;
+#else /*APR_CHARSET_EBCDIC*/
+    static const char ntoa[] = { "0123456789ABCDEF" };
+    char buf[1];
+
+    ch &= 0xFF;
+
+    buf[0] = ch;
+    ap_xlate_proto_to_ascii(buf, 1);
+
+    x[0] = '%';
+    x[1] = ntoa[(buf[0] >> 4) & 0x0F];
+    x[2] = ntoa[buf[0] & 0x0F];
+    x[3] = '\0';
+#endif /*APR_CHARSET_EBCDIC*/
+}
+
+/*
+ * canonicalise a URL-encoded string
+ */
+
+/*
+ * Convert a URL-encoded string to canonical form.
+ * It decodes characters which need not be encoded,
+ * and encodes those which must be encoded, and does not touch
+ * those which must not be touched.
+ */
+PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t,
+	int isenc)
+{
+    int i, j, ch;
+    char *y;
+    char *allowed;	/* characters which should not be encoded */
+    char *reserved;	/* characters which much not be en/de-coded */
+
+/* N.B. in addition to :@&=, this allows ';' in an http path
+ * and '?' in an ftp path -- this may be revised
+ * 
+ * Also, it makes a '+' character in a search string reserved, as
+ * it may be form-encoded. (Although RFC 1738 doesn't allow this -
+ * it only permits ; / ? : @ = & as reserved chars.)
+ */
+    if (t == enc_path)
+	allowed = "$-_.+!*'(),;:@&=";
+    else if (t == enc_search)
+	allowed = "$-_.!*'(),;:@&=";
+    else if (t == enc_user)
+	allowed = "$-_.+!*'(),;@&=";
+    else if (t == enc_fpath)
+	allowed = "$-_.+!*'(),?:@&=";
+    else			/* if (t == enc_parm) */
+	allowed = "$-_.+!*'(),?/:@&=";
+
+    if (t == enc_path)
+	reserved = "/";
+    else if (t == enc_search)
+	reserved = "+";
+    else
+	reserved = "";
+
+    y = apr_palloc(p, 3 * len + 1);
+
+    for (i = 0, j = 0; i < len; i++, j++) {
+/* always handle '/' first */
+	ch = x[i];
+	if (strchr(reserved, ch)) {
+	    y[j] = ch;
+	    continue;
+	}
+/* decode it if not already done */
+	if (isenc && ch == '%') {
+	    if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2]))
+		return NULL;
+	    ch = ap_proxy_hex2c(&x[i + 1]);
+	    i += 2;
+	    if (ch != 0 && strchr(reserved, ch)) {	/* keep it encoded */
+		ap_proxy_c2hex(ch, &y[j]);
+		j += 2;
+		continue;
+	    }
+	}
+/* recode it, if necessary */
+	if (!apr_isalnum(ch) && !strchr(allowed, ch)) {
+	    ap_proxy_c2hex(ch, &y[j]);
+	    j += 2;
+	}
+	else
+	    y[j] = ch;
+    }
+    y[j] = '\0';
+    return y;
+}
+
+/*
+ * Parses network-location.
+ *    urlp           on input the URL; on output the path, after the leading /
+ *    user           NULL if no user/password permitted
+ *    password       holder for password
+ *    host           holder for host
+ *    port           port number; only set if one is supplied.
+ *
+ * Returns an error string.
+ */
+PROXY_DECLARE(char *)
+     ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp,
+			char **passwordp, char **hostp, apr_port_t *port)
+{
+    char *addr, *scope_id, *strp, *host, *url = *urlp;
+    char *user = NULL, *password = NULL;
+    apr_port_t tmp_port;
+    apr_status_t rv;
+
+    if (url[0] != '/' || url[1] != '/')
+	return "Malformed URL";
+    host = url + 2;
+    url = strchr(host, '/');
+    if (url == NULL)
+	url = "";
+    else
+	*(url++) = '\0';	/* skip seperating '/' */
+
+    /* find _last_ '@' since it might occur in user/password part */
+    strp = strrchr(host, '@');
+
+    if (strp != NULL) {
+	*strp = '\0';
+	user = host;
+	host = strp + 1;
+
+/* find password */
+	strp = strchr(user, ':');
+	if (strp != NULL) {
+	    *strp = '\0';
+	    password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1);
+	    if (password == NULL)
+		return "Bad %-escape in URL (password)";
+	}
+
+	user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1);
+	if (user == NULL)
+	    return "Bad %-escape in URL (username)";
+    }
+    if (userp != NULL) {
+	*userp = user;
+    }
+    if (passwordp != NULL) {
+	*passwordp = password;
+    }
+
+    /* Parse the host string to separate host portion from optional port.
+     * Perform range checking on port.
+     */
+    rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p);
+    if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) {
+        return "Invalid host/port";
+    }
+    if (tmp_port != 0) { /* only update caller's port if port was specified */
+        *port = tmp_port;
+    }
+
+    ap_str_tolower(addr); /* DNS names are case-insensitive */
+
+    *urlp = url;
+    *hostp = addr;
+
+    return NULL;
+}
+
+static const char * const lwday[7] =
+{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
+
+/*
+ * If the date is a valid RFC 850 date or asctime() date, then it
+ * is converted to the RFC 1123 format, otherwise it is not modified.
+ * This routine is not very fast at doing conversions, as it uses
+ * sscanf and sprintf. However, if the date is already correctly
+ * formatted, then it exits very quickly.
+ */
+PROXY_DECLARE(const char *)
+     ap_proxy_date_canon(apr_pool_t *p, const char *x1)
+{
+    char *x = apr_pstrdup(p, x1);
+    int wk, mday, year, hour, min, sec, mon;
+    char *q, month[4], zone[4], week[4];
+
+    q = strchr(x, ',');
+    /* check for RFC 850 date */
+    if (q != NULL && q - x > 3 && q[1] == ' ') {
+	*q = '\0';
+	for (wk = 0; wk < 7; wk++)
+	    if (strcmp(x, lwday[wk]) == 0)
+		break;
+	*q = ',';
+	if (wk == 7)
+	    return x;		/* not a valid date */
+	if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' ||
+	    q[17] != ':' || strcmp(&q[20], " GMT") != 0)
+	    return x;
+	if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year,
+		   &hour, &min, &sec, zone) != 7)
+	    return x;
+	if (year < 70)
+	    year += 2000;
+	else
+	    year += 1900;
+    }
+    else {
+/* check for acstime() date */
+	if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' ||
+	    x[16] != ':' || x[19] != ' ' || x[24] != '\0')
+	    return x;
+	if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour,
+		   &min, &sec, &year) != 7)
+	    return x;
+	for (wk = 0; wk < 7; wk++)
+	    if (strcmp(week, apr_day_snames[wk]) == 0)
+		break;
+	if (wk == 7)
+	    return x;
+    }
+
+/* check date */
+    for (mon = 0; mon < 12; mon++)
+	if (strcmp(month, apr_month_snames[mon]) == 0)
+	    break;
+    if (mon == 12)
+	return x;
+
+    q = apr_palloc(p, 30);
+    apr_snprintf(q, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", apr_day_snames[wk],
+       mday, apr_month_snames[mon], year, hour, min, sec);
+    return q;
+}
+
+PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r)
+{
+    request_rec *rp = apr_pcalloc(c->pool, sizeof(*r));
+
+    rp->pool            = c->pool;
+    rp->status          = HTTP_OK;
+
+    rp->headers_in      = apr_table_make(c->pool, 50);
+    rp->subprocess_env  = apr_table_make(c->pool, 50);
+    rp->headers_out     = apr_table_make(c->pool, 12);
+    rp->err_headers_out = apr_table_make(c->pool, 5);
+    rp->notes           = apr_table_make(c->pool, 5);
+
+    rp->server = r->server;
+    rp->proxyreq = r->proxyreq;
+    rp->request_time = r->request_time;
+    rp->connection      = c;
+    rp->output_filters  = c->output_filters;
+    rp->input_filters   = c->input_filters;
+    rp->proto_output_filters  = c->output_filters;
+    rp->proto_input_filters   = c->input_filters;
+
+    rp->request_config  = ap_create_request_config(c->pool);
+    proxy_run_create_req(r, rp);
+
+    return rp;
+}
+
+
+/*
+ * list is a comma-separated list of case-insensitive tokens, with
+ * optional whitespace around the tokens.
+ * The return returns 1 if the token val is found in the list, or 0
+ * otherwise.
+ */
+PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val)
+{
+    int len, i;
+    const char *p;
+
+    len = strlen(val);
+
+    while (list != NULL) {
+	p = ap_strchr_c(list, ',');
+	if (p != NULL) {
+	    i = p - list;
+	    do
+		p++;
+	    while (apr_isspace(*p));
+	}
+	else
+	    i = strlen(list);
+
+	while (i > 0 && apr_isspace(list[i - 1]))
+	    i--;
+	if (i == len && strncasecmp(list, val, len) == 0)
+	    return 1;
+	list = p;
+    }
+    return 0;
+}
+
+/*
+ * list is a comma-separated list of case-insensitive tokens, with
+ * optional whitespace around the tokens.
+ * if val appears on the list of tokens, it is removed from the list,
+ * and the new list is returned.
+ */
+PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val)
+{
+    int len, i;
+    const char *p;
+    char *new = NULL;
+
+    len = strlen(val);
+
+    while (list != NULL) {
+	p = ap_strchr_c(list, ',');
+	if (p != NULL) {
+	    i = p - list;
+	    do
+		p++;
+	    while (apr_isspace(*p));
+	}
+	else
+	    i = strlen(list);
+
+	while (i > 0 && apr_isspace(list[i - 1]))
+	    i--;
+	if (i == len && strncasecmp(list, val, len) == 0) {
+	    /* do nothing */
+	}
+	else {
+	    if (new)
+		new = apr_pstrcat(pool, new, ",", apr_pstrndup(pool, list, i), NULL);
+	    else
+		new = apr_pstrndup(pool, list, i);
+	}
+	list = p;
+    }
+    return new;
+}
+
+/*
+ * Converts 8 hex digits to a time integer
+ */
+PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x)
+{
+    int i, ch;
+    unsigned int j;
+
+    for (i = 0, j = 0; i < 8; i++) {
+	ch = x[i];
+	j <<= 4;
+	if (apr_isdigit(ch))
+	    j |= ch - '0';
+	else if (apr_isupper(ch))
+	    j |= ch - ('A' - 10);
+	else
+	    j |= ch - ('a' - 10);
+    }
+    if (j == 0xffffffff)
+	return -1;		/* so that it works with 8-byte ints */
+    else
+	return j;
+}
+
+/*
+ * Converts a time integer to 8 hex digits
+ */
+PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y)
+{
+    int i, ch;
+    unsigned int j = t;
+
+    for (i = 7; i >= 0; i--) {
+	ch = j & 0xF;
+	j >>= 4;
+	if (ch >= 10)
+	    y[i] = ch + ('A' - 10);
+	else
+	    y[i] = ch + '0';
+    }
+    y[8] = '\0';
+}
+
+PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message)
+{
+    apr_table_setn(r->notes, "error-notes",
+	apr_pstrcat(r->pool, 
+		"The proxy server could not handle the request "
+		"<em><a href=\"", ap_escape_uri(r->pool, r->uri),
+		"\">", ap_escape_html(r->pool, r->method),
+		"&nbsp;", 
+		ap_escape_html(r->pool, r->uri), "</a></em>.<p>\n"
+		"Reason: <strong>",
+		ap_escape_html(r->pool, message), 
+		"</strong></p>", NULL));
+
+    /* Allow "error-notes" string to be printed by ap_send_error_response() */
+    apr_table_setn(r->notes, "verbose-error-to", apr_pstrdup(r->pool, "*"));
+
+    r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode);
+    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+			 "proxy: %s returned by %s", message, r->uri);
+    return statuscode;
+}
+
+static const char *
+     proxy_get_host_of_request(request_rec *r)
+{
+    char *url, *user = NULL, *password = NULL, *err, *host;
+    apr_port_t port;
+
+    if (r->hostname != NULL)
+	return r->hostname;
+
+    /* Set url to the first char after "scheme://" */
+    if ((url = strchr(r->uri, ':')) == NULL
+	|| url[1] != '/' || url[2] != '/')
+	return NULL;
+
+    url = apr_pstrdup(r->pool, &url[1]);	/* make it point to "//", which is what proxy_canon_netloc expects */
+
+    err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port);
+
+    if (err != NULL)
+	ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+		     "%s", err);
+
+    r->hostname = host;
+
+    return host;		/* ought to return the port, too */
+}
+
+/* Return TRUE if addr represents an IP address (or an IP network address) */
+PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p)
+{
+    const char *addr = This->name;
+    long ip_addr[4];
+    int i, quads;
+    long bits;
+
+    /* if the address is given with an explicit netmask, use that */
+    /* Due to a deficiency in apr_inet_addr(), it is impossible to parse */
+    /* "partial" addresses (with less than 4 quads) correctly, i.e.  */
+    /* 192.168.123 is parsed as 192.168.0.123, which is not what I want. */
+    /* I therefore have to parse the IP address manually: */
+    /*if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0) */
+    /* addr and mask were set by proxy_readmask() */
+    /*return 1; */
+
+    /* Parse IP addr manually, optionally allowing */
+    /* abbreviated net addresses like 192.168. */
+
+    /* Iterate over up to 4 (dotted) quads. */
+    for (quads = 0; quads < 4 && *addr != '\0'; ++quads) {
+	char *tmp;
+
+	if (*addr == '/' && quads > 0)	/* netmask starts here. */
+	    break;
+
+	if (!apr_isdigit(*addr))
+	    return 0;		/* no digit at start of quad */
+
+	ip_addr[quads] = strtol(addr, &tmp, 0);
+
+	if (tmp == addr)	/* expected a digit, found something else */
+	    return 0;
+
+	if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {
+	    /* invalid octet */
+	    return 0;
+	}
+
+	addr = tmp;
+
+	if (*addr == '.' && quads != 3)
+	    ++addr;		/* after the 4th quad, a dot would be illegal */
+    }
+
+    for (This->addr.s_addr = 0, i = 0; i < quads; ++i)
+	This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
+
+    if (addr[0] == '/' && apr_isdigit(addr[1])) {	/* net mask follows: */
+	char *tmp;
+
+	++addr;
+
+	bits = strtol(addr, &tmp, 0);
+
+	if (tmp == addr)	/* expected a digit, found something else */
+	    return 0;
+
+	addr = tmp;
+
+	if (bits < 0 || bits > 32)	/* netmask must be between 0 and 32 */
+	    return 0;
+
+    }
+    else {
+	/* Determine (i.e., "guess") netmask by counting the */
+	/* number of trailing .0's; reduce #quads appropriately */
+	/* (so that 192.168.0.0 is equivalent to 192.168.)        */
+	while (quads > 0 && ip_addr[quads - 1] == 0)
+	    --quads;
+
+	/* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */
+	if (quads < 1)
+	    return 0;
+
+	/* every zero-byte counts as 8 zero-bits */
+	bits = 8 * quads;
+
+	if (bits != 32)		/* no warning for fully qualified IP address */
+            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+	      "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld\n",
+		 inet_ntoa(This->addr), bits);
+    }
+
+    This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits));
+
+    if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) {
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+	    "Warning: NetMask and IP-Addr disagree in %s/%ld\n",
+		inet_ntoa(This->addr), bits);
+	This->addr.s_addr &= This->mask.s_addr;
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+	    "         Set to %s/%ld\n",
+		inet_ntoa(This->addr), bits);
+    }
+
+    if (*addr == '\0') {
+	This->matcher = proxy_match_ipaddr;
+	return 1;
+    }
+    else
+	return (*addr == '\0');	/* okay iff we've parsed the whole string */
+}
+
+/* Return TRUE if addr represents an IP address (or an IP network address) */
+static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r)
+{
+    int i, ip_addr[4];
+    struct in_addr addr, *ip;
+    const char *host = proxy_get_host_of_request(r);
+
+    if (host == NULL)   /* oops! */
+       return 0;
+
+    memset(&addr, '\0', sizeof addr);
+    memset(ip_addr, '\0', sizeof ip_addr);
+
+    if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) {
+	for (addr.s_addr = 0, i = 0; i < 4; ++i)
+	    addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
+
+	if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) {
+#if DEBUGGING
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                         "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                         "%s/", inet_ntoa(This->addr));
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                         "%s", inet_ntoa(This->mask));
+#endif
+	    return 1;
+	}
+#if DEBUGGING
+	else {
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                         "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                         "%s/", inet_ntoa(This->addr));
+        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                         "%s", inet_ntoa(This->mask));
+	}
+#endif
+    }
+    else {
+	struct apr_sockaddr_t *reqaddr;
+
+        if (apr_sockaddr_info_get(&reqaddr, host, APR_UNSPEC, 0, 0, r->pool)
+	    != APR_SUCCESS) {
+#if DEBUGGING
+	    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+			 "2)IP-NoMatch: hostname=%s msg=Host not found", 
+			 host);
+#endif
+	    return 0;
+	}
+
+	/* Try to deal with multiple IP addr's for a host */
+	/* FIXME: This needs to be able to deal with IPv6 */
+	while (reqaddr) {
+	    ip = (struct in_addr *) reqaddr->ipaddr_ptr;
+	    if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) {
+#if DEBUGGING
+		ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+			     "3)IP-Match: %s[%s] <-> ", host, 
+			     inet_ntoa(*ip));
+		ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+			     "%s/", inet_ntoa(This->addr));
+		ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+			     "%s", inet_ntoa(This->mask));
+#endif
+		return 1;
+	    }
+#if DEBUGGING
+	    else {
+                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+			     "3)IP-NoMatch: %s[%s] <-> ", host, 
+			     inet_ntoa(*ip));
+                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+			     "%s/", inet_ntoa(This->addr));
+                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+			     "%s", inet_ntoa(This->mask));
+	    }
+#endif
+	    reqaddr = reqaddr->next;
+	}
+    }
+
+    return 0;
+}
+
+/* Return TRUE if addr represents a domain name */
+PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p)
+{
+    char *addr = This->name;
+    int i;
+
+    /* Domain name must start with a '.' */
+    if (addr[0] != '.')
+        return 0;
+
+    /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
+    for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i)
+        continue;
+
+#if 0
+    if (addr[i] == ':') {
+    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+                     "@@@@ handle optional port in proxy_is_domainname()");
+	/* @@@@ handle optional port */
+    }
+#endif
+
+    if (addr[i] != '\0')
+        return 0;
+
+    /* Strip trailing dots */
+    for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i)
+        addr[i] = '\0';
+
+    This->matcher = proxy_match_domainname;
+    return 1;
+}
+
+/* Return TRUE if host "host" is in domain "domain" */
+static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r)
+{
+    const char *host = proxy_get_host_of_request(r);
+    int d_len = strlen(This->name), h_len;
+
+    if (host == NULL)		/* some error was logged already */
+        return 0;
+
+    h_len = strlen(host);
+
+    /* @@@ do this within the setup? */
+    /* Ignore trailing dots in domain comparison: */
+    while (d_len > 0 && This->name[d_len - 1] == '.')
+        --d_len;
+    while (h_len > 0 && host[h_len - 1] == '.')
+        --h_len;
+    return h_len > d_len
+        && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;
+}
+
+/* Return TRUE if host represents a host name */
+PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p)
+{
+    struct apr_sockaddr_t *addr;
+    char *host = This->name;
+    int i;
+
+    /* Host names must not start with a '.' */
+    if (host[0] == '.')
+        return 0;
+
+    /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
+    for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i);
+
+    if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS)
+        return 0;
+    
+    This->hostaddr = addr;
+
+    /* Strip trailing dots */
+    for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i)
+        host[i] = '\0';
+
+    This->matcher = proxy_match_hostname;
+    return 1;
+}
+
+/* Return TRUE if host "host" is equal to host2 "host2" */
+static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r)
+{
+    char *host = This->name;
+    const char *host2 = proxy_get_host_of_request(r);
+    int h2_len;
+    int h1_len;
+
+    if (host == NULL || host2 == NULL)
+        return 0; /* oops! */
+
+    h2_len = strlen(host2);
+    h1_len = strlen(host);
+
+#if 0
+    struct apr_sockaddr_t *addr = *This->hostaddr;
+
+    /* Try to deal with multiple IP addr's for a host */
+    while (addr) {
+        if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?)
+            return 1;
+        addr = addr->next;
+    }
+#endif
+
+    /* Ignore trailing dots in host2 comparison: */
+    while (h2_len > 0 && host2[h2_len - 1] == '.')
+        --h2_len;
+    while (h1_len > 0 && host[h1_len - 1] == '.')
+        --h1_len;
+    return h1_len == h2_len
+        && strncasecmp(host, host2, h1_len) == 0;
+}
+
+/* Return TRUE if addr is to be matched as a word */
+PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p)
+{
+    This->matcher = proxy_match_word;
+    return 1;
+}
+
+/* Return TRUE if string "str2" occurs literally in "str1" */
+static int proxy_match_word(struct dirconn_entry *This, request_rec *r)
+{
+    const char *host = proxy_get_host_of_request(r);
+    return host != NULL && ap_strstr_c(host, This->name) != NULL;
+}
+
+/* checks whether a host in uri_addr matches proxyblock */
+PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, 
+                             apr_sockaddr_t *uri_addr)
+{
+    int j;
+    apr_sockaddr_t * src_uri_addr = uri_addr;
+    /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */
+    for (j = 0; j < conf->noproxies->nelts; j++) {
+        struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
+        struct apr_sockaddr_t *conf_addr = npent[j].addr;
+        uri_addr = src_uri_addr;
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                     "proxy: checking remote machine [%s] against [%s]", uri_addr->hostname, npent[j].name);
+        if ((npent[j].name && ap_strstr_c(uri_addr->hostname, npent[j].name))
+            || npent[j].name[0] == '*') {
+            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+                         "proxy: connect to remote machine %s blocked: name %s matched", uri_addr->hostname, npent[j].name);
+            return HTTP_FORBIDDEN;
+        }
+        while (conf_addr) {
+            while (uri_addr) {
+                char *conf_ip;
+                char *uri_ip;
+                apr_sockaddr_ip_get(&conf_ip, conf_addr);
+                apr_sockaddr_ip_get(&uri_ip, uri_addr);
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                             "proxy: ProxyBlock comparing %s and %s", conf_ip, uri_ip);
+                if (!apr_strnatcasecmp(conf_ip, uri_ip)) {
+                    ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+                                 "proxy: connect to remote machine %s blocked: IP %s matched", uri_addr->hostname, conf_ip);
+                    return HTTP_FORBIDDEN;
+                }
+                uri_addr = uri_addr->next;
+            }
+            conf_addr = conf_addr->next;
+        }
+    }
+    return OK;
+}
+
+/* set up the minimal filter set */
+PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r)
+{
+    ap_add_input_filter("HTTP_IN", NULL, r, c);
+    return OK;
+}
+
+/* converts a series of buckets into a string 
+ * XXX: BillS says this function performs essentially the same function as 
+ * ap_rgetline() in protocol.c. Deprecate this function and use ap_rgetline() 
+ * instead? I think ap_proxy_string_read() will not work properly on non ASCII
+ * (EBCDIC) machines either.
+ */
+PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb,
+                                                 char *buff, apr_size_t bufflen, int *eos)
+{
+    apr_bucket *e;
+    apr_status_t rv;
+    char *pos = buff;
+    char *response;
+    int found = 0;
+    apr_size_t len;
+
+    /* start with an empty string */
+    buff[0] = 0;
+    *eos = 0;
+
+    /* loop through each brigade */
+    while (!found) {
+        /* get brigade from network one line at a time */
+        if (APR_SUCCESS != (rv = ap_get_brigade(c->input_filters, bb, 
+                                                AP_MODE_GETLINE,
+                                                APR_BLOCK_READ,
+                                                0))) {
+            return rv;
+        }
+        /* loop through each bucket */
+        while (!found) {
+            if (*eos || APR_BRIGADE_EMPTY(bb)) {
+                /* The connection aborted or timed out */
+                return APR_ECONNABORTED;
+            }
+            e = APR_BRIGADE_FIRST(bb);
+            if (APR_BUCKET_IS_EOS(e)) {
+                *eos = 1;
+            }
+            else {
+                if (APR_SUCCESS != apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ)) {
+                    return rv;
+                }
+                /* is string LF terminated? 
+                 * XXX: This check can be made more efficient by simply checking 
+                 * if the last character in the 'response' buffer is an ASCII_LF.
+                 * See ap_rgetline() for an example.
+                 */
+                if (memchr(response, APR_ASCII_LF, len)) {
+                    found = 1;
+                }
+                /* concat strings until buff is full - then throw the data away */
+                if (len > ((bufflen-1)-(pos-buff))) {
+                    len = (bufflen-1)-(pos-buff);
+                }
+                if (len > 0) {
+                    pos = apr_cpystrn(pos, response, len);
+                }
+            }
+            APR_BUCKET_REMOVE(e);
+            apr_bucket_destroy(e);
+        }
+    }
+
+    return APR_SUCCESS;
+}
+
+/* unmerge an element in the table */
+PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key)
+{
+    apr_off_t offset = 0;
+    apr_off_t count = 0;
+    char *value = NULL;
+
+    /* get the value to unmerge */
+    const char *initial = apr_table_get(t, key);
+    if (!initial) {
+        return;
+    }
+    value = apr_pstrdup(p, initial);
+
+    /* remove the value from the headers */
+    apr_table_unset(t, key);
+
+    /* find each comma */
+    while (value[count]) {
+        if (value[count] == ',') {
+            value[count] = 0;
+            apr_table_add(t, key, value + offset);
+            offset = count + 1;
+        }
+        count++;
+    }
+    apr_table_add(t, key, value + offset);
+}
+
+PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p,
+                                                      proxy_server_conf *conf,
+                                                      const char *url)
+{
+    proxy_balancer *balancer;
+    char *c, *uri = apr_pstrdup(p, url);
+    int i;
+    
+    c = strchr(uri, ':');   
+    if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
+       return NULL;
+    /* remove path from uri */
+    if ((c = strchr(c + 3, '/')))
+        *c = '\0';
+    balancer = (proxy_balancer *)conf->balancers->elts;
+    for (i = 0; i < conf->balancers->nelts; i++) {
+        if (strcasecmp(balancer->name, uri) == 0)
+            return balancer;
+        balancer++;
+    }
+    return NULL;
+}
+
+PROXY_DECLARE(const char *) ap_proxy_add_balancer(proxy_balancer **balancer,
+                                                  apr_pool_t *p,
+                                                  proxy_server_conf *conf,
+                                                  const char *url)
+{
+    char *c, *q, *uri = apr_pstrdup(p, url);
+    apr_status_t rc = 0;
+
+    c = strchr(uri, ':');   
+    if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
+       return "Bad syntax for a balancer name";
+    /* remove path from uri */
+    if ((q = strchr(c + 3, '/')))
+        *q = '\0';
+
+    ap_str_tolower(uri);
+    *balancer = apr_array_push(conf->balancers);
+    (*balancer)->name = uri;
+    (*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_runtime_worker));
+    /* XXX Is this a right place to create mutex */
+#if APR_HAS_THREADS
+    if ((rc = apr_thread_mutex_create(&((*balancer)->mutex),
+                APR_THREAD_MUTEX_DEFAULT, p)) != APR_SUCCESS) {
+            /* XXX: Do we need to log something here */
+            return "can not create thread mutex";
+    }
+#endif
+
+    return NULL;
+}
+
+PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,
+                                                  proxy_server_conf *conf,
+                                                  const char *url)
+{
+    proxy_worker *worker;
+    char *c, *uri = apr_pstrdup(p, url);
+    int i;
+    
+    c = strchr(uri, ':');   
+    if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
+       return NULL;
+    /* remove path from uri */
+    if ((c = strchr(c + 3, '/')))
+        *c = '\0';
+
+    worker = (proxy_worker *)conf->workers->elts;
+    for (i = 0; i < conf->workers->nelts; i++) {
+        if (strcasecmp(worker->name, uri) == 0) {
+            return worker;
+        }
+        worker++;
+    }
+    return NULL;
+}
+
+static apr_status_t conn_pool_cleanup(void *thepool)
+{
+    proxy_conn_pool *cp = (proxy_conn_pool *)thepool;
+    /* Close the socket */
+    cp->addr = NULL;
+    return APR_SUCCESS;
+}
+
+static void init_conn_pool(apr_pool_t *p, proxy_worker *worker)
+{
+    apr_pool_t *pool;
+    proxy_conn_pool *cp;
+    
+    /* Create a connection pool's subpool. 
+     * This pool is used for connection recycling.
+     * Once the worker is added it is never removed but
+     * it can be disabled.
+     */
+    apr_pool_create(&pool, p);
+    /* Alloc from the same pool as worker.
+     * proxy_conn_pool is permanently attached to the worker. 
+     */
+    cp = (proxy_conn_pool *)apr_pcalloc(p, sizeof(proxy_conn_pool));
+    cp->pool = pool;    
+    worker->cp = cp;
+    apr_pool_cleanup_register(p, (void *)cp,
+                              conn_pool_cleanup,
+                              apr_pool_cleanup_null);      
+
+}
+
+PROXY_DECLARE(const char *) ap_proxy_add_worker(proxy_worker **worker,
+                                                apr_pool_t *p,
+                                                proxy_server_conf *conf,
+                                                const char *url)
+{
+    char *c, *q, *uri = apr_pstrdup(p, url);
+    int port;
+    
+    c = strchr(uri, ':');   
+    if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
+       return "Bad syntax for a remote proxy server";
+    /* remove path from uri */
+    if ((q = strchr(c + 3, '/')))
+        *q = '\0';
+
+    q = strchr(c + 3, ':');
+    if (q != NULL) {
+        if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) {
+            return "Bad syntax for a remote proxy server (bad port number)";
+        }
+    }
+    else
+        port = -1;
+    ap_str_tolower(uri);
+    *worker = apr_array_push(conf->workers);
+    memset(*worker, 0, sizeof(proxy_worker));
+    (*worker)->name = apr_pstrdup(p, uri);
+    *c = '\0';
+    (*worker)->scheme = uri;
+    (*worker)->hostname = c + 3;
+
+    if (port == -1)
+        port = apr_uri_port_of_scheme((*worker)->scheme);
+    (*worker)->port = port;
+
+    init_conn_pool(p, *worker);
+
+    return NULL;
+}
+
+PROXY_DECLARE(void) 
+ap_proxy_add_worker_to_balancer(apr_pool_t *pool, proxy_balancer *balancer, proxy_worker *worker)
+{
+    int i;
+    double median, ffactor = 0.0;
+    proxy_runtime_worker *runtime, *workers;    
+#if PROXY_HAS_SCOREBOARD
+    lb_score *score;
+#else
+    void *score;
+#endif
+
+#if PROXY_HAS_SCOREBOARD
+    int mpm_daemons;
+
+    ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &mpm_daemons);
+    /* Check if we are prefork or single child */
+    if (worker->hmax && mpm_daemons > 1) {
+        /* Check only if workers_limit is set */
+        if (lb_workers_limit && (lb_workers + 1) > lb_workers_limit) {
+            ap_log_perror(APLOG_MARK, APLOG_ERR, 0, pool,
+                          "proxy: Can not add worker (%s) to balancer (%s)."
+                          " Dynamic limit reached.",
+                          worker->name, balancer->name);
+            return;
+        }
+        score = ap_get_scoreboard_lb(getpid(), lb_workers);
+    }
+    else
+#endif
+    {
+        /* Use the plain memory */
+        score = apr_pcalloc(pool, sizeof(proxy_runtime_stat));
+    }
+    if (!score)
+        return;
+    runtime = apr_array_push(balancer->workers);
+    runtime->w = worker;
+    runtime->s = (proxy_runtime_stat *)score;
+    runtime->s->id = lb_workers;
+    /* TODO: deal with the dynamic overflow */
+    ++lb_workers;
+
+    /* Recalculate lbfactors */
+    workers = (proxy_runtime_worker *)balancer->workers->elts;
+
+    for (i = 0; i < balancer->workers->nelts; i++) {
+        /* Set to the original configuration */
+        workers[i].s->lbfactor = workers[i].w->lbfactor;
+        ffactor += workers[i].s->lbfactor;
+    }
+    if (ffactor < 100.0) {
+        int z = 0;
+        for (i = 0; i < balancer->workers->nelts; i++) {
+            if (workers[i].s->lbfactor == 0.0) 
+                ++z;
+        }
+        if (z) {
+            median = (100.0 - ffactor) / z;
+            for (i = 0; i < balancer->workers->nelts; i++) {
+                if (workers[i].s->lbfactor == 0.0) 
+                    workers[i].s->lbfactor = median;
+            }
+        }
+        else {
+            median = (100.0 - ffactor) / balancer->workers->nelts;
+            for (i = 0; i < balancer->workers->nelts; i++)
+                workers[i].s->lbfactor += median;
+        }
+    }
+    else if (ffactor > 100.0) {
+        median = (ffactor - 100.0) / balancer->workers->nelts;
+        for (i = 0; i < balancer->workers->nelts; i++) {
+            if (workers[i].s->lbfactor > median)
+                workers[i].s->lbfactor -= median;
+        }
+    } 
+    for (i = 0; i < balancer->workers->nelts; i++) {
+        /* Update the status entires */
+        workers[i].s->lbstatus = workers[i].s->lbfactor;
+    }
+}
+
+PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,
+                                        proxy_balancer **balancer,
+                                        request_rec *r,
+                                        proxy_server_conf *conf, char **url)
+{
+    int access_status;
+
+    access_status = proxy_run_pre_request(worker, balancer, r, conf, url);
+    if (access_status == DECLINED && *balancer == NULL) {
+        *worker = ap_proxy_get_worker(r->pool, conf, *url);
+        if (*worker) {
+            *balancer = NULL;
+            access_status = OK;
+        }
+        else
+            access_status = DECLINED;
+    }
+    else if (access_status == DECLINED && balancer != NULL) {
+        /* All the workers are busy */
+        access_status = HTTP_SERVICE_UNAVAILABLE;
+    }
+    return access_status;
+}
+
+PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker,
+                                         proxy_balancer *balancer,
+                                         request_rec *r,
+                                         proxy_server_conf *conf)
+{
+    int access_status;
+    if (balancer)
+        access_status = proxy_run_post_request(worker, balancer, r, conf);
+    else { 
+        
+
+        access_status = OK;
+    }
+
+    return access_status;
+}
+
+/* DEPRECATED */
+PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock,
+                                               const char *proxy_function,
+                                               apr_sockaddr_t *backend_addr,
+                                               const char *backend_name,
+                                               proxy_server_conf *conf,
+                                               server_rec *s,
+                                               apr_pool_t *p)
+{
+    apr_status_t rv;
+    int connected = 0;
+    int loglevel;
+    
+    while (backend_addr && !connected) {
+        if ((rv = apr_socket_create(newsock, backend_addr->family,
+                                    SOCK_STREAM, 0, p)) != APR_SUCCESS) {
+            loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
+            ap_log_error(APLOG_MARK, loglevel, rv, s,
+                         "proxy: %s: error creating fam %d socket for target %s",
+                         proxy_function,
+                         backend_addr->family,
+                         backend_name);
+            /* this could be an IPv6 address from the DNS but the
+             * local machine won't give us an IPv6 socket; hopefully the
+             * DNS returned an additional address to try
+             */
+            backend_addr = backend_addr->next;
+            continue;
+        }
+
+#if !defined(TPF) && !defined(BEOS)
+        if (conf->recv_buffer_size > 0 &&
+            (rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF,
+                                     conf->recv_buffer_size))) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                         "apr_socket_opt_set(SO_RCVBUF): Failed to set "
+                         "ProxyReceiveBufferSize, using default");
+        }
+#endif
+
+        /* Set a timeout on the socket */
+        if (conf->timeout_set == 1) {
+            apr_socket_timeout_set(*newsock, conf->timeout);
+        }
+        else {
+             apr_socket_timeout_set(*newsock, s->timeout);
+        }
+
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                     "proxy: %s: fam %d socket created to connect to %s",
+                     proxy_function, backend_addr->family, backend_name);
+
+        /* make the connection out of the socket */
+        rv = apr_socket_connect(*newsock, backend_addr);
+
+        /* if an error occurred, loop round and try again */
+        if (rv != APR_SUCCESS) {
+            apr_socket_close(*newsock);
+            loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
+            ap_log_error(APLOG_MARK, loglevel, rv, s,
+                         "proxy: %s: attempt to connect to %pI (%s) failed",
+                         proxy_function,
+                         backend_addr,
+                         backend_name);
+            backend_addr = backend_addr->next;
+            continue;
+        }
+        connected = 1;
+    }
+    return connected ? 0 : 1;
+}
+
+static apr_status_t proxy_conn_cleanup(void *theconn)
+{
+    proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
+    /* Close the socket */
+    if (conn->sock)
+        apr_socket_close(conn->sock);
+    conn->sock = NULL;
+    conn->pool = NULL;
+    return APR_SUCCESS;
+}
+
+static apr_status_t connection_cleanup(void *theconn)
+{
+    proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
+    proxy_worker *worker = conn->worker;
+    
+    /* deterimine if the connection need to be closed */
+    if (conn->close_on_recycle) {
+        if (conn->sock)
+            apr_socket_close(conn->sock);
+        conn->sock = NULL;
+    }
+#if APR_HAS_THREADS
+    if (worker->hmax && worker->cp->res) {
+        apr_reslist_release(worker->cp->res, (void *)conn);
+    }
+    else
+#endif
+    {
+        worker->cp->conn = conn;
+    }
+
+    /* Allways return the SUCCESS */
+    return APR_SUCCESS;
+}
+
+/* reslist constructor */
+static apr_status_t connection_constructor(void **resource, void *params,
+                                           apr_pool_t *pool)
+{
+    apr_pool_t *ctx;
+    proxy_conn_rec *conn;
+    server_rec *s = (server_rec *)params;
+    
+    /* Create the subpool for each connection
+     * This keeps the memory consumption constant
+     * when disconnecting from backend.
+     */
+    apr_pool_create(&ctx, pool);
+    conn = apr_pcalloc(ctx, sizeof(proxy_conn_rec));
+
+    conn->pool = ctx;
+    *resource = conn;
+    /* register the pool cleanup */
+    apr_pool_cleanup_register(ctx, (void *)conn,
+                              proxy_conn_cleanup,
+                              apr_pool_cleanup_null);      
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                 "proxy: socket is constructed");
+
+    return APR_SUCCESS;
+}
+
+/* reslist destructor */
+static apr_status_t connection_destructor(void *resource, void *params,
+                                          apr_pool_t *pool)
+{
+    proxy_conn_rec *conn = (proxy_conn_rec *)resource;
+    server_rec *s = (server_rec *)params;
+    
+#if 0
+    if (conn->sock)
+        apr_socket_close(conn->sock);
+    conn->sock = NULL;
+    apr_pool_cleanup_kill(conn->pool, conn, proxy_conn_cleanup);
+#endif
+    if (conn->pool)
+        apr_pool_destroy(conn->pool);
+    conn->pool = NULL;
+#if 0
+    if (s != NULL)
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                     "proxy: socket is destructed");
+#endif
+    return APR_SUCCESS;
+}
+
+/* Close the connection 
+ * The proxy_conn_rec from now on can not be used
+ */
+PROXY_DECLARE(apr_status_t) ap_proxy_close_connection(proxy_conn_rec *conn)
+{
+
+    if (conn->worker && conn->worker->cp)
+        conn->worker->cp->conn = NULL;
+    return connection_destructor(conn, NULL, NULL);
+}
+
+static apr_status_t init_conn_worker(proxy_worker *worker, server_rec *s)
+{
+    apr_status_t rv;
+
+#if APR_HAS_THREADS
+    int mpm_threads;
+
+    ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
+    if (mpm_threads > 1) {
+        /* Set hard max to no more then mpm_threads */
+        if (worker->hmax == 0 || worker->hmax > mpm_threads)
+            worker->hmax = mpm_threads;
+        if (worker->smax == 0 || worker->smax > worker->hmax)
+            worker->smax = worker->hmax;
+        /* Set min to be lower then smax */
+        if (worker->min > worker->smax)
+            worker->min = worker->smax; 
+        worker->cp->nfree = worker->hmax;
+    }
+    else {
+        /* This will supress the apr_reslist creation */
+        worker->min = worker->smax = worker->hmax = 0;
+    }
+    if (worker->hmax) {
+        rv = apr_reslist_create(&(worker->cp->res),
+                                worker->min, worker->smax,
+                                worker->hmax, worker->ttl,
+                                connection_constructor, connection_destructor,
+                                s, worker->cp->pool);
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                     "proxy: initialized worker for (%s) min=%d max=%d smax=%d",
+                      worker->hostname, worker->min, worker->hmax, worker->smax);
+
+#if (APR_MAJOR_VERSION > 0)
+        /* Set the acquire timeout */
+        if (rv == APR_SUCCESS && worker->acquire_set)
+            apr_reslist_timeout_set(worker->cp->res, worker->acquire);
+#endif
+    }
+    else
+#endif
+    {
+        
+        connection_constructor((void **)&(worker->cp->conn), s, worker->cp->pool);
+        rv = APR_SUCCESS;
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                     "proxy: initialized single connection worker for (%s)",
+                      worker->hostname);
+    }
+
+    return rv;
+}
+
+PROXY_DECLARE(int) ap_proxy_retry_worker(const char *proxy_function,
+                                         proxy_worker *worker,
+                                         server_rec *s)
+{
+    if (worker->status & PROXY_WORKER_IN_ERROR) {
+        apr_interval_time_t diff;
+        apr_time_t now = apr_time_now();
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                    "proxy: %s: retrying the worker for (%s)",
+                     proxy_function, worker->hostname);
+        if (worker->retry)
+            diff = worker->retry;
+        else
+            diff = apr_time_from_sec((60 + 60 * worker->retries++));
+        if (now > worker->error_time + diff) {
+            worker->status &= ~PROXY_WORKER_IN_ERROR;
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                         "proxy: %s: worker for (%s) has been marked for retry",
+                         proxy_function, worker->hostname);
+            return OK;
+        }
+        else
+            return DECLINED;
+    }
+    else
+        return OK;
+}
+
+PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function,
+                                               proxy_conn_rec **conn,
+                                               proxy_worker *worker,
+                                               server_rec *s)
+{
+    apr_status_t rv;
+
+    if (!(worker->status & PROXY_WORKER_INITIALIZED)) {
+        if ((rv = init_conn_worker(worker, s)) != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                         "proxy: %s: failed to initialize worker for (%s)",
+                         proxy_function, worker->hostname);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+        worker->status = PROXY_WORKER_INITIALIZED;
+    }
+
+    if (!PROXY_WORKER_IS_USABLE(worker)) {
+        /* Retry the worker */
+        ap_proxy_retry_worker(proxy_function, worker, s);
+    
+        if (!PROXY_WORKER_IS_USABLE(worker)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                         "proxy: %s: disabled connection for (%s)",
+                         proxy_function, worker->hostname);
+            return HTTP_SERVICE_UNAVAILABLE;
+        }
+    }
+#if APR_HAS_THREADS
+    if (worker->hmax) {
+        rv = apr_reslist_acquire(worker->cp->res, (void **)conn);
+    }
+    else
+#endif
+    {
+        /* create the new connection if the previous was destroyed */
+        if (!worker->cp->conn)
+            connection_constructor((void **)conn, s, worker->cp->pool);
+        else {
+            *conn = worker->cp->conn;
+            worker->cp->conn = NULL;
+        }
+        rv = APR_SUCCESS;
+    }
+
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                     "proxy: %s: failed to acquire connection for (%s)",
+                     proxy_function, worker->hostname);
+        return HTTP_SERVICE_UNAVAILABLE;
+    }
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                 "proxy: %s: has acquired connection for (%s)",
+                 proxy_function, worker->hostname);
+
+    (*conn)->worker = worker;
+
+    return OK;
+}
+
+PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,
+                                               proxy_conn_rec *conn,
+                                               server_rec *s)
+{
+    apr_status_t rv = APR_SUCCESS;
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                 "proxy: %s: has relesed connection for (%s)",
+                 proxy_function, conn->worker->hostname);
+    /* If there is a connection kill it's cleanup */
+    if (conn->connection)
+        apr_pool_cleanup_kill(conn->connection->pool, conn, connection_cleanup);
+    connection_cleanup(conn);
+    conn->connection = NULL;
+
+    return OK;
+}
+
+PROXY_DECLARE(int)
+ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
+                              proxy_server_conf *conf,
+                              proxy_worker *worker,
+                              proxy_conn_rec *conn,
+                              apr_pool_t *ppool,
+                              apr_uri_t *uri,
+                              char **url,
+                              const char *proxyname,
+                              apr_port_t proxyport,
+                              char *server_portstr,
+                              int server_portstr_size)
+{
+    int server_port;
+    apr_status_t err = APR_SUCCESS;
+    /*
+     * Break up the URL to determine the host to connect to
+     */
+
+    /* we break the URL into host, port, uri */
+    if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
+        return ap_proxyerror(r, HTTP_BAD_REQUEST,
+                             apr_pstrcat(p,"URI cannot be parsed: ", *url,
+                                         NULL));
+    }
+    if (!uri->port) {
+        uri->port = apr_uri_port_of_scheme(uri->scheme);
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                 "proxy: connecting %s to %s:%d", *url, uri->hostname,
+                 uri->port);
+
+    /* allocate these out of the specified connection pool 
+     * The scheme handler decides if this is permanent or
+     * short living pool.
+     */
+    /* are we connecting directly, or via a proxy? */
+    if (proxyname) {
+        conn->hostname = apr_pstrdup(ppool, proxyname);
+        conn->port = proxyport;
+    } else {
+        conn->hostname = apr_pstrdup(ppool, uri->hostname);
+        conn->port = uri->port;
+        *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
+                           uri->query ? uri->query : "",
+                           uri->fragment ? "#" : "",
+                           uri->fragment ? uri->fragment : "", NULL);
+    }
+    /* Worker can have the single constant backend adress.
+     * The single DNS lookup is used once per worker.
+     * If dynamic change is needed then set the addr to NULL
+     * inside dynamic config to force the lookup.
+     */
+    if (!worker->cp->addr)
+        err = apr_sockaddr_info_get(&(worker->cp->addr),
+                                    conn->hostname, APR_UNSPEC,
+                                    conn->port, 0,
+                                    worker->cp->pool);
+
+    if (err != APR_SUCCESS) {
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             apr_pstrcat(p, "DNS lookup failure for: ",
+                                         conn->hostname, NULL));
+    }
+
+    /* Get the server port for the Via headers */
+    {
+        server_port = ap_get_server_port(r);
+        if (ap_is_default_port(server_port, r)) {
+            strcpy(server_portstr,"");
+        } else {
+            apr_snprintf(server_portstr, server_portstr_size, ":%d",
+                         server_port);
+        }
+    }
+
+    /* check if ProxyBlock directive on this host */
+    if (OK != ap_proxy_checkproxyblock(r, conf, worker->cp->addr)) {
+        return ap_proxyerror(r, HTTP_FORBIDDEN,
+                             "Connect to remote machine blocked");
+    }
+    return OK;
+}
+
+static int is_socket_connected(apr_socket_t *sock)
+
+{
+    apr_size_t buffer_len = 1;
+    char test_buffer[1]; 
+    apr_status_t socket_status;
+    apr_interval_time_t current_timeout;
+    
+    /* save timeout */
+    apr_socket_timeout_get(sock, &current_timeout);
+    /* set no timeout */
+    apr_socket_timeout_set(sock, 0);
+    socket_status = apr_socket_recv(sock, test_buffer, &buffer_len);
+    /* put back old timeout */
+    apr_socket_timeout_set(sock, current_timeout);
+    if (APR_STATUS_IS_EOF(socket_status))
+        return 0;
+    else
+        return 1;
+}
+
+PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
+                                            proxy_conn_rec *conn,
+                                            proxy_worker *worker,
+                                            server_rec *s)
+{
+    apr_status_t rv;
+    int connected = 0;
+    int loglevel;
+    apr_sockaddr_t *backend_addr = worker->cp->addr;
+    apr_socket_t *newsock;
+    
+    if (conn->sock) {
+        /* This increases the connection pool size
+         * but the number of dropped connections is
+         * relatively small compared to connection lifetime
+         */
+        if (!(connected = is_socket_connected(conn->sock))) {        
+            apr_socket_close(conn->sock);
+            conn->sock = NULL;
+        }
+    }
+    while (backend_addr && !connected) {
+        if ((rv = apr_socket_create(&newsock, backend_addr->family,
+                                SOCK_STREAM, APR_PROTO_TCP,
+                                conn->pool)) != APR_SUCCESS) {
+            loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
+            ap_log_error(APLOG_MARK, loglevel, rv, s,
+                         "proxy: %s: error creating fam %d socket for target %s",
+                         proxy_function,
+                         backend_addr->family,
+                         worker->hostname);
+            /* this could be an IPv6 address from the DNS but the
+             * local machine won't give us an IPv6 socket; hopefully the
+             * DNS returned an additional address to try
+             */
+            backend_addr = backend_addr->next;
+            continue;
+        }
+
+#if !defined(TPF) && !defined(BEOS)
+        if (worker->recv_buffer_size > 0 &&
+            (rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF,
+                                     worker->recv_buffer_size))) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                         "apr_socket_opt_set(SO_RCVBUF): Failed to set "
+                         "ProxyReceiveBufferSize, using default");
+        }
+#endif
+
+        /* Set a timeout on the socket */
+        if (worker->timeout_set == 1) {
+            apr_socket_timeout_set(newsock, worker->timeout);
+        }
+        else {
+             apr_socket_timeout_set(newsock, s->timeout);
+        }
+        /* Set a keepalive option */
+        if (worker->keepalive) {
+            if ((rv = apr_socket_opt_set(newsock, 
+                            APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                             "apr_socket_opt_set(SO_KEEPALIVE): Failed to set"
+                             " Keepalive");
+            }
+        }
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                     "proxy: %s: fam %d socket created to connect to %s",
+                     proxy_function, backend_addr->family, worker->hostname);
+
+        /* make the connection out of the socket */
+        rv = apr_socket_connect(newsock, backend_addr);
+
+        /* if an error occurred, loop round and try again */
+        if (rv != APR_SUCCESS) {
+            apr_socket_close(newsock);
+            loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
+            ap_log_error(APLOG_MARK, loglevel, rv, s,
+                         "proxy: %s: attempt to connect to %pI (%s) failed",
+                         proxy_function,
+                         backend_addr,
+                         worker->hostname);
+            backend_addr = backend_addr->next;
+            continue;
+        }
+        
+        conn->sock   = newsock;
+        connected    = 1;
+    }
+    /* Put the entire worker to error state
+     * Altrough some connections may be alive
+     * no further connections to the worker could be made
+     */
+    if (!connected && PROXY_WORKER_IS_USABLE(worker)) {
+        worker->status |= PROXY_WORKER_IN_ERROR;
+        worker->error_time = apr_time_now();
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+            "ap_proxy_connect_backend disabling worker for (%s)",
+            worker->hostname);
+    }
+    else {
+        worker->error_time = 0;
+        worker->retries = 0;
+    }
+    return connected ? OK : DECLINED;
+}
+
+PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,
+                                              proxy_conn_rec *conn,
+                                              conn_rec *c,
+                                              server_rec *s)
+{
+    proxy_worker *worker = conn->worker;
+    apr_sockaddr_t *backend_addr = worker->cp->addr;
+
+    /* The socket is now open, create a new backend server connection 
+    * 
+    */
+    conn->connection = ap_run_create_connection(c->pool, s, conn->sock,
+                                                c->id, c->sbh,
+                                                c->bucket_alloc);
+
+    if (!conn->connection) {
+        /* the peer reset the connection already; ap_run_create_connection() 
+        * closed the socket
+        */
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
+                     s, "proxy: %s: an error occurred creating a "
+                     "new connection to %pI (%s)", proxy_function,
+                     backend_addr, conn->hostname);
+        /* XXX: Will be closed when proxy_conn is closed */
+        apr_socket_close(conn->sock);
+        conn->sock = NULL;
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+    /* register the connection cleanup to client connection
+     * so that the connection can be closed or reused
+     */
+    apr_pool_cleanup_register(c->pool, (void *)conn,
+                              connection_cleanup,
+                              apr_pool_cleanup_null);      
+
+    /* For ssl connection to backend */
+    if (conn->is_ssl) {
+        if (!ap_proxy_ssl_enable(conn->connection)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0,
+                         s, "proxy: %s: failed to enable ssl support "
+                         "for %pI (%s)", proxy_function, 
+                         backend_addr, conn->hostname);
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+    }
+    else {
+        /* TODO: See if this will break FTP */
+        ap_proxy_ssl_disable(conn->connection);
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                 "proxy: %s: connection complete to %pI (%s)",
+                 proxy_function, backend_addr, conn->hostname);
+
+    /* set up the connection filters */
+    ap_run_pre_connection(conn->connection, conn->sock);
+
+    return OK;
+}
+
+PROXY_DECLARE(int) ap_proxy_lb_workers(void)
+{
+    /* Set the dynamic #workers limit */
+    lb_workers_limit = lb_workers + PROXY_DYNAMIC_BALANCER_LIMIT;
+    return lb_workers_limit;
+}
diff --git a/connectors/build.properties.default b/connectors/build.properties.default
new file mode 100644
index 0000000..4c90588
--- /dev/null
+++ b/connectors/build.properties.default
@@ -0,0 +1,246 @@
+# -----------------------------------------------------------------------------
+# build.properties.sample
+#
+# This is an example "build.properties" file, used to customize building Tomcat
+# for your local environment.  It defines the location of all external
+# modules that Tomcat depends on.  Copy this file to "build.properties"
+# in the top-level source directory, and customize it as needed.
+#
+# $Id$
+# -----------------------------------------------------------------------------
+
+
+# ----- Compile Control Flags -----
+compile.debug=on
+compile.deprecation=off
+compile.optimize=on
+
+
+# ----- Build Control Flags
+
+#Version number
+version=4.1
+
+#Proxy to download subproject
+#proxy.host=proxy.domain
+#proxy.port=8080
+#proxy.use=on
+
+#CVS root for the jakarta subprojects that do not have released yet.
+cvsroot=":pserver:anoncvs@cvs.apache.org:/home/cvspublic"
+
+
+# ----- Default Base Path for Dependent Packages -----
+base.path=/usr/share/java
+
+
+# --------------------------------------------------
+#                REQUIRED LIBRARIES
+# --------------------------------------------------
+
+
+# ----- Commons Beanutils, version 1.1 or later -----
+commons-beanutils.home=${base.path}/commons-beanutils-1.6.1
+commons-beanutils.lib=${commons-beanutils.home}
+commons-beanutils.jar=${commons-beanutils.lib}/commons-beanutils.jar
+commons-beanutils.loc=http://www.apache.org/dist/jakarta/commons/beanutils/binaries/commons-beanutils-1.6.1.tar.gz
+
+
+# ----- Commons Collections, version 1.0 or later -----
+commons-collections.home=${base.path}/commons-collections-2.1
+commons-collections.lib=${commons-collections.home}
+commons-collections.jar=${commons-collections.lib}/commons-collections.jar
+commons-collections.loc=http://www.apache.org/dist/jakarta/commons/collections/binaries/collections-2.1.tar.gz
+
+
+# ----- Commons Digester, version 1.1.1 or later -----
+commons-digester.home=${base.path}/commons-digester-1.4.1
+commons-digester.lib=${commons-digester.home}
+commons-digester.jar=${commons-digester.lib}/commons-digester.jar
+commons-digester.loc=http://www.apache.org/dist/jakarta/commons/digester/binaries/commons-digester-1.4.1.tar.gz
+
+
+# ----- Commons FileUpload, nightly build -----
+commons-fileupload.home=${base.path}/commons-fileupload-1.0-beta-1
+commons-fileupload.lib=${commons-fileupload.home}
+commons-fileupload.jar=${commons-fileupload.lib}/commons-fileupload-1.0-beta-1.jar
+commons-fileupload.loc=http://www.apache.org/dist/jakarta/commons/fileupload/commons-fileupload-1.0-beta-1.tar.gz 
+
+
+# ----- Commons Logging, version 1.0.1 or later -----
+commons-logging.home=${base.path}/commons-logging-1.0.3
+commons-logging.lib=${commons-logging.home}
+commons-logging-api.jar=${commons-logging.lib}/commons-logging-api.jar
+commons-logging.jar=${commons-logging.lib}/commons-logging.jar
+commons-logging.loc=http://www.apache.org/dist/jakarta/commons/logging/binaries/commons-logging-1.0.3.tar.gz
+
+
+# ----- Java Naming and Directory Interface (JNDI), version 1.2 or later -----
+# Note: Optional with JDK 1.3+
+jndi.home=${base.path}/jndi-1.2.1
+jndi.lib=${jndi.home}/lib
+jndi.jar=${jndi.lib}/jndi.jar
+ldap.jar=${jndi.lib}/ldap.jar
+jaas.jar=${jndi.lib}/jaas.jar
+
+
+# ----- Jakarta Regular Expressions Library, version 1.3 -----
+regexp.home=${base.path}/jakarta-regexp-1.3
+regexp.lib=${regexp.home}
+regexp.jar=${regexp.lib}/jakarta-regexp-1.3.jar
+regexp.loc=http://www.apache.org/dist/jakarta/regexp/binaries/jakarta-regexp-1.3.tar.gz
+
+
+# ----- Jakarta Servlet API Classes (Servlet 2.3 / JSP 1.2) -----
+servlet.home=${base.path}/jakarta-servletapi-4
+servlet.lib=${servlet.home}/dist/lib
+servlet.jar=${servlet.lib}/servlet.jar
+servlet.loc=jakarta-servletapi-4
+
+
+# ----- Xerces XML Parser, version 1.4.4 to 2.0.0 Beta 4 -----
+# Note: Optional with JDK 1.4+
+#xerces.home=${base.path}/xerces-1_4_4
+#xerces.lib=${xerces.home}
+#xerces.jar=${xerces.lib}/xerces.jar
+
+
+# ----- Xerces XML Parser, version 2.0.0 or later -----
+# Note: Optional with JDK 1.4+, or if Xerces 1.x is present
+xerces.home=${base.path}/xerces-2_3_0
+xerces.lib=${xerces.home}
+xerces.loc=http://xml.apache.org/dist/xerces-j/old_xerces2/Xerces-J-bin.2.3.0.tar.gz
+xercesImpl.jar=${xerces.lib}/xercesImpl.jar
+xml-apis.jar=${xerces.lib}/xml-apis.jar
+
+
+# --------------------------------------------------
+#                OPTIONAL LIBRARIES
+# --------------------------------------------------
+
+# ----- Mirror ------
+mirror=http://www.apache.org/dist
+
+# ----- Java Activation Framework (JAF), version 1.0.1 or later -----
+activation.home=${base.path}/jaf-1.0.1
+activation.lib=${activation.home}
+activation.jar=${activation.lib}/activation.jar
+
+
+# ----- Commons Daemon, version 20020219 or later -----
+commons-daemon.home=${base.path}/commons-daemon
+commons-daemon.lib=${commons-daemon.home}/dist
+commons-daemon.jar=${commons-daemon.lib}/commons-daemon.jar
+commons-daemon.loc=jakarta-commons-sandbox/daemon
+
+
+# ----- Commons DBCP, version 1.0 or later -----
+commons-dbcp.version=1.1
+commons-dbcp.home=${base.path}/commons-dbcp-${commons-dbcp.version}
+commons-dbcp.lib=${commons-dbcp.home}/commons-dbcp-1.0
+commons-dbcp.jar=${commons-dbcp.lib}/commons-dbcp.jar
+commons-dbcp.loc=${mirror}/jakarta/commons/dbcp/binaries/commons-dbcp-${commons-dbcp.version}.tar.gz
+
+
+# ----- Commons Modeler, version 1.0 or later -----
+commons-modeler.version=1.1
+commons-modeler.home=${base.path}/commons-modeler-${commons-modeler.version}
+commons-modeler.lib=${commons-modeler.home}
+commons-modeler.jar=${commons-modeler.lib}/commons-modeler.jar
+commons-modeler.loc=${mirror}/jakarta/commons/modeler/binaries/modeler-${commons-modeler.version}.tar.gz
+
+
+# ----- Commons Pool, version 1.0 or later -----
+commons-pool.version=1.1
+commons-pool.home=${base.path}/commons-pool-${commons-pool.version}
+commons-pool.lib=${commons-pool.home}
+commons-pool.jar=${commons-pool.lib}/commons-pool.jar
+commons-pool.loc=${mirror}/jakarta/commons/pool/binaries/commons-pool-${commons-pool.version}.tar.gz
+
+
+# ----- JavaService, version 1.2.0 or later -----
+# do we really need this anymore?
+javaservice.home=${base.path}/javaservice
+javaservice.loc=http://www.alexandriasc.com/software/JavaService/JavaService-bin-1.2.0.zip
+
+
+# ----- Java Database Connectivity (JDBC) Optional Package, version 2.0 -----
+jdbc20ext.home=${base.path}/jdbc2_0-stdext
+jdbc20ext.lib=${jdbc20ext.home}
+jdbc20ext.jar=${jdbc20ext.lib}/jdbc2_0-stdext.jar
+
+
+# ----- Java Management Extensions (JMX), JMX RI 1.0.1 or later or MX4J 1.0 or later -----
+jmx.version=1.1.1
+jmx.home=${base.path}/mx4j-${jmx.version}
+jmx.lib=${jmx.home}/lib
+jmx.jar=${jmx.lib}/mx4j-jmx.jar
+jmx.loc=http://telia.dl.sourceforge.net/sourceforge/mx4j/mx4j-${jmx.version}.tar.gz
+
+
+# ----- Java Secure Sockets Extension (JSSE), version 1.0.2 or later -----
+jsse.home=${base.path}/jsse-1.0.3
+jsse.lib=${jsse.home}/lib
+jcert.jar=${jsse.lib}/jcert.jar
+jnet.jar=${jsse.lib}/jnet.jar
+jsse.jar=${jsse.lib}/jsse.jar
+
+
+# ----- Java Transaction API (JTA), version 1.0.1 or later -----
+jta.home=${base.path}/jta-1_0_1a
+jta.lib=${jta.home}
+jta.jar=${jta.lib}/jta.jar
+
+
+# ----- JUnit Unit Test Suite, version 3.7 or later -----
+junit.home=${base.path}/junit3.7
+junit.lib=${junit.home}
+junit.jar=${junit.lib}/junit.jar
+junit.loc=http://download.sourceforge.net/junit/junit3.7.zip
+
+
+# ----- Java Mail, version 1.2 or later -----
+mail.home=${base.path}/javamail-1.2
+mail.lib=${mail.home}
+mail.jar=${mail.lib}/mail.jar
+
+
+# ----- NSIS, version 1.9x -----
+nsis.home=${base.path}/nsis
+nsis.loc=http://www.nullsoft.com/free/nsis/nsis198.exe
+
+
+# ----- PureTLS Extension, version 0.9 or later -----
+puretls.home=${base.path}/puretls-0.9b2
+puretls.lib=${puretls.home}/build
+puretls.jar=${puretls.lib}/puretls.jar
+
+
+# ----- Struts, version 1.0.1 or later -----
+struts.version=1.1
+struts.home=${base.path}/jakarta-struts-${struts.version}
+struts.lib=${struts.home}/lib
+struts.jar=${struts.lib}/struts.jar
+struts.loc=${mirror}/jakarta/struts/binaries/jakarta-struts-${struts.version}.tar.gz
+
+
+# ----- Tyrex Data Source, version 1.0 -----
+# Now tyrex is http://tyrex.sourceforge.net/
+tyrex.home=${base.path}/tyrex-1.0
+tyrex.lib=${tyrex.home}
+tyrex.jar=${tyrex.lib}/tyrex-1.0.jar
+tyrex.loc=http://belnet.dl.sourceforge.net/sourceforge/tyrex/tyrex-1.0.tgz
+
+
+# ----- Tomcat4.1.x -----
+tomcat41.version=4.1.30
+tomcat41.home=${base.path}/jakarta-tomcat-${tomcat41.version}
+tomcat41.jar=${tomcat41.home}/server/lib/catalina.jar
+servlet-api.jar=${tomcat41.home}/common/lib/servlet.jar
+tomcat41.loc=http://www.apache.org/dist/jakarta/tomcat-4/v${tomcat41.version}/bin/jakarta-tomcat-${tomcat41.version}.tar.gz 
+
+# ----- Tomcat33 -----
+tomcat33.version=3.3.2
+tomcat33.home=${base.path}/jakarta-tomcat-${tomcat33.version}
+tomcat33.jar=${tomcat33.home}/lib/common/tomcat_core.jar
+tomcat33.loc=${mirror}/jakarta/tomcat-3/v${tomcat33.version}/bin/jakarta-tomcat-${tomcat33.version}.tar.gz
diff --git a/connectors/build.xml b/connectors/build.xml
new file mode 100644
index 0000000..933a30b
--- /dev/null
+++ b/connectors/build.xml
@@ -0,0 +1,324 @@
+<project name="Jtc" default="dist" basedir=".">
+
+<!-- ========== Initialize Properties ===================================== -->
+
+
+  <property file="${user.home}/build.properties"/>   <!-- User local        -->
+  <property file="build.properties"/>                <!-- Component local   -->
+  <property file="build.properties.default"/>
+
+  <property name="dist.dir" location="dist" />
+  <property name="lib.dir" location="${dist.dir}/lib" />
+  <property name="build.dir" location="build" />
+
+  <target name="dist" 
+          description="Compile jtc distribution" depends="build,javadoc">
+    <copy todir="${dist.dir}" >
+      <fileset dir="." >
+        <include name="LICENSE" />
+        <include name="build.xml" />
+        <include name="build.properties.default" />
+        <include name="README.txt" />
+        <include name="RELEASE-NOTES.txt" />
+        <include name="doc/**" />
+      </fileset>
+    </copy>
+
+    <jar file="${dist.dir}/tomcat-connectors-src.jar">
+       <fileset dir="." includes="**" >        
+          <exclude name="webapp/**"/>
+          <exclude name="naming/**"/>
+          <exclude name="build/**"/>
+          <exclude name="dist/**"/>
+          <exclude name="release/**"/>
+          <exclude name="scandoc/**"/>
+       </fileset>
+     </jar>
+  </target>
+
+
+  <target name="build" 
+          description="Compile jtc">
+    <mkdir dir="${build.dir}/classes" />
+    <mkdir dir="${lib.dir}" />
+    
+    <ant dir="util">
+      <property name="puretls.jar" value="${puretls.jar}" />
+      <property name="jsse.lib" value="${jsse.lib}" />
+      <property name="tomcat-util.build" value="${build.dir}" />
+      <property name="tomcat-util.lib" value="${lib.dir}" />
+    </ant>
+
+    <ant dir="coyote" target="compile">
+      <property name="catalina.home" value="${tomcat41.home}"/>
+      <property name="tomcat33.home" value="${tomcat33.home}"/>
+      <property name="build.home" value="${build.dir}"/>
+      <property name="tomcat5.detect" value="true"/> <!-- This is a hack to disable ajp -->
+      <property name="servlet.jar" value="${tomcat41.home}/common/lib/servlet.jar" />
+
+      <property name="tomcat-coyote.jar" value="${lib.dir}/tomcat-coyote.jar" />
+      <property name="tomcat33-coyote.jar" value="${lib.dir}/tomcat33-coyote.jar" />
+      <property name="tomcat4-coyote.jar" value="${lib.dir}/tomcat4-coyote.jar" />
+      <property name="tomcat-util.jar"
+                value="${lib.dir}/tomcat-util.jar"/>
+      <property name="servlet.jar"   value="${servlet-api.jar}"/>
+    </ant>
+
+    <ant dir="http11">
+      <property name="build.home" value="${build.dir}"/>
+      <property name="tomcat-http11.jar" value="${lib.dir}/tomcat-http11.jar"/>
+      <property name="tomcat-coyote.jar" value="${lib.dir}/tomcat-coyote.jar" />
+      <property name="commons-logging.jar" value="${commons-logging.jar}"/>
+    </ant>
+
+    <ant dir="jk" target="build-jk">
+      <property name="commons-logging.jar" value="${commons-logging.jar}"/>
+      <property name="jmx.jar" value="${jmx.jar}"/>
+      <property name="jmx.detect" value="true"/>
+      <property name="tomcat-coyote.jar" value="${lib.dir}/tomcat-coyote.jar" />
+      <property name="jk.build" value="${build.dir}"/>
+
+      <property name="tomcat-jk2.jar" value="${lib.dir}/tomcat-jk2.jar" />
+      <property name="tomcat-jkconfig.jar" value="${lib.dir}/jkconfig.jar" />
+
+      <!-- don't include in dist -->
+      <property name="tomcat5.detect" value="true"/>
+      <property name="tomcat-jk.jar" value="${build.dir}/lib/tomcat-jk.jar" /> 
+      <property name="tomcat-jkshm.jar" value="${build.dir}/lib/jkshm.jar" />
+      <property name="tomcat-jni.jar" value="${build.dir}/lib/tomcat-jni.jar" />
+    </ant>
+
+    <jar file="${dist.dir}/lib/tomcat33-resources.jar" >
+      <fileset dir="${build.dir}/classes" >
+        <include name="**/*.properties" />
+      </fileset>
+    </jar>
+
+    <jar file="${dist.dir}/tomcat-connectors.jar" index="true" >
+      <fileset dir="${build.dir}/classes" >
+        <include name="**" />
+        <exclude name="org/apache/ajp/**" />
+      </fileset>
+    </jar>
+
+  </target>
+
+  <target name="javadoc" 
+          description="javadocs">
+    <mkdir dir="${dist.dir}/doc/api"/>
+    <path id="compile.classpath">
+        <pathelement location="${build.dir}/classes" />
+        <pathelement location="${jmx.jar}" />
+        <pathelement location="${jsse.jar}" />
+        <pathelement location="${java.home}/lib/jsse.jar" />
+        <pathelement location="${jnet.jar}" />
+        <pathelement location="${jcert.jar}" />
+        <pathelement location="${puretls.jar}" />
+        <pathelement location="${commons-logging.jar}" />
+        <pathelement location="${commons-modeler.jar}" />
+    </path>
+    <javadoc 
+      destdir="${dist.dir}/doc/api"
+      author="true"
+      version="true"
+      windowtitle="Tomcat Connector Documentation"
+      doctitle="Tomcat Connector"
+      bottom="Copyright &#169; 2003 Apache Software Foundation.  All Rights Reserved.">
+
+      <fileset dir="util/java" includes="**/*.java"/>
+      <fileset dir="coyote/src/java" includes="**/*.java">
+      </fileset>
+      <fileset dir="http11/src/java" includes="**/*.java"/>
+      <fileset dir="jk/java" includes="**/*.java">
+        <exclude name="org/apache/ajp/**"/>
+      </fileset>
+      <classpath refid="compile.classpath"/>
+    </javadoc>
+
+
+  </target>
+
+  <target name="coyote" 
+          description="Compile Coyote and all related protocols">
+    <ant dir="util" />
+    <ant dir="coyote" target="compile" />
+    <ant dir="http11" />
+    <ant dir="jk" target="build-jk"/>
+    
+    <jar jarfile="jtc.jar" 
+      manifest="coyote/src/conf/MANIFEST.MF" >
+      <fileset dir="util/build/classes" includes="org/apache/**" />
+      <fileset dir="coyote/build/classes" includes="org/apache/coyote/**" />
+      <fileset dir="http11/build/classes" includes="org/apache/coyote/**" />
+      <fileset dir="jk/build/classes" >
+        <include name="org/apache/**" />
+        <exclude name="org/apache/jk/ant/**" />
+      </fileset>
+    </jar>
+  </target>
+  
+
+  <target name="clean" description="Delete build and dist trees">
+    <delete dir="dist" />
+    <delete dir="build" />
+  </target>
+
+
+  <target name="download" depends="proxyflags" 
+    description="Download binary packages" >
+    
+    <mkdir dir="${base.path}" />
+    
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-logging.loc}"/>
+      <param name="destfile" value="${commons-logging.jar}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${commons-modeler.loc}"/>
+      <param name="destfile" value="${commons-modeler.jar}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${jmx.loc}"/>
+      <param name="destfile" value="${jmx.jar}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${tomcat41.loc}"/>
+      <param name="destfile" value="${tomcat41.jar}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${tomcat33.loc}"/>
+      <param name="destfile" value="${tomcat33.jar}"/>
+    </antcall>
+
+    <antcall target="downloadgz">
+      <param name="sourcefile" value="${regexp.loc}"/>
+      <param name="destfile" value="${regexp.jar}"/>
+    </antcall>
+
+  </target>
+
+
+
+  <!-- Common download targets - we should use an import with ant1.6. This is cut&paste from tocmat5 -->
+  
+  <target name="proxyflags">
+    <!-- check proxy parameters. -->
+    <condition property="useproxy">
+      <equals arg1="${proxy.use}" arg2="on" />
+    </condition>
+  </target>
+
+  <target name="setproxy"  if="useproxy">
+    <taskdef name="setproxy"
+      classname="org.apache.tools.ant.taskdefs.optional.net.SetProxy" />
+    <setproxy proxyhost="${proxy.host}" proxyport="${proxy.port}"/> 
+    <echo message="Using ${proxy.host}:${proxy.port} to download ${sourcefile}"/>
+  </target>
+
+  <target name="testexist">
+    <echo message="Testing  for ${destfile}"/>
+    <available file="${destfile}" property="exist"/>
+  </target>
+
+  <target name="testsrc">
+    <echo message="Testing  for ${destfile} versus ${cvs.base}/${location}/src"/>
+    <uptodate property="exist"
+              targetfile="${destfile}">
+      <srcfiles dir="${cvs.base}/${location}/src" includes="**" />
+    </uptodate>
+  </target>
+
+  <target name="downloadgz" unless="exist" depends="setproxy,testexist">
+    <!-- Download and extract the package -->
+    <get src="${sourcefile}" dest="${base.path}/file.tar.gz" />
+    <gunzip src="${base.path}/file.tar.gz" dest="${base.path}/file.tar"/>
+    <untar src="${base.path}/file.tar" dest="${base.path}"/>
+    <delete file="${base.path}/file.tar"/>
+    <delete file="${base.path}/file.tar.gz"/>
+  </target>
+
+  <target name="downloadzip" unless="exist" depends="setproxy,testexist">
+    <!-- Download and extract the package -->
+    <get src="${sourcefile}" dest="${base.path}/file.zip" />
+    <mkdir dir="${destdir}" />
+    <unzip src="${base.path}/file.zip" dest="${destdir}"/>
+    <delete file="${base.path}/file.zip"/>
+  </target>
+
+  <target name="downloadfile" unless="exist" depends="setproxy,testexist">
+    <!-- Download extract the file -->
+    <mkdir dir="${destdir}" />
+    <get src="${sourcefile}" dest="${destfile}" />
+  </target>
+
+
+  <target name="release" >
+    <property name="ver" value="1.1M1" />
+<!--
+    <property name="tag" value="JTC_1_1_M1" />
+-->
+    <property name="tag" value="HEAD" />
+    <property name="rel.name" value="tomcat-connectors" />
+
+<!--
+-->
+    <delete dir="release" />
+    <mkdir dir="release" />
+
+    <cvs command="checkout" 
+         package="jakarta-tomcat-connectors" 
+         cvsroot=":pserver:anoncvs@cvs.apache.org:/home/cvspublic"
+         dest="release" 
+         tag="${tag}"/> 
+
+    <rename src="release/jakarta-tomcat-connectors" dest="release/${rel.name}-${ver}-src" />
+
+    <zip file="release/${rel.name}-${ver}-src.zip" >
+      <fileset dir="release" >
+        <include name="${rel.name}-${ver}-src/**" />
+        <exclude name="${rel.name}-${ver}-src/webapp/**" />
+        <exclude name="${rel.name}-${ver}-src/build/**" />
+        <exclude name="${rel.name}-${ver}-src/dist/**" />
+        <exclude name="${rel.name}-${ver}-src/scandoc/**" />
+        <exclude name="${rel.name}-${ver}-src/naming/**" />
+      </fileset>
+    </zip>
+    <tar  tarfile="release/${rel.name}-${ver}-src.tar" >
+      <tarfileset dir="release" >
+        <include name="${rel.name}-${ver}-src/**" />
+        <exclude name="${rel.name}-${ver}-src/webapp/**" />
+        <exclude name="${rel.name}-${ver}-src/build/**" />
+        <exclude name="${rel.name}-${ver}-src/dist/**" />
+        <exclude name="${rel.name}-${ver}-src/scandoc/**" />
+        <exclude name="${rel.name}-${ver}-src/naming/**" />
+      </tarfileset>
+    </tar> 
+    <gzip zipfile="release/${rel.name}-${ver}-src.tar.gz" src="release/${rel.name}-${ver}-src.tar"/>
+    <delete file="release/${rel.name}-${ver}-src.tar" />
+
+<!--
+-->
+    <ant dir="release/${rel.name}-${ver}-src" target="download" />
+    <ant dir="release/${rel.name}-${ver}-src" target="dist" inheritAll="false" />
+
+    <mkdir dir="release/${rel.name}-${ver}" />
+    <copy todir="release/${rel.name}-${ver}" >
+       <fileset dir="release/${rel.name}-${ver}-src/dist" includes="**"/>
+    </copy>
+    <copy file="release/${rel.name}-${ver}/${rel.name}.jar" tofile="release/${rel.name}-${ver}.jar" />
+    <zip file="release/${rel.name}-${ver}.zip" >
+       <fileset dir="release" includes="${rel.name}-${ver}/**"/>
+    </zip>
+    <tar  tarfile="release/${rel.name}-${ver}.tar" >
+       <tarfileset dir="release" includes="${rel.name}-${ver}/**"/>
+    </tar> 
+    <gzip zipfile="release/${rel.name}-${ver}.tar.gz" src="release/${rel.name}-${ver}.tar"/>
+    
+  </target>
+
+  
+</project>
diff --git a/connectors/common/build/apache.m4 b/connectors/common/build/apache.m4
new file mode 100644
index 0000000..87c820f
--- /dev/null
+++ b/connectors/common/build/apache.m4
@@ -0,0 +1,196 @@
+dnl
+dnl apache.m4: autoconf macro for Apache/apxs
+dnl
+
+dnl
+dnl check for apxs.
+dnl
+
+AC_DEFUN(JTC_CHECK_APXS,[
+WEBSERVER=""
+apache_dir=""
+apache_include=""
+APXS="apxs"
+AC_ARG_WITH(apxs,
+[  --with-apxs[=FILE]      Build shared Apache module. FILE is the optional
+                        pathname to the apxs tool; defaults to finding
+			apxs in your PATH.],
+[
+    case "${withval}" in 
+        y | yes | true) find_apxs=true ;;
+        n | no | false) find_apxs=false ;;
+        *) find_apxs=false ;;
+    esac
+
+    if ${TEST} ${find_apxs} ; then    
+        AC_MSG_RESULT([need to check for Perl first, apxs depends on it...])
+        AC_PATH_PROG(PERL,perl,$PATH)dnl
+    
+        if ${TEST} ${find_apxs} ; then
+            APXS=${withval}
+        else
+            AC_PATH_PROG(APXS,apxs,$PATH)dnl
+        fi
+    
+        if ${TEST} -n "${APXS}" ; then
+            dnl Seems that we have it, but have to check if it is OK first        
+            if ${TEST} ! -x "${APXS}" ; then
+                AC_MSG_ERROR(Invalid location for apxs: '${APXS}')
+            fi
+            
+            $APXS -q PREFIX >/dev/null 2>/dev/null || apxs_support=false
+    
+            if ${TEST} "${apxs_support}" = "false" ; then
+                AC_MSG_RESULT(could not find apxs)
+                AC_MSG_ERROR(You must specify a valid --with-apxs path)
+            fi
+
+            dnl test apache version
+            $RM -rf test
+            $APXS -n test -g
+            APA=`grep STANDARD20 test/mod_test.c`
+            $RM -rf test
+            if ${TEST} -z "$APA" ; then
+                WEBSERVER="apache-1.3"
+            else
+                WEBSERVER="apache-2.0"
+            fi
+            AC_MSG_RESULT([building connector for \"$WEBSERVER\"])
+    
+            AC_SUBST(APXS)
+
+            dnl apache_dir and apache_include are also needed.
+	    apache_dir=`$APXS -q PREFIX`
+	    apache_include="-I`$APXS -q INCLUDEDIR`"
+        fi
+    fi
+],
+[
+	AC_MSG_RESULT(no apxs given)
+])
+])dnl
+
+dnl
+dnl check for apache (static link).
+dnl
+
+AC_DEFUN(JTC_CHECK_APACHE,[
+
+dnl it is copied from the configure of JServ ;=)
+dnl and adapted. 
+
+apache_dir_is_src="false"
+AC_ARG_WITH(apache,
+[  --with-apache=DIR      Build static Apache module. DIR is the pathname 
+                        to the Apache source directory.],
+[
+    if ${TEST} ! -z "$WEBSERVER" ; then
+        AC_MSG_ERROR([Sorry cannot use --with-apxs=${APXS} and --with-apache=${withval} togother, please choose one of both])
+    fi
+
+    AC_MSG_CHECKING([for Apache source directory (assume static build)])
+
+    if ${TEST} -n "${withval}" && ${TEST} -d "${withval}" ; then
+        if ${TEST} -d "${withval}/src" ; then
+           # handle the case where people use relative paths to 
+           # the apache source directory by pre-pending the current
+           # build directory to the path. there are probably 
+           # errors with this if configure is run while in a 
+           # different directory than what you are in at the time
+           if ${TEST} -n "`${ECHO} ${withval}|${GREP} \"^\.\.\"`" ; then
+               withval=`pwd`/${withval}
+           fi
+
+           apache_dir=${withval}
+           apache_dir_is_src="true"
+           AC_MSG_RESULT(${apache_dir})
+        
+           AC_MSG_CHECKING(for Apache include directory)
+
+           if ${TEST} -d "${withval}/src/include" ; then
+               # read osdir from the existing apache.
+               osdir=`${GREP} '^OSDIR=' ${withval}/src/Makefile.config | ${SED} -e 's:^OSDIR=.*/os:os:'`
+               if ${TEST} -z "${osdir}" ; then
+                   osdir=os/unix
+               fi
+               apache_include="-I${withval}/src/include \
+                   -I${withval}/src/${osdir}"
+               WEBSERVER="apache-1.3"
+               AC_MSG_RESULT([${apache_include}, version 1.3])
+           else
+               AC_MSG_ERROR([Sorry Apache 1.2.x is no longer supported.])
+           fi
+        else
+           if ${TEST} -d "${withval}/include" ; then
+              # osdir for Apache20.
+              WEBSERVER="apache-2.0"
+              apache_dir=${withval}
+              apache_dir_is_src="true"
+              AC_MSG_RESULT(${apache_dir})
+           fi
+        fi
+    fi
+
+    dnl Make sure we have a result.
+    if ${TEST} -z "$WEBSERVER" ; then
+        AC_MSG_ERROR([Directory $apache_dir is not a valid Apache source distribution])
+    fi
+
+# VT: Now, which one I'm supposed to use? Let's figure it out later
+
+    configure_apache=true
+    configure_src=true
+    
+    AC_MSG_RESULT([building connector for \"$WEBSERVER\"])
+],
+[
+	AC_MSG_RESULT(no apache given)
+])
+AC_SUBST(apache_include)
+APACHE_DIR=${apache_dir}
+AC_SUBST(APACHE_DIR)
+])
+
+dnl
+dnl check for EAPI (static link only).
+dnl
+
+AC_DEFUN(JTC_CHECK_EAPI,[
+
+dnl CFLAGS for EAPI mod_ssl (Apache 1.3)
+dnl it also allows the CFLAGS environment variable.
+CFLAGS="${CFLAGS}"
+AC_ARG_ENABLE(
+EAPI,
+[  --enable-EAPI           Enable EAPI support (mod_ssl, Apache 1.3)],
+[
+case "${enableval}" in
+    y | Y | YES | yes | TRUE | true )
+        CFLAGS="${CFLAGS} -DEAPI"
+        AC_MSG_RESULT([...Enabling EAPI Support...])
+        ;;
+esac
+])
+AC_SUBST(CFLAGS)
+])
+
+
+dnl
+dnl set flags for apxs.
+dnl
+
+AC_DEFUN(JTC_SET_APXS_FLAGS,[
+dnl the APXSCFLAGS is given by apxs to the C compiler
+dnl the APXSLDFLAGS is given to the linker (for APRVARS).
+APXSLDFLAGS=""
+APXSCFLAGS=""
+if ${TEST} -n "${CFLAGS}" ; then
+	APXSCFLAGS="${CFLAGS}"
+fi
+dnl the APXSLDFLAGS is normaly empty but APXSCFLAGS is not.
+if ${TEST} -n "${LDFLAGS}" ; then
+	APXSLDFLAGS="-Wl,${LDFLAGS}"
+fi
+AC_SUBST(APXSCFLAGS)
+AC_SUBST(APXSLDFLAGS)
+])
diff --git a/connectors/common/build/get_ver.awk b/connectors/common/build/get_ver.awk
new file mode 100644
index 0000000..1e50a1e
--- /dev/null
+++ b/connectors/common/build/get_ver.awk
@@ -0,0 +1,67 @@
+BEGIN {
+
+  # fetch mod_jk version numbers from input file and writes them to STDOUT
+
+  while ((getline < ARGV[1]) > 0) {
+    if (match ($0, /^#define JK_VERMAJOR [^"]+/)) {
+      jk_ver_major = substr($3, 1, length($3));
+    }
+    else if (match ($0, /^#define JK_VERMINOR [^"]+/)) {
+      jk_ver_minor = substr($3, 1, length($3));
+    }
+    else if (match ($0, /^#define JK_VERFIX [^"]+/)) {
+      jk_ver_fix = substr($3, 1, length($3));
+    }
+    else if (match ($0, /^#define JK_VERISRELEASE [^"]+/)) {
+      jk_ver_isrelease = substr($3, 1, length($3));
+    }
+    else if (match ($0, /^#define JK_VERBETA [^"]+/)) {
+      jk_ver_isbeta = substr($3, 1, length($3));
+    }
+    else if (match ($0, /^#define JK_BETASTRING [^"]+/)) {
+      jk_ver_betastr = substr($3, 2, length($3) - 2);
+    }
+  }
+  jk_ver = jk_ver_major "," jk_ver_minor "," jk_ver_fix;
+  jk_ver_str = jk_ver_major "." jk_ver_minor "." jk_ver_fix;
+  if (jk_ver_isrelease != 1) {
+    jk_ver_str = jk_ver_str "-dev";
+  }
+  if (jk_ver_isbeta == 1) {
+    jk_ver_str = jk_ver_str "-beta-" jk_ver_betastr;
+  }
+  
+  # fetch Apache version numbers from input file and writes them to STDOUT
+
+  if (ARGV[2]) {
+    if (match (ARGV[2], /ap_release.h/)) {
+      while ((getline < ARGV[2]) > 0) {
+        if (match ($0, /^#define AP_SERVER_MAJORVERSION "[^"]+"/)) {
+          ap_ver_major = substr($3, 2, length($3) - 2);
+        }
+        else if (match ($0, /^#define AP_SERVER_MINORVERSION "[^"]+"/)) {
+          ap_ver_minor = substr($3, 2, length($3) - 2);
+        }
+        else if (match ($0, /^#define AP_SERVER_PATCHLEVEL/)) {
+          ap_ver_str_patch = substr($3, 2, length($3) - 2);
+          if (match (ap_ver_str_patch, /[0-9][0-9]*/)) {
+            ap_ver_patch = substr(ap_ver_str_patch, RSTART, RLENGTH); 
+          }
+        }
+      }
+      ap_ver_str = ap_ver_major "." ap_ver_minor "." ap_ver_str_patch;
+    }
+    if (match (ARGV[2], /httpd.h/)) {
+      while ((getline < ARGV[2]) > 0) {
+        if (match ($0, /^#define SERVER_BASEREVISION "[^"]+"/)) {
+          ap_ver_str = substr($3, 2, length($3) - 2);
+        }
+      }
+    }
+    print "AP_VERSION_STR = " ap_ver_str "";
+  }
+
+  print "JK_VERSION = " jk_ver "";
+  print "JK_VERSION_STR = " jk_ver_str "";
+
+}
diff --git a/connectors/common/build/os_apache.m4 b/connectors/common/build/os_apache.m4
new file mode 100644
index 0000000..8716772
--- /dev/null
+++ b/connectors/common/build/os_apache.m4
@@ -0,0 +1,27 @@
+dnl copied from httpd-2.0/os/config.m4
+dnl OS changed to OS_APACHE and OS_DIR to OS_APACHE_DIR
+
+AC_MSG_CHECKING(for target platform)
+
+#PLATFORM=`${CONFIG_SHELL-/bin/sh} $ac_config_guess`
+PLATFORM=$host
+
+case "$PLATFORM" in
+*beos*)
+  OS_APACHE="beos"
+  OS_APACHE_DIR=$OS_APACHE
+  ;;
+*pc-os2_emx*)
+  OS_APACHE="os2"
+  OS_APACHE_DIR=$OS_APACHE
+  ;;
+bs2000*)
+  OS_APACHE="unix"
+  OS_APACHE_DIR=bs2000  # only the OS_APACHE_DIR is platform specific.
+  ;;
+*)
+  OS_APACHE="unix"
+  OS_APACHE_DIR=$OS_APACHE;;
+esac
+
+AC_MSG_RESULT($OS_APACHE)
diff --git a/connectors/coyote/.cvsignore b/connectors/coyote/.cvsignore
new file mode 100644
index 0000000..3995981
--- /dev/null
+++ b/connectors/coyote/.cvsignore
@@ -0,0 +1,2 @@
+build
+build.properties
\ No newline at end of file
diff --git a/connectors/coyote/build.properties.sample b/connectors/coyote/build.properties.sample
new file mode 100644
index 0000000..53f87ad
--- /dev/null
+++ b/connectors/coyote/build.properties.sample
@@ -0,0 +1,35 @@
+# -------------------------------------------------------------------
+# build.properties.sample
+#
+# This is an example "build.properties" file, used to customize 
+# building Jakarta Coyote for your local environment.  
+# Make any changes you need, and rename this file to 
+# "build.properties" 
+#
+# $Id$
+# -------------------------------------------------------------------
+
+
+# -------------------------------------------------------------------
+# CONFIGURATION OPTIONS
+# -------------------------------------------------------------------
+
+# 
+
+
+# -------------------------------------------------------------------
+# EXTERNAL DEPENDENCIES 
+# -------------------------------------------------------------------
+
+# tomcat33.home -- Tomcat 3.3 distribution directory
+tomcat33.home=../../jakarta-tomcat/build/tomcat
+
+# catalina.home -- Tomcat 4.x distribution directory
+catalina.home=../../jakarta-tomcat-4.0/build
+
+# tomcat-util.jar -- Tomcat util package
+tomcat-util.jar=../util/build/lib/tomcat-util.jar
+
+# junit.jar -- JUnit classes (http://junit.org)
+junit.jar=/java/junit/junit.jar
+
diff --git a/connectors/coyote/build.xml b/connectors/coyote/build.xml
new file mode 100644
index 0000000..83b94ec
--- /dev/null
+++ b/connectors/coyote/build.xml
@@ -0,0 +1,375 @@
+<project name="Coyote" default="compile" basedir=".">
+
+
+<!--
+        "Coyote" connector framework for Jakarta Tomcat
+        $Id$
+-->
+
+
+<!-- ========== Initialize Properties ===================================== -->
+
+
+  <property file="${user.home}/build.properties"/>   <!-- User local        -->
+  <property file="build.properties"/>                <!-- Component local   -->
+  <property file="../build.properties"/>             <!-- Commons local     -->
+  <property file="../build.properties.default"/>             <!-- Commons local     -->
+
+
+<!-- ========== External Dependencies ===================================== -->
+
+
+  <!-- The directories corresponding to your necessary dependencies -->
+  <property name="junit.home"              value="/usr/local/junit3.5"/>
+
+  <!-- Dependencies within jakarta-tomcat-connectors -->
+  <property name="util.home"               value="../util"/>
+
+
+<!-- ========== Derived Values ============================================ -->
+
+
+  <!-- The locations of necessary jar files -->
+  <property name="tomcat-util.jar"  value="../util/build/lib/tomcat-util.jar"/>
+  <property name="junit.jar"        value="${junit.home}/junit.jar"/>
+
+  <property name="commons-modeler.jar" location="../../jakarta-commons/modeler/dist/commons-modeler.jar" />
+
+  <property name="jmx.jar" location="../lib/mx4j.jar" />
+
+
+<!-- ========== Component Declarations ==================================== -->
+
+
+  <!-- The name of this component -->
+  <property name="component.name"          value="coyote"/>
+  <!-- The title of this component -->
+  <property name="component.title"         value="Coyote"/>
+
+  <!-- The current version number of this component -->
+  <property name="component.version"       value="1.0-dev"/>
+
+  <!-- The base directory for compilation targets -->
+  <property name="build.home"              value="build"/>
+
+  <!-- The base directory for component configuration files -->
+  <property name="conf.home"               value="src/conf"/>
+
+  <!-- The base directory for component sources -->
+  <property name="source.home"             value="src/java"/>
+
+  <!-- The base directory for unit test sources -->
+  <property name="test.home"               value="src/test"/>
+
+<!-- ========== Compiler Defaults ========================================= -->
+
+
+  <!-- Should Java compilations set the 'debug' compiler option? -->
+  <property name="compile.debug"           value="true"/>
+
+  <!-- Should Java compilations set the 'deprecation' compiler option? -->
+  <property name="compile.deprecation"     value="false"/>
+
+  <!-- Should Java compilations set the 'optimize' compiler option? -->
+  <property name="compile.optimize"        value="true"/>
+
+    <!-- default locations -->
+    <property name="tomcat33.home" 
+	      location="${base.path}/jakarta-tomcat-3.3" />
+    <property name="catalina.home" 
+	      location="${base.path}/jakarta-tomcat-4.1.24" />
+
+    <property name="servlet.jar"
+              value="${catalina.home}/common/lib/servlet.jar" />
+
+  <!-- Construct compile classpath -->
+  <path id="compile.classpath">
+    <pathelement location="${build.home}/classes"/>
+    <pathelement location="${util.home}/build/classes"/>
+    <pathelement location="${commons-logging.jar}"/>
+    <pathelement location="${commons-modeler.jar}"/>
+    <pathelement location="${jmx.jar}"/>
+    <pathelement location="${tomcat-util.jar}"/>
+    <pathelement location="${catalina.home}/server/lib/catalina.jar"/>
+    <pathelement location="${servlet.jar}"/>
+  </path>
+  <path id="compile.classpath.tomcat33">
+    <pathelement location="${build.home}/classes"/>
+    <pathelement location="${util.home}/build/classes"/>
+    <pathelement location="${commons-logging.jar}"/>
+    <pathelement location="${commons-modeler.jar}"/>
+    <pathelement location="${jmx.jar}"/>
+    <pathelement location="${tomcat33.home}/lib/container/container_util.jar"/>
+    <pathelement location="${tomcat33.home}/lib/container/tomcat_modules.jar"/>
+    <pathelement location="${tomcat33.home}/lib/common/tomcat_core.jar"/>
+    <pathelement location="${tomcat33.home}/lib/common/core_util.jar"/>
+  </path>
+
+
+<!-- ========== Test Execution Defaults =================================== -->
+
+
+  <!-- Construct unit test classpath -->
+  <path id="test.classpath">
+    <pathelement location="${build.home}/classes"/>
+    <pathelement location="${build.home}/tests"/>
+    <pathelement location="${tomcat-util.jar}"/>
+    <pathelement location="${junit.jar}"/>
+  </path>
+
+  <!-- Should all tests fail if one does? -->
+  <property name="test.failonerror"        value="true"/>
+
+  <!-- The test runner to execute -->
+  <property name="test.runner"             value="junit.textui.TestRunner"/>
+  <property name="test.entry" value="org.apache.coyote.TestAll"/>
+
+
+<!-- ========== Detection and Reports ===================================== -->
+
+
+    <target name="report-tc5" if="tomcat5.detect" >
+	<echo message="Tomcat5 detected "  />
+    </target>
+    <target name="report-tc4" if="tomcat4.detect" >
+	<echo message="Tomcat4 detected "  />
+    </target>
+    <target name="report-tc33" if="tomcat33.detect" >
+	<echo message="Tomcat3.3 detected "  />
+    </target>
+
+    <target name="report" depends="report-tc5, report-tc4, report-tc33" />
+
+
+<!-- ========== Executable Targets ======================================== -->
+
+
+  <target name="init"
+   description="Initialize and evaluate conditionals">
+    <echo message="-------- ${component.title} ${component.version} --------"/>
+    <filter  token="name"                  value="${component.name}"/>
+    <filter  token="version"               value="${component.version}"/>
+  </target>
+
+
+  <target name="prepare" depends="init"
+   description="Prepare build directory">
+    <mkdir dir="${build.home}"/>
+    <mkdir dir="${build.home}/classes"/>
+    <mkdir dir="${build.home}/conf"/>
+    <mkdir dir="${build.home}/docs"/>
+    <mkdir dir="${build.home}/docs/api"/>
+    <mkdir dir="${build.home}/lib"/>
+    <mkdir dir="${build.home}/tests"/>
+    <condition property="tomcat5.detect">
+      <and>
+        <available file="${catalina.home}/server/lib/catalina.jar" />
+        <available
+          classname="javax.servlet.ServletRequestEvent"
+          classpath="${servlet.jar}"
+        />
+      </and>
+    </condition>
+    <condition property="tomcat4.detect">
+      <and>
+        <available file="${catalina.home}/server/lib/catalina.jar" />
+        <not>
+          <available
+            classname="javax.servlet.ServletRequestEvent"
+            classpath="${servlet.jar}"
+          />
+        </not>
+      </and>
+    </condition>
+    <available property="tomcat33.detect" file="${tomcat33.home}/lib/common/tomcat_core.jar" />
+  </target>
+
+
+  <target name="static" depends="prepare"
+   description="Copy static files to build directory">
+    <tstamp/>
+    <copy  todir="${build.home}/conf" filtering="on">
+      <fileset dir="${conf.home}" includes="*.MF"/>
+    </copy>
+  </target>
+
+
+  <target name="compile.shared"
+   description="Compile shareable components">
+    <mkdir dir="${build.home}/classes"/>
+    <javac  srcdir="${source.home}"
+           destdir="${build.home}/classes"
+             debug="${compile.debug}"
+       deprecation="${compile.deprecation}"
+          optimize="${compile.optimize}">
+      <classpath refid="compile.classpath"/>
+      <exclude name="org/apache/coyote/tomcat5/**" />
+      <exclude name="org/apache/coyote/tomcat4/**" />
+      <exclude name="org/apache/coyote/tomcat3/**" />
+    </javac>
+    <copy    todir="${build.home}/classes" filtering="on">
+      <fileset dir="${source.home}" excludes="**/*.java"/>
+    </copy>
+  </target>
+
+
+  <target name="compile.tomcat5" if="tomcat5.detect"
+   description="Compile Tomcat 5.x Adapter">
+    <javac  srcdir="${source.home}"
+           destdir="${build.home}/classes"
+             debug="${compile.debug}"
+       deprecation="${compile.deprecation}"
+          optimize="${compile.optimize}">
+      <classpath refid="compile.classpath"/>
+      <include name="org/apache/coyote/tomcat5/**" />
+    </javac>
+  </target>
+
+  <target name="jar.tomcat5" depends="static,compile.shared,compile.tomcat5" >
+    <jar  jarfile="${tomcat-coyote.jar}"
+         index="true"
+         basedir="${build.home}/classes"
+         manifest="${build.home}/conf/MANIFEST.MF"
+         excludes="**/tomcat3/*" >
+      <include name="org/apache/coyote/*.class" />
+      <include name="org/apache/coyote/memory/*.class" />
+      <!-- included with catalina.jar -->
+      <exclude name="org/apache/coyote/tomcat5/**" />
+    </jar>
+  </target>
+
+  <target name="compile.tomcat4" if="tomcat4.detect"
+   description="Compile Tomcat 4.x Adapter">
+    <javac  srcdir="${source.home}"
+           destdir="${build.home}/classes"
+             debug="${compile.debug}"
+       deprecation="${compile.deprecation}"
+          optimize="${compile.optimize}">
+      <classpath refid="compile.classpath"/>
+      <include name="org/apache/coyote/tomcat4/**" />
+    </javac>
+    <copy    todir="${build.home}/classes" filtering="on">
+      <fileset dir="${source.home}" excludes="**/*.java"/>
+    </copy>
+  </target>
+
+  <target name="compile.tomcat33" if="tomcat33.detect"
+   description="Compile Tomcat 3.3.x Adapter">
+    <javac  srcdir="${source.home}"
+           destdir="${build.home}/classes"
+             debug="${compile.debug}"
+       deprecation="${compile.deprecation}"
+          optimize="${compile.optimize}">
+      <classpath refid="compile.classpath.tomcat33"/>
+      <include name="org/apache/coyote/tomcat3/**" />
+    </javac>
+    <copy    todir="${build.home}/classes" filtering="on">
+      <fileset dir="${source.home}" excludes="**/*.java"/>
+    </copy>
+  </target>
+
+  <target name="shared.jar" depends="static,compile.shared"
+       description="Build shared Coyote jar"> 
+    <property name="tomcat-coyote.jar" value="${build.home}/lib/tomcat-${component.name}.jar" />
+    <jar    jarfile="${tomcat-coyote.jar}"
+            index="true"
+            manifest="${build.home}/conf/MANIFEST.MF">
+      <fileset dir="${build.home}/classes" >
+        <exclude name="**/tomcat3/*"/>
+        <!-- exclude name="**/tomcat4/*"/ -->
+        <exclude name="**/tomcat5/*"/>
+        <include name="org/apache/coyote/**"/>
+      </fileset>
+    </jar>
+  </target>
+ 
+  <target name="tomcat4.jar" depends="static,compile.shared,compile.tomcat4"
+       description="Build Tomcat 4 Adapter jar" if="tomcat4.detect">
+    <property name="tomcat4-coyote.jar" value="${build.home}/lib/tomcat4-${component.name}.jar" />
+    <jar jarfile="${tomcat4-coyote.jar}"
+         index="true"
+         basedir="${build.home}/classes"
+         manifest="${build.home}/conf/MANIFEST.MF"
+         includes="org/apache/coyote/tomcat4/**" />
+  </target>
+
+  <target name="tomcat33.jar" depends="static,compile.shared,compile.tomcat33"
+       description="Build Tomcat 3.3 Adapter jar" if="tomcat33.detect">
+    <property name="tomcat33-coyote.jar" value="${build.home}/lib/tomcat33-${component.name}.jar" />
+    <jar jarfile="${tomcat33-coyote.jar}"
+         index="true"
+         basedir="${build.home}/classes"
+         manifest="${build.home}/conf/MANIFEST.MF"
+         includes="org/apache/coyote/tomcat3/**" /> 
+  </target>
+
+  <target name="compile" 
+   depends="static,report,shared.jar,tomcat4.jar,tomcat33.jar"
+   description="Compile Coyote and its Adapters">
+  </target>
+
+  <target name="compile.tests" depends="compile"
+   description="Compile unit test cases">
+    <javac  srcdir="${test.home}"
+           destdir="${build.home}/tests"
+             debug="${compile.debug}"
+       deprecation="${compile.deprecation}"
+          optimize="${compile.optimize}">
+      <classpath refid="test.classpath"/>
+    </javac>
+    <copy    todir="${build.home}/tests" filtering="on">
+      <fileset dir="${test.home}" excludes="**/*.java"/>
+    </copy>
+  </target>
+
+
+  <target name="clean"
+   description="Clean build and distribution directories">
+    <delete    dir="${build.home}"/>
+  </target>
+
+
+  <target name="all" depends="clean,compile"
+   description="Clean and compile all components"/>
+
+
+  <target name="javadoc" unless="docs-uptodate"
+   description="Create component Javadoc documentation">
+    <mkdir dir="${build.home}/docs/api"/>
+    <javadoc sourcepath="${source.home}"
+                destdir="${build.home}/docs/api"
+           packagenames="org.apache.coyote"
+                 author="true"
+                private="true"
+                version="true"
+               doctitle="&lt;h1&gt;${component.title}&lt;/h1&gt;"
+            windowtitle="${component.title} (Version ${component.version})"
+                 bottom="Copyright (c) 2001 - Apache Software Foundation">
+      <classpath refid="compile.classpath"/>
+    </javadoc>
+  </target>
+
+
+<!-- ========== Unit Test Targets ========================================= -->
+
+
+  <target name="test"  depends="compile.tests" if="test.entry"
+   description="Run all unit test cases">
+      <!--
+      <junit printsummary="yes" fork="on" haltonfailure="yes">
+      	<formatter type="plain" usefile="false"/>
+      	<test name="${test.entry}"/>
+        <classpath refid="test.classpath"/>
+      </junit>
+      -->
+
+      <java classname="${test.runner}" fork="yes"
+       failonerror="${test.failonerror}">
+        <jvmarg value="${java.protocol.handler.pkgs}"/>
+        <arg value="${test.entry}"/>
+        <classpath refid="test.classpath"/>
+      </java>
+  </target>
+
+
+</project>
diff --git a/connectors/coyote/src/conf/MANIFEST.MF b/connectors/coyote/src/conf/MANIFEST.MF
new file mode 100644
index 0000000..379ca08
--- /dev/null
+++ b/connectors/coyote/src/conf/MANIFEST.MF
@@ -0,0 +1,6 @@
+Extension-Name: @name@
+Specification-Vendor: Apache Software Foundation
+Specification-Version: 1.1
+Implementation-Vendor: Apache Software Foundation
+Implementation-Version: @version@
+
diff --git a/connectors/coyote/src/java/org/apache/coyote/ActionCode.java b/connectors/coyote/src/java/org/apache/coyote/ActionCode.java
new file mode 100644
index 0000000..8d3b36d
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/ActionCode.java
@@ -0,0 +1,138 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+ 
+package org.apache.coyote;
+
+
+/**
+ * Enumerated class containing the adapter event codes.
+ *
+ * @author Remy Maucherat
+ */
+public final class ActionCode {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final ActionCode ACTION_ACK = new ActionCode(1);
+
+
+    public static final ActionCode ACTION_CLOSE = new ActionCode(2);
+
+
+    public static final ActionCode ACTION_COMMIT = new ActionCode(3);
+
+
+    /**
+     * A flush() operation originated by the client ( i.e. a flush() on
+     * the servlet output stream or writer, called by a servlet ).
+     */
+    public static final ActionCode ACTION_CLIENT_FLUSH = new ActionCode(4);
+
+    
+    public static final ActionCode ACTION_CUSTOM = new ActionCode(5);
+
+
+    public static final ActionCode ACTION_RESET = new ActionCode(6);
+
+
+    public static final ActionCode ACTION_START = new ActionCode(7);
+
+
+    public static final ActionCode ACTION_STOP = new ActionCode(8);
+
+
+    public static final ActionCode ACTION_WEBAPP = new ActionCode(9);
+
+    /** Hook called after request, but before recycling. Can be used
+        for logging, to update counters, custom cleanup - the request
+        is still visible
+    */
+    public static final ActionCode ACTION_POST_REQUEST = new ActionCode(10);
+
+    /**
+     * Callback for lazy evaluation - extract the remote host address.
+     */
+    public static final ActionCode ACTION_REQ_HOST_ATTRIBUTE = 
+        new ActionCode(11);
+
+
+    /**
+     * Callback for lazy evaluation - extract the remote host infos (address, name, port) and local address.
+     */
+    public static final ActionCode ACTION_REQ_HOST_ADDR_ATTRIBUTE = new ActionCode(12);
+
+    /**
+     * Callback for lazy evaluation - extract the SSL-related attributes.
+     */
+    public static final ActionCode ACTION_REQ_SSL_ATTRIBUTE = new ActionCode(13);
+
+
+    /** Chain for request creation. Called each time a new request is created
+        ( requests are recycled ).
+     */
+    public static final ActionCode ACTION_NEW_REQUEST = new ActionCode(14);
+
+
+    /**
+     * Callback for lazy evaluation - extract the SSL-certificate 
+     * (including forcing a re-handshake if necessary)
+     */
+    public static final ActionCode ACTION_REQ_SSL_CERTIFICATE = new ActionCode(15);
+    
+    
+    /**
+     * Callback for lazy evaluation - socket remote port.
+     **/
+    public static final ActionCode ACTION_REQ_REMOTEPORT_ATTRIBUTE = new ActionCode(16);
+
+    
+    /**
+     * Callback for lazy evaluation - socket local port.
+     **/
+    public static final ActionCode ACTION_REQ_LOCALPORT_ATTRIBUTE = new ActionCode(17);
+    
+    
+    /**
+     * Callback for lazy evaluation - local address.
+     **/
+    public static final ActionCode ACTION_REQ_LOCAL_ADDR_ATTRIBUTE = new ActionCode(18);
+    
+    
+    /**
+     * Callback for lazy evaluation - local address.
+     **/
+    public static final ActionCode ACTION_REQ_LOCAL_NAME_ATTRIBUTE = new ActionCode(19);
+
+    // ----------------------------------------------------------- Constructors
+    int code;
+
+    /**
+     * Private constructor.
+     */
+    private ActionCode(int code) {
+        this.code=code;
+    }
+
+    /** Action id, useable in switches and table indexes
+     */
+    public int getCode() {
+        return code;
+    }
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/ActionHook.java b/connectors/coyote/src/java/org/apache/coyote/ActionHook.java
new file mode 100644
index 0000000..458b0e7
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/ActionHook.java
@@ -0,0 +1,37 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote;
+
+
+/**
+ * Action hook.
+ *
+ * @author Remy Maucherat
+ */
+public interface ActionHook {
+
+
+    /**
+     * Send an action to the connector.
+     * 
+     * @param actionCode Type of the action
+     * @param param Action parameter
+     */
+    public void action(ActionCode actionCode, Object param);
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/Adapter.java b/connectors/coyote/src/java/org/apache/coyote/Adapter.java
new file mode 100644
index 0000000..e8eb7a1
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/Adapter.java
@@ -0,0 +1,47 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote;
+
+
+/**
+ * Adapter.
+ *
+ * @author Remy Maucherat
+ */
+public interface Adapter {
+
+
+    /** 
+     * Call the service method, and notify all listeners
+     *
+     * @exception Exception if an error happens during handling of
+     *   the request. Common errors are:
+     *   <ul><li>IOException if an input/output error occurs and we are
+     *   processing an included servlet (otherwise it is swallowed and
+     *   handled by the top level error handler mechanism)
+     *       <li>ServletException if a servlet throws an exception and
+     *  we are processing an included servlet (otherwise it is swallowed
+     *  and handled by the top level error handler mechanism)
+     *  </ul>
+     *  Tomcat should be able to handle and log any other exception ( including
+     *  runtime exceptions )
+     */
+    public void service(Request req, Response res)
+	throws Exception;
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/Constants.java b/connectors/coyote/src/java/org/apache/coyote/Constants.java
new file mode 100644
index 0000000..9a7715e
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/Constants.java
@@ -0,0 +1,55 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote;
+
+import java.util.Locale;
+
+/**
+ * Constants.
+ *
+ * @author Remy Maucherat
+ */
+public final class Constants {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1";
+
+
+    public static final String LOCALE_DEFAULT = "en";
+
+
+    public static final Locale DEFAULT_LOCALE = new Locale(LOCALE_DEFAULT, "");
+
+
+    public static final int MAX_NOTES = 32;
+
+
+    // Request states
+    public static final int STAGE_NEW = 0;
+    public static final int STAGE_PARSE = 1;
+    public static final int STAGE_PREPARE = 2;
+    public static final int STAGE_SERVICE = 3;
+    public static final int STAGE_ENDINPUT = 4;
+    public static final int STAGE_ENDOUTPUT = 5;
+    public static final int STAGE_KEEPALIVE = 6;
+    public static final int STAGE_ENDED = 7;
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/InputBuffer.java b/connectors/coyote/src/java/org/apache/coyote/InputBuffer.java
new file mode 100644
index 0000000..acecf12
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/InputBuffer.java
@@ -0,0 +1,41 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+
+
+/**
+ * Input buffer.
+ * 
+ * @author Remy Maucherat
+ */
+public interface InputBuffer {
+
+
+    /** Return from the input stream.
+        IMPORTANT: the current model assumes that the protocol will 'own' the
+        buffer and return a pointer to it in ByteChunk ( i.e. the param will
+        have chunk.getBytes()==null before call, and the result after the call ).
+    */
+    public int doRead(ByteChunk chunk, Request request) 
+        throws IOException;
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/OutputBuffer.java b/connectors/coyote/src/java/org/apache/coyote/OutputBuffer.java
new file mode 100644
index 0000000..2c5c4a4
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/OutputBuffer.java
@@ -0,0 +1,36 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+
+
+/**
+ * Output buffer.
+ * 
+ * @author Remy Maucherat
+ */
+public interface OutputBuffer {
+
+
+    public int doWrite(ByteChunk chunk, Response response) 
+        throws IOException;
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/Processor.java b/connectors/coyote/src/java/org/apache/coyote/Processor.java
new file mode 100644
index 0000000..5fceaf7
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/Processor.java
@@ -0,0 +1,42 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+/**
+ * Processor.
+ *
+ * @author Remy Maucherat
+ */
+public interface Processor {
+
+
+    public void setAdapter(Adapter adapter);
+
+
+    public Adapter getAdapter();
+
+
+    public void process(InputStream input, OutputStream output)
+        throws IOException;
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/ProtocolHandler.java b/connectors/coyote/src/java/org/apache/coyote/ProtocolHandler.java
new file mode 100644
index 0000000..71a76cd
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/ProtocolHandler.java
@@ -0,0 +1,78 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote;
+
+
+/**
+ * Abstract the protocol implementation, including threading, etc.
+ * Processor is single threaded and specific to stream-based protocols,
+ * will not fit Jk protocols like JNI.
+ *
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ */
+public interface ProtocolHandler {
+
+
+    /**
+     * Pass config info.
+     */
+    public void setAttribute(String name, Object value);
+
+
+    public Object getAttribute(String name);
+
+
+    /**
+     * The adapter, used to call the connector.
+     */
+    public void setAdapter(Adapter adapter);
+
+
+    public Adapter getAdapter();
+
+
+    /**
+     * Init the protocol.
+     */
+    public void init()
+        throws Exception;
+
+
+    /**
+     * Start the protocol.
+     */
+    public void start()
+        throws Exception;
+
+    /**
+     * Pause the protocol (optional).
+     */
+    public void pause()
+        throws Exception;
+
+    /**
+     * Resume the protocol (optional).
+     */
+    public void resume()
+        throws Exception;
+
+    public void destroy()
+        throws Exception;
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/Request.java b/connectors/coyote/src/java/org/apache/coyote/Request.java
new file mode 100644
index 0000000..53d4d90
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/Request.java
@@ -0,0 +1,521 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.buf.UDecoder;
+
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.http.Parameters;
+import org.apache.tomcat.util.http.ContentType;
+import org.apache.tomcat.util.http.Cookies;
+
+/**
+ * This is a low-level, efficient representation of a server request. Most 
+ * fields are GC-free, expensive operations are delayed until the  user code 
+ * needs the information.
+ *
+ * Processing is delegated to modules, using a hook mechanism.
+ * 
+ * This class is not intended for user code - it is used internally by tomcat
+ * for processing the request in the most efficient way. Users ( servlets ) can
+ * access the information using a facade, which provides the high-level view
+ * of the request.
+ *
+ * For lazy evaluation, the request uses the getInfo() hook. The following ids
+ * are defined:
+ * <ul>
+ *  <li>req.encoding - returns the request encoding
+ *  <li>req.attribute - returns a module-specific attribute ( like SSL keys, etc ).
+ * </ul>
+ *
+ * Tomcat defines a number of attributes:
+ * <ul>
+ *   <li>"org.apache.tomcat.request" - allows access to the low-level
+ *       request object in trusted applications 
+ * </ul>
+ *
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Jason Hunter [jch@eng.sun.com]
+ * @author Harish Prabandham
+ * @author Alex Cruikshank [alex@epitonic.com]
+ * @author Hans Bergsten [hans@gefionsoftware.com]
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ */
+public final class Request {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public Request() {
+
+        parameters.setQuery(queryMB);
+        parameters.setURLDecoder(urlDecoder);
+        parameters.setHeaders(headers);
+
+        schemeMB.setString("http");
+        methodMB.setString("GET");
+        uriMB.setString("/");
+        queryMB.setString("");
+        protoMB.setString("HTTP/1.0");
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    private int serverPort = -1;
+    private MessageBytes serverNameMB = MessageBytes.newInstance();
+
+    private String localHost;
+    
+    private int remotePort;
+    private int localPort;
+
+    private MessageBytes schemeMB = MessageBytes.newInstance();
+
+    private MessageBytes methodMB = MessageBytes.newInstance();
+    private MessageBytes unparsedURIMB = MessageBytes.newInstance();
+    private MessageBytes uriMB = MessageBytes.newInstance();
+    private MessageBytes decodedUriMB = MessageBytes.newInstance();
+    private MessageBytes queryMB = MessageBytes.newInstance();
+    private MessageBytes protoMB = MessageBytes.newInstance();
+
+    // remote address/host
+    private MessageBytes remoteAddrMB = MessageBytes.newInstance();
+    private MessageBytes localNameMB = MessageBytes.newInstance();
+    private MessageBytes remoteHostMB = MessageBytes.newInstance();
+    private MessageBytes localAddrMB = MessageBytes.newInstance();
+     
+    private MimeHeaders headers = new MimeHeaders();
+
+    private MessageBytes instanceId = MessageBytes.newInstance();
+
+    /**
+     * Notes.
+     */
+    private Object notes[] = new Object[Constants.MAX_NOTES];
+
+
+    /**
+     * Associated input buffer.
+     */
+    private InputBuffer inputBuffer = null;
+
+
+    /**
+     * URL decoder.
+     */
+    private UDecoder urlDecoder = new UDecoder();
+
+
+    /**
+     * HTTP specific fields. (remove them ?)
+     */
+    private long contentLength = -1;
+    private MessageBytes contentTypeMB = null;
+    private String charEncoding = null;
+    private Cookies cookies = new Cookies(headers);
+    private Parameters parameters = new Parameters();
+
+    private MessageBytes remoteUser=MessageBytes.newInstance();
+    private MessageBytes authType=MessageBytes.newInstance();
+    private HashMap attributes=new HashMap();
+
+    private Response response;
+    private ActionHook hook;
+
+    private int bytesRead=0;
+    // Time of the request - usefull to avoid repeated calls to System.currentTime
+    private long startTime = 0L;
+
+    private RequestInfo reqProcessorMX=new RequestInfo(this);
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Get the instance id (or JVM route). Curently Ajp is sending it with each
+     * request. In future this should be fixed, and sent only once ( or
+     * 'negociated' at config time so both tomcat and apache share the same name.
+     * 
+     * @return the instance id
+     */
+    public MessageBytes instanceId() {
+        return instanceId;
+    }
+
+
+    public MimeHeaders getMimeHeaders() {
+        return headers;
+    }
+
+
+    public UDecoder getURLDecoder() {
+        return urlDecoder;
+    }
+
+    // -------------------- Request data --------------------
+
+
+    public MessageBytes scheme() {
+        return schemeMB;
+    }
+    
+    public MessageBytes method() {
+        return methodMB;
+    }
+    
+    public MessageBytes unparsedURI() {
+        return unparsedURIMB;
+    }
+
+    public MessageBytes requestURI() {
+        return uriMB;
+    }
+
+    public MessageBytes decodedURI() {
+        return decodedUriMB;
+    }
+
+    public MessageBytes query() {
+        return queryMB;
+    }
+
+    public MessageBytes queryString() {
+        return queryMB;
+    }
+
+    public MessageBytes protocol() {
+        return protoMB;
+    }
+    
+    /** 
+     * Return the buffer holding the server name, if
+     * any. Use isNull() to check if there is no value
+     * set.
+     * This is the "virtual host", derived from the
+     * Host: header.
+     */
+    public MessageBytes serverName() {
+        return serverNameMB;
+    }
+
+    public int getServerPort() {
+        return serverPort;
+    }
+    
+    public void setServerPort(int serverPort ) {
+        this.serverPort=serverPort;
+    }
+
+    public MessageBytes remoteAddr() {
+        return remoteAddrMB;
+    }
+
+    public MessageBytes remoteHost() {
+        return remoteHostMB;
+    }
+
+    public MessageBytes localName() {
+        return localNameMB;
+    }    
+
+    public MessageBytes localAddr() {
+        return localAddrMB;
+    }
+    
+    public String getLocalHost() {
+        return localHost;
+    }
+
+    public void setLocalHost(String host) {
+        this.localHost = host;
+    }    
+    
+    public int getRemotePort(){
+        return remotePort;
+    }
+        
+    public void setRemotePort(int port){
+        this.remotePort = port;
+    }
+    
+    public int getLocalPort(){
+        return localPort;
+    }
+        
+    public void setLocalPort(int port){
+        this.localPort = port;
+    }
+
+    // -------------------- encoding/type --------------------
+
+
+    /**
+     * Get the character encoding used for this request.
+     */
+    public String getCharacterEncoding() {
+
+        if (charEncoding != null)
+            return charEncoding;
+
+        charEncoding = ContentType.getCharsetFromContentType(getContentType());
+        return charEncoding;
+
+    }
+
+
+    public void setCharacterEncoding(String enc) {
+        this.charEncoding = enc;
+    }
+
+
+    public void setContentLength(int len) {
+        this.contentLength = len;
+    }
+
+
+    public int getContentLength() {
+        long length = getContentLengthLong();
+
+        if (length < Integer.MAX_VALUE) {
+            return (int) length;
+        }
+        return -1;
+    }
+
+    public long getContentLengthLong() {
+        if( contentLength > -1 ) return contentLength;
+
+        MessageBytes clB = headers.getValue("content-length");
+        contentLength = (clB == null || clB.isNull()) ? -1 : clB.getLong();
+
+        return contentLength;
+    }
+
+    public String getContentType() {
+        contentType();
+        if ((contentTypeMB == null) || contentTypeMB.isNull()) 
+            return null;
+        return contentTypeMB.toString();
+    }
+
+
+    public void setContentType(String type) {
+        contentTypeMB.setString(type);
+    }
+
+
+    public MessageBytes contentType() {
+        if (contentTypeMB == null)
+            contentTypeMB = headers.getValue("content-type");
+        return contentTypeMB;
+    }
+
+
+    public void setContentType(MessageBytes mb) {
+        contentTypeMB=mb;
+    }
+
+
+    public String getHeader(String name) {
+        return headers.getHeader(name);
+    }
+
+    // -------------------- Associated response --------------------
+
+    public Response getResponse() {
+        return response;
+    }
+
+    public void setResponse( Response response ) {
+        this.response=response;
+        response.setRequest( this );
+    }
+    
+    public void action(ActionCode actionCode, Object param) {
+        if( hook==null && response!=null )
+            hook=response.getHook();
+        
+        if (hook != null) {
+            if( param==null ) 
+                hook.action(actionCode, this);
+            else
+                hook.action(actionCode, param);
+        }
+    }
+
+
+    // -------------------- Cookies --------------------
+
+
+    public Cookies getCookies() {
+        return cookies;
+    }
+
+
+    // -------------------- Parameters --------------------
+
+
+    public Parameters getParameters() {
+        return parameters;
+    }
+
+
+    // -------------------- Other attributes --------------------
+    // We can use notes for most - need to discuss what is of general interest
+    
+    public void setAttribute( String name, Object o ) {
+        attributes.put( name, o );
+    }
+
+    public HashMap getAttributes() {
+        return attributes;
+    }
+
+    public Object getAttribute(String name ) {
+        return attributes.get(name);
+    }
+    
+    public MessageBytes getRemoteUser() {
+        return remoteUser;
+    }
+
+    public MessageBytes getAuthType() {
+        return authType;
+    }
+
+    // -------------------- Input Buffer --------------------
+
+
+    public InputBuffer getInputBuffer() {
+        return inputBuffer;
+    }
+
+
+    public void setInputBuffer(InputBuffer inputBuffer) {
+        this.inputBuffer = inputBuffer;
+    }
+
+
+    /**
+     * Read data from the input buffer and put it into a byte chunk.
+     */
+    public int doRead(ByteChunk chunk) 
+        throws IOException {
+        int n = inputBuffer.doRead(chunk, this);
+        if (n > 0) {
+            bytesRead+=n;
+        }
+        return n;
+    }
+
+
+    // -------------------- debug --------------------
+
+    public String toString() {
+        return "R( " + requestURI().toString() + ")";
+    }
+
+    public long getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(long startTime) {
+        this.startTime = startTime;
+    }
+
+    // -------------------- Per-Request "notes" --------------------
+
+
+    public final void setNote(int pos, Object value) {
+        notes[pos] = value;
+    }
+
+
+    public final Object getNote(int pos) {
+        return notes[pos];
+    }
+
+
+    // -------------------- Recycling -------------------- 
+
+
+    public void recycle() {
+        bytesRead=0;
+
+        contentLength = -1;
+        contentTypeMB = null;
+        charEncoding = null;
+        headers.recycle();
+        serverNameMB.recycle();
+        serverPort=-1;
+        localPort = -1;
+        remotePort = -1;
+
+        cookies.recycle();
+        parameters.recycle();
+
+        unparsedURIMB.recycle();
+        uriMB.recycle(); 
+        decodedUriMB.recycle();
+        queryMB.recycle();
+        methodMB.recycle();
+        protoMB.recycle();
+        //remoteAddrMB.recycle();
+        //remoteHostMB.recycle();
+
+        // XXX Do we need such defaults ?
+        schemeMB.recycle();
+        methodMB.setString("GET");
+        uriMB.setString("/");
+        queryMB.setString("");
+        protoMB.setString("HTTP/1.0");
+        //remoteAddrMB.setString("127.0.0.1");
+        //remoteHostMB.setString("localhost");
+
+        instanceId.recycle();
+        remoteUser.recycle();
+        authType.recycle();
+        attributes.clear();
+    }
+
+    // -------------------- Info  --------------------
+    public void updateCounters() {
+        reqProcessorMX.updateCounters();
+    }
+
+    public RequestInfo getRequestProcessor() {
+        return reqProcessorMX;
+    }
+
+    public int getBytesRead() {
+        return bytesRead;
+    }
+
+    public void setBytesRead(int bytesRead) {
+        this.bytesRead = bytesRead;
+    }
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/RequestGroupInfo.java b/connectors/coyote/src/java/org/apache/coyote/RequestGroupInfo.java
new file mode 100644
index 0000000..b8810b6
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/RequestGroupInfo.java
@@ -0,0 +1,163 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote;
+
+import java.util.ArrayList;
+
+/** This can be moved to top level ( eventually with a better name ).
+ *  It is currently used only as a JMX artifact, to agregate the data
+ *  collected from each RequestProcessor thread.
+ */
+public class RequestGroupInfo {
+    ArrayList processors=new ArrayList();
+    private long deadMaxTime = 0;
+    private long deadProcessingTime = 0;
+    private int deadRequestCount = 0;
+    private int deadErrorCount = 0;
+    private long deadBytesReceived = 0;
+    private long deadBytesSent = 0;
+
+    public synchronized void addRequestProcessor( RequestInfo rp ) {
+        processors.add( rp );
+    }
+
+    public synchronized void removeRequestProcessor( RequestInfo rp ) {
+        if( rp != null ) {
+            if( deadMaxTime < rp.getMaxTime() )
+                deadMaxTime = rp.getMaxTime();
+            deadProcessingTime += rp.getProcessingTime();
+            deadRequestCount += rp.getRequestCount();
+            deadErrorCount += rp.getErrorCount();
+            deadBytesReceived += rp.getBytesReceived();
+            deadBytesSent += rp.getBytesSent();
+
+            processors.remove( rp );
+        }
+    }
+
+    public synchronized long getMaxTime() {
+        long maxTime=deadMaxTime;
+        for( int i=0; i<processors.size(); i++ ) {
+            RequestInfo rp=(RequestInfo)processors.get( i );
+            if( maxTime < rp.getMaxTime() ) maxTime=rp.getMaxTime();
+        }
+        return maxTime;
+    }
+
+    // Used to reset the times
+    public synchronized void setMaxTime(long maxTime) {
+        deadMaxTime = maxTime;
+        for( int i=0; i<processors.size(); i++ ) {
+            RequestInfo rp=(RequestInfo)processors.get( i );
+            rp.setMaxTime(maxTime);
+        }
+    }
+
+    public synchronized long getProcessingTime() {
+        long time=deadProcessingTime;
+        for( int i=0; i<processors.size(); i++ ) {
+            RequestInfo rp=(RequestInfo)processors.get( i );
+            time += rp.getProcessingTime();
+        }
+        return time;
+    }
+
+    public synchronized void setProcessingTime(long totalTime) {
+        deadProcessingTime = totalTime;
+        for( int i=0; i<processors.size(); i++ ) {
+            RequestInfo rp=(RequestInfo)processors.get( i );
+            rp.setProcessingTime( totalTime );
+        }
+    }
+
+    public synchronized int getRequestCount() {
+        int requestCount=deadRequestCount;
+        for( int i=0; i<processors.size(); i++ ) {
+            RequestInfo rp=(RequestInfo)processors.get( i );
+            requestCount += rp.getRequestCount();
+        }
+        return requestCount;
+    }
+
+    public synchronized void setRequestCount(int requestCount) {
+        deadRequestCount = requestCount;
+        for( int i=0; i<processors.size(); i++ ) {
+            RequestInfo rp=(RequestInfo)processors.get( i );
+            rp.setRequestCount( requestCount );
+        }
+    }
+
+    public synchronized int getErrorCount() {
+        int requestCount=deadErrorCount;
+        for( int i=0; i<processors.size(); i++ ) {
+            RequestInfo rp=(RequestInfo)processors.get( i );
+            requestCount += rp.getErrorCount();
+        }
+        return requestCount;
+    }
+
+    public synchronized void setErrorCount(int errorCount) {
+        deadErrorCount = errorCount;
+        for( int i=0; i<processors.size(); i++ ) {
+            RequestInfo rp=(RequestInfo)processors.get( i );
+            rp.setErrorCount( errorCount);
+        }
+    }
+
+    public synchronized long getBytesReceived() {
+        long bytes=deadBytesReceived;
+        for( int i=0; i<processors.size(); i++ ) {
+            RequestInfo rp=(RequestInfo)processors.get( i );
+            bytes += rp.getBytesReceived();
+        }
+        return bytes;
+    }
+
+    public synchronized void setBytesReceived(long bytesReceived) {
+        deadBytesReceived = bytesReceived;
+        for( int i=0; i<processors.size(); i++ ) {
+            RequestInfo rp=(RequestInfo)processors.get( i );
+            rp.setBytesReceived( bytesReceived );
+        }
+    }
+
+    public synchronized long getBytesSent() {
+        long bytes=deadBytesSent;
+        for( int i=0; i<processors.size(); i++ ) {
+            RequestInfo rp=(RequestInfo)processors.get( i );
+            bytes += rp.getBytesSent();
+        }
+        return bytes;
+    }
+
+    public synchronized void setBytesSent(long bytesSent) {
+        deadBytesSent = bytesSent;
+        for( int i=0; i<processors.size(); i++ ) {
+            RequestInfo rp=(RequestInfo)processors.get( i );
+            rp.setBytesSent( bytesSent );
+        }
+    }
+
+    public void resetCounters() {
+        this.setBytesReceived(0);
+        this.setBytesSent(0);
+        this.setRequestCount(0);
+        this.setProcessingTime(0);
+        this.setMaxTime(0);
+        this.setErrorCount(0);
+    }
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/RequestInfo.java b/connectors/coyote/src/java/org/apache/coyote/RequestInfo.java
new file mode 100644
index 0000000..5728842
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/RequestInfo.java
@@ -0,0 +1,215 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote;
+
+
+/**
+ * Structure holding the Request and Response objects. It also holds statistical
+ * informations about request processing and provide management informations
+ * about the requests beeing processed.
+ *
+ * Each thread uses a Request/Response pair that is recycled on each request.
+ * This object provides a place to collect global low-level statistics - without
+ * having to deal with synchronization ( since each thread will have it's own
+ * RequestProcessorMX ).
+ *
+ * TODO: Request notifications will be registered here.
+ *
+ * @author Costin Manolache
+ */
+public class RequestInfo  {
+    RequestGroupInfo global=null;
+
+    // ----------------------------------------------------------- Constructors
+
+    public RequestInfo( Request req) {
+        this.req=req;
+    }
+
+    public RequestGroupInfo getGlobalProcessor() {
+        return global;
+    }
+    
+    public void setGlobalProcessor(RequestGroupInfo global) {
+        if( global != null) {
+            this.global=global;
+            global.addRequestProcessor( this );
+        } else {
+        	if (this.global != null) {
+                this.global.removeRequestProcessor( this ); 
+                this.global = null;
+            }
+        }
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+    Request req;
+    Response res;
+    int stage = Constants.STAGE_NEW;
+
+    // -------------------- Information about the current request  -----------
+    // This is usefull for long-running requests only
+
+    public String getMethod() {
+        return req.method().toString();
+    }
+
+    public String getCurrentUri() {
+        return req.requestURI().toString();
+    }
+
+    public String getCurrentQueryString() {
+        return req.queryString().toString();
+    }
+
+    public String getProtocol() {
+        return req.protocol().toString();
+    }
+
+    public String getVirtualHost() {
+        return req.serverName().toString();
+    }
+
+    public int getServerPort() {
+        return req.getServerPort();
+    }
+
+    public String getRemoteAddr() {
+        req.action(ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE, null);
+        return req.remoteAddr().toString();
+    }
+
+    public int getContentLength() {
+        return req.getContentLength();
+    }
+
+    public long getRequestBytesReceived() {
+        return req.getBytesRead();
+    }
+
+    public long getRequestBytesSent() {
+        return req.getResponse().getBytesWritten();
+    }
+
+    public long getRequestProcessingTime() {
+        return (System.currentTimeMillis() - req.getStartTime());
+    }
+
+    // -------------------- Statistical data  --------------------
+    // Collected at the end of each request.
+    private long bytesSent;
+    private long bytesReceived;
+
+    // Total time = divide by requestCount to get average.
+    private long processingTime;
+    // The longest response time for a request
+    private long maxTime;
+    // URI of the request that took maxTime
+    private String maxRequestUri;
+
+    private int requestCount;
+    // number of response codes >= 400
+    private int errorCount;
+
+
+    /** Called by the processor before recycling the request. It'll collect
+     * statistic information.
+     */
+    void updateCounters() {
+        bytesReceived+=req.getBytesRead();
+        bytesSent+=req.getResponse().getBytesWritten();
+
+        requestCount++;
+        if( req.getResponse().getStatus() >=400 )
+            errorCount++;
+        long t0=req.getStartTime();
+        long t1=System.currentTimeMillis();
+        long time=t1-t0;
+        processingTime+=time;
+        if( maxTime < time ) {
+            maxTime=time;
+            maxRequestUri=req.requestURI().toString();
+        }
+    }
+
+    public int getStage() {
+        return stage;
+    }
+
+    public void setStage(int stage) {
+        this.stage = stage;
+    }
+
+    public long getBytesSent() {
+        return bytesSent;
+    }
+
+    public void setBytesSent(long bytesSent) {
+        this.bytesSent = bytesSent;
+    }
+
+    public long getBytesReceived() {
+        return bytesReceived;
+    }
+
+    public void setBytesReceived(long bytesReceived) {
+        this.bytesReceived = bytesReceived;
+    }
+
+    public long getProcessingTime() {
+        return processingTime;
+    }
+
+    public void setProcessingTime(long processingTime) {
+        this.processingTime = processingTime;
+    }
+
+    public long getMaxTime() {
+        return maxTime;
+    }
+
+    public void setMaxTime(long maxTime) {
+        this.maxTime = maxTime;
+    }
+
+    public String getMaxRequestUri() {
+        return maxRequestUri;
+    }
+
+    public void setMaxRequestUri(String maxRequestUri) {
+        this.maxRequestUri = maxRequestUri;
+    }
+
+    public int getRequestCount() {
+        return requestCount;
+    }
+
+    public void setRequestCount(int requestCount) {
+        this.requestCount = requestCount;
+    }
+
+    public int getErrorCount() {
+        return errorCount;
+    }
+
+    public void setErrorCount(int errorCount) {
+        this.errorCount = errorCount;
+    }
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/Response.java b/connectors/coyote/src/java/org/apache/coyote/Response.java
new file mode 100644
index 0000000..100835c
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/Response.java
@@ -0,0 +1,574 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote;
+
+import java.io.IOException;
+import java.util.Locale;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.http.MimeHeaders;
+
+/**
+ * Response object.
+ * 
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author Jason Hunter [jch@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Harish Prabandham
+ * @author Hans Bergsten <hans@gefionsoftware.com>
+ * @author Remy Maucherat
+ */
+public final class Response {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public Response() {
+    }
+
+
+    // ----------------------------------------------------- Class Variables
+
+    /**
+     * Default locale as mandated by the spec.
+     */
+    private static Locale DEFAULT_LOCALE = Locale.getDefault();
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Status code.
+     */
+    protected int status = 200;
+
+
+    /**
+     * Status message.
+     */
+    protected String message = null;
+
+
+    /**
+     * Response headers.
+     */
+    protected MimeHeaders headers = new MimeHeaders();
+
+
+    /**
+     * Associated output buffer.
+     */
+    protected OutputBuffer outputBuffer;
+
+
+    /**
+     * Notes.
+     */
+    protected Object notes[] = new Object[Constants.MAX_NOTES];
+
+
+    /**
+     * Committed flag.
+     */
+    protected boolean commited = false;
+
+
+    /**
+     * Action hook.
+     */
+    public ActionHook hook;
+
+
+    /**
+     * HTTP specific fields.
+     */
+    protected String contentType = null;
+    protected String contentLanguage = null;
+    protected String characterEncoding = Constants.DEFAULT_CHARACTER_ENCODING;
+    protected int contentLength = -1;
+    private Locale locale = DEFAULT_LOCALE;
+
+    // General informations
+    private long bytesWritten=0;
+
+    /**
+     * Holds request error exception.
+     */
+    protected Exception errorException = null;
+
+    /**
+     * Has the charset been explicitly set.
+     */
+    protected boolean charsetSet = false;
+
+    /**
+     * Request error URI.
+     */
+    protected String errorURI = null;
+
+    protected Request req;
+
+    // ------------------------------------------------------------- Properties
+
+    public Request getRequest() {
+        return req;
+    }
+
+    public void setRequest( Request req ) {
+        this.req=req;
+    }
+
+    public OutputBuffer getOutputBuffer() {
+        return outputBuffer;
+    }
+
+
+    public void setOutputBuffer(OutputBuffer outputBuffer) {
+        this.outputBuffer = outputBuffer;
+    }
+
+
+    public MimeHeaders getMimeHeaders() {
+        return headers;
+    }
+
+
+    public ActionHook getHook() {
+        return hook;
+    }
+
+
+    public void setHook(ActionHook hook) {
+        this.hook = hook;
+    }
+
+
+    // -------------------- Per-Response "notes" --------------------
+
+
+    public final void setNote(int pos, Object value) {
+        notes[pos] = value;
+    }
+
+
+    public final Object getNote(int pos) {
+        return notes[pos];
+    }
+
+
+    // -------------------- Actions --------------------
+
+
+    public void action(ActionCode actionCode, Object param) {
+        if (hook != null) {
+            if( param==null ) 
+                hook.action(actionCode, this);
+            else
+                hook.action(actionCode, param);
+        }
+    }
+
+
+    // -------------------- State --------------------
+
+
+    public int getStatus() {
+        return status;
+    }
+
+    
+    /** 
+     * Set the response status 
+     */ 
+    public void setStatus( int status ) {
+        this.status = status;
+    }
+
+
+    /**
+     * Get the status message.
+     */
+    public String getMessage() {
+        return message;
+    }
+
+
+    /**
+     * Set the status message.
+     */
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+
+    public boolean isCommitted() {
+        return commited;
+    }
+
+
+    public void setCommitted(boolean v) {
+        this.commited = v;
+    }
+
+
+    // -----------------Error State --------------------
+
+
+    /** 
+     * Set the error Exception that occurred during
+     * request processing.
+     */
+    public void setErrorException(Exception ex) {
+    errorException = ex;
+    }
+
+
+    /** 
+     * Get the Exception that occurred during request
+     * processing.
+     */
+    public Exception getErrorException() {
+        return errorException;
+    }
+
+
+    public boolean isExceptionPresent() {
+        return ( errorException != null );
+    }
+
+
+    /** 
+     * Set request URI that caused an error during
+     * request processing.
+     */
+    public void setErrorURI(String uri) {
+        errorURI = uri;
+    }
+
+
+    /** Get the request URI that caused the original error.
+     */
+    public String getErrorURI() {
+        return errorURI;
+    }
+
+
+    // -------------------- Methods --------------------
+    
+    
+    public void reset() 
+        throws IllegalStateException {
+        
+        // Reset the headers only if this is the main request,
+        // not for included
+        contentType = null;;
+        locale = DEFAULT_LOCALE;
+        contentLanguage = null;
+        characterEncoding = Constants.DEFAULT_CHARACTER_ENCODING;
+        contentLength = -1;
+        charsetSet = false;
+
+        status = 200;
+        message = null;
+        headers.clear();
+        
+        // Force the PrintWriter to flush its data to the output
+        // stream before resetting the output stream
+        //
+        // Reset the stream
+        if (commited) {
+            //String msg = sm.getString("servletOutputStreamImpl.reset.ise"); 
+            throw new IllegalStateException();
+        }
+        
+        action(ActionCode.ACTION_RESET, this);
+    }
+
+
+    public void finish() throws IOException {
+        action(ActionCode.ACTION_CLOSE, this);
+    }
+
+
+    public void acknowledge() throws IOException {
+        action(ActionCode.ACTION_ACK, this);
+    }
+
+
+    // -------------------- Headers --------------------
+    public boolean containsHeader(String name) {
+        return headers.getHeader(name) != null;
+    }
+
+
+    public void setHeader(String name, String value) {
+        char cc=name.charAt(0);
+        if( cc=='C' || cc=='c' ) {
+            if( checkSpecialHeader(name, value) )
+            return;
+        }
+        headers.setValue(name).setString( value);
+    }
+
+
+    public void addHeader(String name, String value) {
+        char cc=name.charAt(0);
+        if( cc=='C' || cc=='c' ) {
+            if( checkSpecialHeader(name, value) )
+            return;
+        }
+        headers.addValue(name).setString( value );
+    }
+
+    
+    /** 
+     * Set internal fields for special header names. 
+     * Called from set/addHeader.
+     * Return true if the header is special, no need to set the header.
+     */
+    private boolean checkSpecialHeader( String name, String value) {
+        // XXX Eliminate redundant fields !!!
+        // ( both header and in special fields )
+        if( name.equalsIgnoreCase( "Content-Type" ) ) {
+            setContentType( value );
+            return true;
+        }
+        if( name.equalsIgnoreCase( "Content-Length" ) ) {
+            try {
+                int cL=Integer.parseInt( value );
+                setContentLength( cL );
+                return true;
+            } catch( NumberFormatException ex ) {
+                // Do nothing - the spec doesn't have any "throws" 
+                // and the user might know what he's doing
+                return false;
+            }
+        }
+        if( name.equalsIgnoreCase( "Content-Language" ) ) {
+            // XXX XXX Need to construct Locale or something else
+        }
+        return false;
+    }
+
+
+    /** Signal that we're done with the headers, and body will follow.
+     *  Any implementation needs to notify ContextManager, to allow
+     *  interceptors to fix headers.
+     */
+    public void sendHeaders() throws IOException {
+        action(ActionCode.ACTION_COMMIT, this);
+        commited = true;
+    }
+
+
+    // -------------------- I18N --------------------
+
+
+    public Locale getLocale() {
+        return locale;
+    }
+
+    /**
+     * Called explicitely by user to set the Content-Language and
+     * the default encoding
+     */
+    public void setLocale(Locale locale) {
+
+        if (locale == null) {
+            return;  // throw an exception?
+        }
+
+        // Save the locale for use by getLocale()
+        this.locale = locale;
+
+        // Set the contentLanguage for header output
+        contentLanguage = locale.getLanguage();
+        if ((contentLanguage != null) && (contentLanguage.length() > 0)) {
+            String country = locale.getCountry();
+            StringBuffer value = new StringBuffer(contentLanguage);
+            if ((country != null) && (country.length() > 0)) {
+                value.append('-');
+                value.append(country);
+            }
+            contentLanguage = value.toString();
+        }
+
+    }
+
+    /**
+     * Return the content language.
+     */
+    public String getContentLanguage() {
+        return contentLanguage;
+    }
+
+    /*
+     * Overrides the name of the character encoding used in the body
+     * of the response. This method must be called prior to writing output
+     * using getWriter().
+     *
+     * @param charset String containing the name of the chararacter encoding.
+     */
+    public void setCharacterEncoding(String charset) {
+
+        if (isCommitted())
+            return;
+        if (charset == null)
+            return;
+
+        characterEncoding = charset;
+        charsetSet=true;
+    }
+
+    public String getCharacterEncoding() {
+        return characterEncoding;
+    }
+
+    /**
+     * Sets the content type.
+     *
+     * This method must preserve any response charset that may already have 
+     * been set via a call to response.setContentType(), response.setLocale(),
+     * or response.setCharacterEncoding().
+     *
+     * @param type the content type
+     */
+    public void setContentType(String type) {
+
+        int semicolonIndex = -1;
+
+        if (type == null) {
+            this.contentType = null;
+            return;
+        }
+
+        /*
+         * Remove the charset param (if any) from the Content-Type, and use it
+         * to set the response encoding.
+         * The most recent response encoding setting will be appended to the
+         * response's Content-Type (as its charset param) by getContentType();
+         */
+        boolean hasCharset = false;
+        int len = type.length();
+        int index = type.indexOf(';');
+        while (index != -1) {
+            semicolonIndex = index;
+            index++;
+            while (index < len && Character.isSpace(type.charAt(index))) {
+                index++;
+            }
+            if (index+8 < len
+                    && type.charAt(index) == 'c'
+                    && type.charAt(index+1) == 'h'
+                    && type.charAt(index+2) == 'a'
+                    && type.charAt(index+3) == 'r'
+                    && type.charAt(index+4) == 's'
+                    && type.charAt(index+5) == 'e'
+                    && type.charAt(index+6) == 't'
+                    && type.charAt(index+7) == '=') {
+                hasCharset = true;
+                break;
+            }
+            index = type.indexOf(';', index);
+        }
+
+        if (!hasCharset) {
+            this.contentType = type;
+            return;
+        }
+
+        this.contentType = type.substring(0, semicolonIndex);
+        String tail = type.substring(index+8);
+        int nextParam = tail.indexOf(';');
+        String charsetValue = null;
+        if (nextParam != -1) {
+            this.contentType += tail.substring(nextParam);
+            charsetValue = tail.substring(0, nextParam);
+        } else {
+            charsetValue = tail;
+        }
+
+        // The charset value may be quoted, but must not contain any quotes.
+        if (charsetValue != null && charsetValue.length() > 0) {
+            charsetSet=true;
+            charsetValue = charsetValue.replace('"', ' ');
+            this.characterEncoding = charsetValue.trim();
+        }
+    }
+
+    public String getContentType() {
+
+        String ret = contentType;
+
+        if (ret != null 
+            && characterEncoding != null
+            && charsetSet) {
+            ret = ret + ";charset=" + characterEncoding;
+        }
+
+        return ret;
+    }
+    
+    public void setContentLength(int contentLength) {
+        this.contentLength = contentLength;
+    }
+
+    public int getContentLength() {
+        return contentLength;
+    }
+
+
+    /** 
+     * Write a chunk of bytes.
+     */
+    public void doWrite(ByteChunk chunk/*byte buffer[], int pos, int count*/)
+        throws IOException
+    {
+        outputBuffer.doWrite(chunk, this);
+        bytesWritten+=chunk.getLength();
+    }
+
+    // --------------------
+    
+    public void recycle() {
+        
+        contentType = null;
+        contentLanguage = null;
+        locale = DEFAULT_LOCALE;
+        characterEncoding = Constants.DEFAULT_CHARACTER_ENCODING;
+        charsetSet = false;
+        contentLength = -1;
+        status = 200;
+        message = null;
+        commited = false;
+        errorException = null;
+        errorURI = null;
+        headers.clear();
+
+        // update counters
+        bytesWritten=0;
+    }
+
+    public long getBytesWritten() {
+        return bytesWritten;
+    }
+
+    public void setBytesWritten(long bytesWritten) {
+        this.bytesWritten = bytesWritten;
+    }
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/memory/MemoryProtocolHandler.java b/connectors/coyote/src/java/org/apache/coyote/memory/MemoryProtocolHandler.java
new file mode 100644
index 0000000..ed0d518
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/memory/MemoryProtocolHandler.java
@@ -0,0 +1,166 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.memory;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+
+import org.apache.coyote.Adapter;
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.ProtocolHandler;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+
+
+/**
+ * Abstract the protocol implementation, including threading, etc.
+ * Processor is single threaded and specific to stream-based protocols,
+ * will not fit Jk protocols like JNI.
+ *
+ * @author Remy Maucherat
+ */
+public class MemoryProtocolHandler
+    implements ProtocolHandler {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Pass config info.
+     */
+    public void setAttribute(String name, Object value) {
+    }
+
+    public Object getAttribute(String name) {
+        return null;
+    }
+
+
+    /**
+     * Associated adapter.
+     */
+    protected Adapter adapter = null;
+
+    /**
+     * The adapter, used to call the connector.
+     */
+    public void setAdapter(Adapter adapter) {
+        this.adapter = adapter;
+    }
+
+    public Adapter getAdapter() {
+        return (adapter);
+    }
+
+
+    // ------------------------------------------------ ProtocolHandler Methods
+
+
+    /**
+     * Init the protocol.
+     */
+    public void init()
+        throws Exception {
+    }
+
+
+    /**
+     * Start the protocol.
+     */
+    public void start()
+        throws Exception {
+    }
+
+
+    public void pause() 
+        throws Exception {
+    }
+
+    public void resume() 
+        throws Exception {
+    }
+
+    public void destroy()
+        throws Exception {
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process specified request.
+     */
+    public void process(Request request, ByteChunk input,
+                        Response response, ByteChunk output)
+        throws Exception {
+
+        InputBuffer inputBuffer = new ByteChunkInputBuffer(input);
+        OutputBuffer outputBuffer = new ByteChunkOutputBuffer(output);
+        request.setInputBuffer(inputBuffer);
+        response.setOutputBuffer(outputBuffer);
+
+        adapter.service(request, response);
+
+    }
+
+
+    // --------------------------------------------- ByteChunkInputBuffer Class
+
+
+    protected class ByteChunkInputBuffer
+        implements InputBuffer {
+
+        protected ByteChunk input = null;
+
+        public ByteChunkInputBuffer(ByteChunk input) {
+            this.input = input;
+        }
+
+        public int doRead(ByteChunk chunk, Request request) 
+            throws IOException {
+            return input.substract(chunk);
+        }
+
+    }
+
+
+    // -------------------------------------------- ByteChunkOuptutBuffer Class
+
+
+    protected class ByteChunkOutputBuffer
+        implements OutputBuffer {
+
+        protected ByteChunk output = null;
+
+        public ByteChunkOutputBuffer(ByteChunk output) {
+            this.output = output;
+        }
+
+        public int doWrite(ByteChunk chunk, Response response) 
+            throws IOException {
+            output.append(chunk);
+            return chunk.getLength();
+        }
+
+    }
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat3/CoyoteInterceptor2.java b/connectors/coyote/src/java/org/apache/coyote/tomcat3/CoyoteInterceptor2.java
new file mode 100644
index 0000000..a3f4386
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat3/CoyoteInterceptor2.java
@@ -0,0 +1,257 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat3;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.ProtocolHandler;
+import org.apache.tomcat.core.BaseInterceptor;
+import org.apache.tomcat.core.Context;
+import org.apache.tomcat.core.ContextManager;
+import org.apache.tomcat.core.TomcatException;
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.tomcat.util.net.SSLSupport;
+
+/** Standalone http.
+ *
+ *  Connector properties:
+ *  <ul>
+ *  <li> secure - will load a SSL socket factory and act as https server</li>
+ *  </ul>
+ *
+ *  Properties passed to the net layer:
+ *  <ul>
+ *  <li>timeout</li>
+ *  <li>backlog</li>
+ *  <li>address</li>
+ *  <li>port</li>
+ *  </ul>
+ * Thread pool properties:
+ *  <ul>
+ *  <li>minSpareThreads</li>
+ *  <li>maxSpareThreads</li>
+ *  <li>maxThreads</li>
+ *  <li>poolOn</li>
+ *  </ul>
+ * Properties for HTTPS:
+ *  <ul>
+ *  <li>keystore - certificates - default to ~/.keystore</li>
+ *  <li>keypass - password</li>
+ *  <li>clientauth - true if the server should authenticate the client using certs</li>
+ *  </ul>
+ * Properties for HTTP:
+ *  <ul>
+ *  <li>reportedname - name of server sent back to browser (security purposes)</li>
+ *  </ul>
+ *  <ul>
+ *  <li>compression - use gzip compression in HTTP 1.1 (on/off) - def off</li>
+ *  </ul>
+ *  <ul>
+ *  <li>compressionMinSize - minimum size content to use gzip compression in HTTP 1.1 - def 2048</li>
+ *  </ul>
+ *  <ul>
+ *  <li>noCompressionUserAgents - comma separated list of userAgents who didn't support gzip</li>
+ *  </ul>
+ *  <ul>
+ *  <li>restrictedUserAgents - comma separated list of userAgents who didn't support HTTP 1.1 (use HTTP 1.0)</li>
+ *  </ul>
+ *  <ul>
+ *  <li>compressableMimeTypes - comma separated list of mime types supported for compression - def text/html,text/xml,text/plain</li>
+ *  </ul>
+ */
+public class CoyoteInterceptor2 extends BaseInterceptor
+{
+    private String processorClassName="org.apache.coyote.http11.Http11Protocol";
+    Tomcat3Adapter adapter;
+    ProtocolHandler proto;
+    int protocolNote;
+    
+    public CoyoteInterceptor2() {
+	super();
+	// defaults:
+        this.setAttribute( "port", "8080" );
+        this.setAttribute( "soLinger", "-1" );
+    }
+
+    // -------------------- PoolTcpConnector --------------------
+
+    /** Set the class of the processor to use.
+     */
+    public void setProcessorClassName(String pcn) {
+	processorClassName = pcn;
+    }
+
+    // -------------------- Start/stop --------------------
+    Hashtable attributes=new Hashtable();
+    
+    public void setAttribute( String prop, Object value) {
+	attributes.put( prop, value );
+    }
+
+
+    public void setProperty( String prop, String value ) {
+        setAttribute( prop, value );
+    }
+
+    /** Called when the ContextManger is started
+     */
+    public void engineInit(ContextManager cm) throws TomcatException {
+	super.engineInit( cm );
+
+        protocolNote = cm.getNoteId(ContextManager.MODULE_NOTE,
+				    "coyote.protocol");
+        adapter=new Tomcat3Adapter(cm);
+        try {
+            Class c=Class.forName(processorClassName);
+            proto=(ProtocolHandler)c.newInstance();
+	    setNote(protocolNote, proto);
+        } catch( Exception ex ) {
+            ex.printStackTrace();
+        }
+        
+        this.setAttribute("jkHome", cm.getHome());
+
+        proto.setAdapter( adapter );
+        try {
+            Enumeration keys=attributes.keys();
+            while( keys.hasMoreElements() ) {
+                String k=(String)keys.nextElement();
+                Object o=attributes.get(k);
+                if( o instanceof String )
+                    IntrospectionUtils.setProperty( proto, k, (String)o );
+                else
+                    IntrospectionUtils.setAttribute( proto, k, o );
+            }
+	    proto.init();
+        } catch( Exception ex ) {
+            throw new TomcatException( "Error setting protocol properties ", ex );
+        }
+    }
+
+    /** Called when the ContextManger is started
+     */
+    public void engineStart(ContextManager cm) throws TomcatException {
+	try {
+            proto.start();
+	} catch( Exception ex ) {
+            ex.printStackTrace();
+	    throw new TomcatException( ex );
+	}
+    }
+
+    public void engineShutdown(ContextManager cm) throws TomcatException {
+	try {
+	    proto.destroy();	
+        } catch( Exception ex ) {
+	    throw new TomcatException( ex );
+	}
+    }
+
+    // -------------------- Handler implementation --------------------
+
+    /** Handle HTTP expectations.
+     */
+    public int preService(org.apache.tomcat.core.Request request,
+                          org.apache.tomcat.core.Response response) {
+	if(response instanceof Tomcat3Response) {
+	    try {
+		((Tomcat3Response)response).sendAcknowledgement();
+	    } catch(Exception ex) {
+		log("Can't send ACK", ex);
+	    }
+	}
+	return 0;
+    }
+
+    public int postRequest(org.apache.tomcat.core.Request request,
+                           org.apache.tomcat.core.Response response) {
+	if(request instanceof Tomcat3Request) {
+	    try {
+                Tomcat3Request httpReq=(Tomcat3Request)request;
+                org.apache.coyote.Request cReq = httpReq.getCoyoteRequest();
+                cReq.action( ActionCode.ACTION_POST_REQUEST , null);
+	    } catch(Exception ex) {
+		log("Can't send ACK", ex);
+	    }
+	}
+        return 0;
+    }
+    
+    /**
+       getInfo calls for SSL data
+       
+       @return the requested data
+    */
+    public Object getInfo( Context ctx, org.apache.tomcat.core.Request request,
+                           int id, String key ) {
+        if( ! ( request instanceof Tomcat3Request ) )
+            return null;
+
+        Tomcat3Request httpReq=(Tomcat3Request)request;
+        
+        if(key!=null && httpReq!=null ){
+            org.apache.coyote.Request cReq = httpReq.getCoyoteRequest();
+            Object info = cReq.getAttribute(key);
+            if( info != null)
+                return info;
+            // XXX Should use MsgContext, pass the attribute we need.
+            // This will extract both 
+            if(isSSLAttribute(key)) {
+                cReq.action(ActionCode.ACTION_REQ_SSL_ATTRIBUTE,
+                            httpReq.getCoyoteRequest() );
+		// Only allowed a single cert under the 2.2 Spec.
+		Object [] value = (Object []) cReq.getAttribute(SSLSupport.CERTIFICATE_KEY);
+		if( value != null ) {
+		    cReq.setAttribute(SSLSupport.CERTIFICATE_KEY, value[0]);
+		}
+		
+                return cReq.getAttribute(key);
+            }
+
+            return cReq.getAttribute( key );
+        }
+        return super.getInfo(ctx,request,id,key);
+    }
+
+    public int setInfo( Context ctx, org.apache.tomcat.core.Request request,
+                         int id, String key, String object ) {
+        if( ! ( request instanceof Tomcat3Request ) )
+            return DECLINED;
+        
+        Tomcat3Request httpReq=(Tomcat3Request)request;
+        
+        if(key!=null && httpReq!=null ){
+            org.apache.coyote.Request cReq = httpReq.getCoyoteRequest();
+            cReq.setAttribute(key, object);
+	    return OK;
+        }
+	return super.setInfo(ctx, request, id, key, object);
+    }
+
+    /**
+     * Check if a string is a reserved SSL attribute key.
+     */
+    public static boolean isSSLAttribute(String key) {
+	return SSLSupport.CIPHER_SUITE_KEY.equals(key) ||
+	    SSLSupport.KEY_SIZE_KEY.equals(key)        ||
+	    SSLSupport.CERTIFICATE_KEY.equals(key)     ||
+	    SSLSupport.SESSION_ID_KEY.equals(key);
+    }
+}
+
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat3/Tomcat3Adapter.java b/connectors/coyote/src/java/org/apache/coyote/tomcat3/Tomcat3Adapter.java
new file mode 100644
index 0000000..8d9f238
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat3/Tomcat3Adapter.java
@@ -0,0 +1,71 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat3;
+
+import org.apache.coyote.Adapter;
+import org.apache.tomcat.core.ContextManager;
+
+/** Adapter between Coyote and Tomcat.
+ *
+ *  This class handles the task of passing of an individual request to
+ *  Tomcat to handle.  Also some of the connection-specific methods are
+ *  delegated to here.
+ *
+ *  @author Bill Barker
+ */
+public class Tomcat3Adapter implements Adapter {
+    ContextManager cm;
+    
+    Tomcat3Adapter(ContextManager ctxman) {
+	cm   = ctxman;
+    }
+
+    static int containerRequestNOTE=1; // XXX Implement a NoteManager, namespaces.
+    
+    /** Pass off an individual request to Tomcat.
+     */
+    public void service(org.apache.coyote.Request request, 
+			org.apache.coyote.Response response) 
+	    throws Exception
+    {
+        Tomcat3Request reqA;
+        Tomcat3Response resA;
+
+        reqA=(Tomcat3Request)request.getNote( containerRequestNOTE );
+        if( reqA==null ) {
+            reqA=new Tomcat3Request();
+            resA=new Tomcat3Response();
+            cm.initRequest( reqA, resA );
+
+            reqA.setCoyoteRequest(request);
+            resA.setCoyoteResponse(response);
+            request.setNote( containerRequestNOTE, reqA );
+        } else {
+            resA=(Tomcat3Response)reqA.getResponse();
+        }
+        
+        if( reqA.scheme().isNull() ) {
+	    reqA.scheme().setString("http");
+	}
+	try {
+	    cm.service( reqA, resA );
+	} finally {
+	    reqA.recycle();
+	    resA.recycle();
+	}
+    }
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat3/Tomcat3Request.java b/connectors/coyote/src/java/org/apache/coyote/tomcat3/Tomcat3Request.java
new file mode 100644
index 0000000..0ef8571
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat3/Tomcat3Request.java
@@ -0,0 +1,222 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat3;
+
+import java.io.IOException;
+
+import org.apache.coyote.ActionCode;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+
+/** The Request to connect with Coyote.
+ *  This class handles the I/O requirements and transferring the request
+ *  line and Mime headers between Coyote and Tomcat.
+ * 
+ *  @author Bill Barker
+ *  @author Costin Manolache
+ */
+public class Tomcat3Request extends org.apache.tomcat.core.Request {
+
+    org.apache.coyote.Request coyoteRequest=null;
+
+    // For SSL attributes we need to call an ActionHook to get
+    // info from the protocol handler.
+    //    SSLSupport sslSupport=null;
+
+    ByteChunk  readChunk = new ByteChunk(8096);
+    int  pos=-1;
+    int  end=-1;
+    byte [] readBuffer = null;
+
+
+    public Tomcat3Request() {
+        super();
+        remoteAddrMB.recycle();
+        remoteHostMB.recycle();
+    }
+
+    public void recycle() {
+	super.recycle();
+	if( coyoteRequest != null) coyoteRequest.recycle();
+
+        remoteAddrMB.recycle();
+        remoteHostMB.recycle();
+	readChunk.recycle();
+
+	readBuffer=null;
+	pos=-1;
+	end=-1;
+    }
+
+    public org.apache.coyote.Request getCoyoteRequest() {
+        return coyoteRequest;
+    }
+    
+    /** Attach the Coyote Request to this Request.
+     *  This is currently set pre-request to allow copying the request
+     *  attributes to the Tomcat attributes.
+     */
+    public void setCoyoteRequest(org.apache.coyote.Request cReq) {
+        coyoteRequest=cReq;
+
+        // The CoyoteRequest/Tomcat3Request are bound togheter, they
+        // don't change. That means we can use the same field ( which
+        // doesn't change as well.
+        schemeMB = coyoteRequest.scheme();
+        methodMB = coyoteRequest.method();
+        uriMB = coyoteRequest.requestURI();
+        queryMB = coyoteRequest.query();
+        protoMB = coyoteRequest.protocol();
+
+	headers  = coyoteRequest.getMimeHeaders();
+	scookies.setHeaders(headers);
+	params.setHeaders(headers);
+        params.setQuery( queryMB );
+        
+        remoteAddrMB = coyoteRequest.remoteAddr();
+	remoteHostMB = coyoteRequest.remoteHost();
+	serverNameMB = coyoteRequest.serverName();
+
+        
+    }
+    
+    /** Read a single character from the request body.
+     */
+    public int doRead() throws IOException {
+	if( available == 0 ) 
+	    return -1;
+	// #3745
+	// if available == -1: unknown length, we'll read until end of stream.
+	if( available!= -1 )
+	    available--;
+	if(pos >= end) {
+	    if(readBytes() < 0)
+		return -1;
+	}
+	return readBuffer[pos++] & 0xFF;
+    }
+
+    /** Read a chunk from the request body.
+     */
+    public int doRead(byte[] b, int off, int len) throws IOException {
+	if( available == 0 )
+	    return -1;
+	// if available == -1: unknown length, we'll read until end of stream.
+	if(pos >= end) {
+	    if(readBytes() <= 0) 
+		return -1;
+	}
+	int rd = -1;
+	if((end - pos) > len) {
+	    rd = len;
+	} else {
+	    rd = end - pos;
+	}
+
+        System.arraycopy(readBuffer, pos, b, off, rd);
+	pos += rd;
+	if( available!= -1 )
+	    available -= rd;
+
+	return rd;
+    }
+    
+    /**
+     * Read bytes to the read chunk buffer.
+     */
+    protected int readBytes()
+        throws IOException {
+
+        int result = coyoteRequest.doRead(readChunk);
+        if (result > 0) {
+            readBuffer = readChunk.getBytes();
+            end = readChunk.getEnd();
+            pos = readChunk.getStart();
+        } else if( result < 0 ) {
+            throw new IOException( "Read bytes failed " + result );
+        }
+        return result;
+
+    }
+
+    // -------------------- override special methods
+
+    public MessageBytes remoteAddr() {
+	if( remoteAddrMB.isNull() ) {
+	    coyoteRequest.action( ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE, coyoteRequest );
+	}
+	return remoteAddrMB;
+    }
+
+    public MessageBytes remoteHost() {
+	if( remoteHostMB.isNull() ) {
+	    coyoteRequest.action( ActionCode.ACTION_REQ_HOST_ATTRIBUTE, coyoteRequest );
+	}
+	return remoteHostMB;
+    }
+
+    public String getLocalHost() {
+	return localHost;
+    }
+
+    public MessageBytes serverName(){
+        // That's set by protocol in advance, it's needed for mapping anyway,
+        // no need to do lazy eval.
+        return coyoteRequest.serverName();
+    }
+
+    public int getServerPort(){
+        return coyoteRequest.getServerPort();
+    }
+    
+    public void setServerPort(int i ) {
+	coyoteRequest.setServerPort( i );
+    }
+
+
+    public  void setRemoteUser( String s ) {
+	super.setRemoteUser(s);
+	coyoteRequest.getRemoteUser().setString(s);
+    }
+
+    public String getRemoteUser() {
+	String s=coyoteRequest.getRemoteUser().toString();
+	if( s == null )
+	    s=super.getRemoteUser();
+	return s;
+    }
+
+    public String getAuthType() {
+	return coyoteRequest.getAuthType().toString();
+    }
+    
+    public void setAuthType(String s ) {
+	coyoteRequest.getAuthType().setString(s);
+    }
+
+    public String getJvmRoute() {
+	return coyoteRequest.instanceId().toString();
+    }
+    
+    public void setJvmRoute(String s ) {
+	coyoteRequest.instanceId().setString(s);
+    }
+
+    public boolean isSecure() {
+	return "https".equalsIgnoreCase( coyoteRequest.scheme().toString());
+    }
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat3/Tomcat3Response.java b/connectors/coyote/src/java/org/apache/coyote/tomcat3/Tomcat3Response.java
new file mode 100644
index 0000000..c11b4b2
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat3/Tomcat3Response.java
@@ -0,0 +1,152 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat3;
+
+import java.io.IOException;
+import java.util.Locale;
+
+import org.apache.coyote.ActionCode;
+import org.apache.tomcat.core.Response;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+/** The Response to connect with Coyote.
+ *  This class mostly handles the I/O between Tomcat and Coyte.
+ *  @Author Bill Barker
+ */
+
+class Tomcat3Response extends  Response {
+    String reportedname=null;
+
+    org.apache.coyote.Response coyoteResponse=null;
+
+    ByteChunk outputChunk = new ByteChunk();
+
+    boolean  acknowledged=false;
+    
+    public Tomcat3Response() {
+        super();
+    }
+
+    /** Attach a Coyote Request to this request.
+     */
+    public void setCoyoteResponse(org.apache.coyote.Response cRes) {
+	coyoteResponse = cRes;
+	headers = coyoteResponse.getMimeHeaders();
+    }
+
+    public void init() {
+	super.init();
+    }
+
+    public void recycle() {
+	super.recycle();
+	if(coyoteResponse != null) coyoteResponse.recycle();
+	outputChunk.recycle();
+	acknowledged=false;
+    }
+
+    // XXX What is this ? */
+    public void setReported(String reported) {
+        reportedname = reported;
+    }
+
+    public void endHeaders()  throws IOException {
+	super.endHeaders();
+	coyoteResponse.setStatus(getStatus());
+	// Check that the content-length has been set.
+	int cLen = getContentLength();
+	if( cLen >= 0 ) {
+	    coyoteResponse.setContentLength(cLen);
+	}
+        // Calls a sendHeaders callback to the protocol
+	coyoteResponse.sendHeaders();
+    }
+
+    public void clientFlush() throws IOException {
+        coyoteResponse.action( ActionCode.ACTION_CLIENT_FLUSH, coyoteResponse );
+    }
+    
+    public void doWrite( byte buffer[], int pos, int count)
+	throws IOException
+    {
+	if( count > 0 ) {
+            // XXX should be an explicit callback as well.
+	    outputChunk.setBytes(buffer, pos, count);
+	    coyoteResponse.doWrite( outputChunk );
+	}
+    }
+
+    public void reset() throws IllegalStateException {
+	super.reset();
+	if( ! included )
+	    coyoteResponse.reset();
+    }
+    
+    public void finish() throws IOException {
+	super.finish();
+	coyoteResponse.finish();
+    }
+
+    /**
+     * Send an acknowledgment of a request.
+     * 
+     * @exception IOException if an input/output error occurs
+     */
+    public void sendAcknowledgement()
+        throws IOException {
+
+	if( status >= 300 ) // Don't ACK on errors.
+	    acknowledged = true;
+        // Don't ACK twice on the same request. (e.g. on a forward)
+	if(acknowledged)
+	    return;
+        // Ignore any call from an included servlet
+        if (isIncluded())
+            return; 
+        if (isBufferCommitted())
+            throw new IllegalStateException
+                (sm.getString("hsrf.error.ise"));
+
+	coyoteResponse.acknowledge();
+	acknowledged=true;
+    }
+
+    public void setLocale(Locale locale) {
+        if (locale == null || included) {
+            return;  // throw an exception?
+        }
+        this.locale = locale;
+        coyoteResponse.setLocale(locale);
+        contentLanguage = coyoteResponse.getContentLanguage();
+        // maintain Tomcat 3.3 behavior by setting the header too
+        // and by not trying to guess the characterEncoding
+        headers.setValue("Content-Language").setString(contentLanguage);
+    }
+
+    public void setContentType(String contentType) {
+        if (included) {
+            return;
+        }
+        coyoteResponse.setContentType(contentType);
+        this.contentType = coyoteResponse.getContentType();
+        this.characterEncoding = coyoteResponse.getCharacterEncoding();
+        this.haveCharacterEncoding = true;
+        // maintain Tomcat 3.3 behavior by setting the header too
+        headers.setValue("Content-Type").setString(contentType);
+    }
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/Constants.java b/connectors/coyote/src/java/org/apache/coyote/tomcat4/Constants.java
new file mode 100644
index 0000000..48e875c
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/Constants.java
@@ -0,0 +1,50 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat4;
+
+
+/**
+ * Constants.
+ *
+ * @author Remy Maucherat
+ */
+public final class Constants {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final String Package = "org.apache.coyote.tomcat4";
+    public static final int DEFAULT_CONNECTION_LINGER = -1;
+    public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
+    public static final int DEFAULT_CONNECTION_UPLOAD_TIMEOUT = 300000;
+    public static final int DEFAULT_SERVER_SOCKET_TIMEOUT = 0;
+
+    public static final int PROCESSOR_IDLE = 0;
+    public static final int PROCESSOR_ACTIVE = 1;
+
+    /**
+     * Default header names.
+     */
+    public static final String AUTHORIZATION_HEADER = "authorization";
+
+    /**
+     * SSL Certificate Request Attributite.
+     */
+    public static final String SSL_CERTIFICATE_ATTR = "org.apache.coyote.request.X509Certificate";
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteAdapter.java b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteAdapter.java
new file mode 100644
index 0000000..54cca8a
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteAdapter.java
@@ -0,0 +1,687 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat4;
+
+
+import java.io.IOException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.tomcat.util.buf.B2CConverter;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.Cookies;
+import org.apache.tomcat.util.http.ServerCookie;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.Adapter;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.Logger;
+import org.apache.catalina.util.StringManager;
+
+
+/**
+ * Implementation of a request processor which delegates the processing to a
+ * Coyote processor.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+final class CoyoteAdapter
+    implements Adapter {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final int ADAPTER_NOTES = 1;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new CoyoteProcessor associated with the specified connector.
+     *
+     * @param connector CoyoteConnector that owns this processor
+     * @param id Identifier of this CoyoteProcessor (unique per connector)
+     */
+    public CoyoteAdapter(CoyoteConnector connector) {
+
+        super();
+        this.connector = connector;
+        this.debug = connector.getDebug();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The CoyoteConnector with which this processor is associated.
+     */
+    private CoyoteConnector connector = null;
+
+
+    /**
+     * The debugging detail level for this component.
+     */
+    private int debug = 0;
+
+
+    /**
+     * The match string for identifying a session ID parameter.
+     */
+    private static final String match =
+        ";" + Globals.SESSION_PARAMETER_NAME + "=";
+
+
+    /**
+     * The match string for identifying a session ID parameter.
+     */
+    private static final char[] SESSION_ID = match.toCharArray();
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // -------------------------------------------------------- Adapter Methods
+
+
+    /**
+     * Service method.
+     */
+    public void service(Request req, Response res)
+        throws Exception {
+
+        CoyoteRequest request = (CoyoteRequest) req.getNote(ADAPTER_NOTES);
+        CoyoteResponse response = (CoyoteResponse) res.getNote(ADAPTER_NOTES);
+
+        if (request == null) {
+
+            // Create objects
+            request = (CoyoteRequest) connector.createRequest();
+            request.setCoyoteRequest(req);
+            response = (CoyoteResponse) connector.createResponse();
+            response.setCoyoteResponse(res);
+
+            // Link objects
+            request.setResponse(response);
+            response.setRequest(request);
+
+            // Set as notes
+            req.setNote(ADAPTER_NOTES, request);
+            res.setNote(ADAPTER_NOTES, response);
+
+            // Set query string encoding
+            req.getParameters().setQueryStringEncoding
+                (connector.getURIEncoding());
+
+        }
+
+        try {
+            // Parse and set Catalina and configuration specific 
+            // request parameters
+            postParseRequest(req, request, res, response);
+            // Calling the container
+            connector.getContainer().invoke(request, response);
+            response.finishResponse();
+
+            req.action( ActionCode.ACTION_POST_REQUEST , null);
+        } catch (IOException e) {
+            ;
+        } catch (Throwable t) {
+            log(sm.getString("coyoteAdapter.service"), t);
+        } finally {
+            // Recycle the wrapper request and response
+            request.recycle();
+            response.recycle();
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Parse additional request parameters.
+     */
+    protected void postParseRequest(Request req, CoyoteRequest request,
+                                    Response res, CoyoteResponse response)
+        throws Exception {
+        // XXX the processor needs to set a correct scheme and port prior to this point, 
+        // in ajp13 protocols dont make sense to get the port from the connector..
+        // XXX the processor may have set a correct scheme and port prior to this point, 
+        // in ajp13 protocols dont make sense to get the port from the connector...
+        // otherwise, use connector configuration
+        if (! req.scheme().isNull()) {
+            // use processor specified scheme to determine secure state
+            request.setSecure(req.scheme().equals("https"));
+        } else {
+            // use connector scheme and secure configuration, (defaults to
+            // "http" and false respectively)
+            req.scheme().setString(connector.getScheme());
+            request.setSecure(connector.getSecure());
+        }
+ 
+        // Filter trace method
+        if (!connector.getAllowTrace() 
+            && req.method().equalsIgnoreCase("TRACE")) {
+            res.setStatus(403);
+            res.setMessage("TRACE method is not allowed");
+            throw new IOException("TRACE method is not allowed");
+        }
+
+        request.setAuthorization
+            (req.getHeader(Constants.AUTHORIZATION_HEADER));
+        // FIXME: the code below doesnt belongs to here, this is only  have sense 
+        // in Http11, not in ajp13..
+        // At this point the Host header has been processed.
+        // Override if the proxyPort/proxyHost are set 
+        String proxyName = connector.getProxyName();
+        int proxyPort = connector.getProxyPort();
+        if (proxyPort != 0) {
+            request.setServerPort(proxyPort);
+            req.setServerPort(proxyPort);
+        } else {
+            request.setServerPort(req.getServerPort());
+        }
+        if (proxyName != null) {
+            request.setServerName(proxyName);
+            req.serverName().setString(proxyName);
+        } else {
+            request.setServerName(req.serverName().toString());
+        }
+
+        // URI decoding
+        req.decodedURI().duplicate(req.requestURI());
+        try {
+          req.getURLDecoder().convert(req.decodedURI(), false);
+        } catch (IOException ioe) {
+            res.setStatus(400);
+            res.setMessage("Invalid URI");
+            throw ioe;
+        }
+
+        // Normalize decoded URI
+        if (!normalize(req.decodedURI())) {
+            res.setStatus(400);
+            res.setMessage("Invalid URI");
+            throw new IOException("Invalid URI");
+        }
+
+        // URI character decoding
+        convertURI(req.decodedURI(), request);
+
+        // Parse session Id
+        parseSessionId(req, request);
+
+        // Additional URI normalization and validation is needed for security 
+        // reasons on Tomcat 4.0.x
+        if (connector.getUseURIValidationHack()) {
+            String uri = validate(request.getRequestURI());
+            if (uri == null) {
+                res.setStatus(400);
+                res.setMessage("Invalid URI");
+                throw new IOException("Invalid URI");
+            } else {
+                req.requestURI().setString(uri);
+                // Redoing the URI decoding
+                req.decodedURI().duplicate(req.requestURI());
+                req.getURLDecoder().convert(req.decodedURI(), true);
+                convertURI(req.decodedURI(), request);
+            }
+        }
+
+        // Parse cookies
+        parseCookies(req, request);
+
+        // Set the SSL properties
+        if( request.isSecure() ) {
+            res.action(ActionCode.ACTION_REQ_SSL_ATTRIBUTE,
+                       request.getCoyoteRequest());
+            //Set up for getAttributeNames
+            request.getAttribute(Globals.CERTIFICATES_ATTR);
+            request.getAttribute(Globals.CIPHER_SUITE_ATTR);
+            request.getAttribute(Globals.KEY_SIZE_ATTR);
+        }
+
+        // Set the remote principal
+        String principal = req.getRemoteUser().toString();
+        if (principal != null) {
+            request.setUserPrincipal(new CoyotePrincipal(principal));
+        }
+
+        // Set the authorization type
+        String authtype = req.getAuthType().toString();
+        if (authtype != null) {
+            request.setAuthType(authtype);
+        }
+
+    }
+
+    /**
+     * Parse session id in URL.
+     * FIXME: Optimize this.
+     */
+    protected void parseSessionId(Request req, CoyoteRequest request) {
+
+        req.decodedURI().toChars();
+        CharChunk uriCC = req.decodedURI().getCharChunk();
+        int semicolon = uriCC.indexOf(match, 0, match.length(), 0);
+
+        if (semicolon > 0) {
+
+            // Parse session ID, and extract it from the decoded request URI
+            int start = uriCC.getStart();
+            int end = uriCC.getEnd();
+
+            int sessionIdStart = start + semicolon + match.length();
+            int semicolon2 = uriCC.indexOf(';', sessionIdStart);
+            if (semicolon2 >= 0) {
+                request.setRequestedSessionId
+                    (new String(uriCC.getBuffer(), sessionIdStart, 
+                                semicolon2 - semicolon - match.length()));
+                req.decodedURI().setString
+                    (new String(uriCC.getBuffer(), start, semicolon) + 
+                            new String(uriCC.getBuffer(),
+                                        semicolon2,
+                                        end-semicolon2));
+            } else {
+                request.setRequestedSessionId
+                    (new String(uriCC.getBuffer(), sessionIdStart, 
+                                end - sessionIdStart));
+                req.decodedURI().setString
+                    (new String(uriCC.getBuffer(), start, semicolon));
+            }
+            request.setRequestedSessionURL(true);
+
+            // Extract session ID from request URI
+            String uri = req.requestURI().toString();
+            semicolon = uri.indexOf(match);
+
+            if (semicolon > 0) {
+                String rest = uri.substring(semicolon + match.length());
+                semicolon2 = rest.indexOf(';');
+                if (semicolon2 >= 0) {
+                    rest = rest.substring(semicolon2);
+                } else {
+                    rest = "";
+                }
+                req.requestURI().setString(uri.substring(0, semicolon) + rest);
+            }
+
+        } else {
+            request.setRequestedSessionId(null);
+            request.setRequestedSessionURL(false);
+        }
+
+    }
+
+
+    /**
+     * Parse cookies.
+     */
+    protected void parseCookies(Request req, CoyoteRequest request) {
+
+        Cookies serverCookies = req.getCookies();
+        int count = serverCookies.getCookieCount();
+        if (count <= 0)
+            return;
+
+        Cookie[] cookies = new Cookie[count];
+
+        int idx=0;
+        for (int i = 0; i < count; i++) {
+            ServerCookie scookie = serverCookies.getCookie(i);
+            if (scookie.getName().equals(Globals.SESSION_COOKIE_NAME)) {
+                // Override anything requested in the URL
+                if (!request.isRequestedSessionIdFromCookie()) {
+                    // Accept only the first session id cookie
+                    request.setRequestedSessionId
+                        (scookie.getValue().toString());
+                    request.setRequestedSessionCookie(true);
+                    request.setRequestedSessionURL(false);
+                    if (debug >= 1)
+                        log(" Requested cookie session id is " +
+                            ((HttpServletRequest) request.getRequest())
+                            .getRequestedSessionId());
+                }
+            }
+            try {
+                Cookie cookie = new Cookie(scookie.getName().toString(),
+                                           scookie.getValue().toString());
+                cookie.setPath(scookie.getPath().toString());
+                cookie.setVersion(scookie.getVersion());
+                String domain = scookie.getDomain().toString();
+                if (domain != null) {
+                    cookie.setDomain(scookie.getDomain().toString());
+                }
+                cookies[idx++] = cookie;
+            } catch(Exception ex) {
+                log("Bad Cookie Name: " + scookie.getName() + 
+                    " /Value: " + scookie.getValue(),ex);
+            }
+        }
+        if( idx < count ) {
+            Cookie [] ncookies = new Cookie[idx];
+            System.arraycopy(cookies, 0, ncookies, 0, idx);
+            cookies = ncookies;
+        }
+
+        request.setCookies(cookies);
+
+    }
+
+
+    /**
+     * Return a context-relative path, beginning with a "/", that represents
+     * the canonical version of the specified path after ".." and "." elements
+     * are resolved out.  If the specified path attempts to go outside the
+     * boundaries of the current context (i.e. too many ".." path elements
+     * are present), return <code>null</code> instead.
+     * This code is not optimized, and is only needed for Tomcat 4.0.x.
+     *
+     * @param path Path to be validated
+     */
+    protected static String validate(String path) {
+
+        if (path == null)
+            return null;
+
+        // Create a place for the normalized path
+        String normalized = path;
+
+        // Normalize "/%7E" and "/%7e" at the beginning to "/~"
+        if (normalized.startsWith("/%7E") ||
+            normalized.startsWith("/%7e"))
+            normalized = "/~" + normalized.substring(4);
+
+        // Prevent encoding '%', '/', '.' and '\', which are special reserved
+        // characters
+        if ((normalized.indexOf("%25") >= 0)
+            || (normalized.indexOf("%2F") >= 0)
+            || (normalized.indexOf("%2E") >= 0)
+            || (normalized.indexOf("%5C") >= 0)
+            || (normalized.indexOf("%2f") >= 0)
+            || (normalized.indexOf("%2e") >= 0)
+            || (normalized.indexOf("%5c") >= 0)) {
+            return null;
+        }
+
+        if (normalized.equals("/."))
+            return "/";
+
+        // Normalize the slashes and add leading slash if necessary
+        if (normalized.indexOf('\\') >= 0)
+            normalized = normalized.replace('\\', '/');
+        if (!normalized.startsWith("/"))
+            normalized = "/" + normalized;
+
+        // Resolve occurrences of "//" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("//");
+            if (index < 0)
+                break;
+            normalized = normalized.substring(0, index) +
+                normalized.substring(index + 1);
+        }
+
+        // Resolve occurrences of "/./" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("/./");
+            if (index < 0)
+                break;
+            normalized = normalized.substring(0, index) +
+                normalized.substring(index + 2);
+        }
+
+        // Resolve occurrences of "/../" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("/../");
+            if (index < 0)
+                break;
+            if (index == 0)
+                return (null);  // Trying to go outside our context
+            int index2 = normalized.lastIndexOf('/', index - 1);
+            normalized = normalized.substring(0, index2) +
+                normalized.substring(index + 3);
+        }
+
+        // Declare occurrences of "/..." (three or more dots) to be invalid
+        // (on some Windows platforms this walks the directory tree!!!)
+        if (normalized.indexOf("/...") >= 0)
+            return (null);
+
+        // Return the normalized path that we have completed
+        return (normalized);
+
+    }
+
+
+    /**
+     * Character conversion of the URI.
+     */
+    protected void convertURI(MessageBytes uri, CoyoteRequest request) 
+        throws Exception {
+
+        ByteChunk bc = uri.getByteChunk();
+        CharChunk cc = uri.getCharChunk();
+        cc.allocate(bc.getLength(), -1);
+
+        String enc = connector.getURIEncoding();
+        if (enc != null) {
+            B2CConverter conv = request.getURIConverter();
+            try {
+                if (conv == null) {
+                    conv = new B2CConverter(enc);
+                    request.setURIConverter(conv);
+                } else {
+                    conv.recycle();
+                }
+            } catch (IOException e) {
+                // Ignore
+                log("Invalid URI encoding; using HTTP default", e);
+                connector.setURIEncoding(null);
+            }
+            if (conv != null) {
+                try {
+                    conv.convert(bc, cc);
+                    uri.setChars(cc.getBuffer(), cc.getStart(), 
+                                 cc.getLength());
+                    return;
+                } catch (IOException e) {
+                    if (debug >= 1) {
+                        log("Invalid URI character encoding; trying ascii", e);
+                    }
+                    cc.recycle();
+                }
+            }
+        }
+
+        // Default encoding: fast conversion
+        byte[] bbuf = bc.getBuffer();
+        char[] cbuf = cc.getBuffer();
+        int start = bc.getStart();
+        for (int i = 0; i < bc.getLength(); i++) {
+            cbuf[i] = (char) (bbuf[i + start] & 0xff);
+        }
+        uri.setChars(cbuf, 0, bc.getLength());
+
+    }
+
+
+    /**
+     * Normalize URI.
+     * <p>
+     * This method normalizes "\", "//", "/./" and "/../". This method will
+     * return false when trying to go above the root, or if the URI contains
+     * a null byte.
+     * 
+     * @param uriMB URI to be normalized
+     */
+    public static boolean normalize(MessageBytes uriMB) {
+
+        ByteChunk uriBC = uriMB.getByteChunk();
+        byte[] b = uriBC.getBytes();
+        int start = uriBC.getStart();
+        int end = uriBC.getEnd();
+
+        // URL * is acceptable
+        if ((end - start == 1) && b[start] == (byte) '*')
+          return true;
+
+        int pos = 0;
+        int index = 0;
+
+        // Replace '\' with '/'
+        // Check for null byte
+        for (pos = start; pos < end; pos++) {
+            if (b[pos] == (byte) '\\')
+                b[pos] = (byte) '/';
+            if (b[pos] == (byte) 0)
+                return false;
+        }
+
+        // The URL must start with '/'
+        if (b[start] != (byte) '/') {
+            return false;
+        }
+
+        // Replace "//" with "/"
+        for (pos = start; pos < (end - 1); pos++) {
+            if (b[pos] == (byte) '/') {
+                while ((pos + 1 < end) && (b[pos + 1] == (byte) '/')) {
+                    copyBytes(b, pos, pos + 1, end - pos - 1);
+                    end--;
+                }
+            }
+        }
+
+        // If the URI ends with "/." or "/..", then we append an extra "/"
+        // Note: It is possible to extend the URI by 1 without any side effect
+        // as the next character is a non-significant WS.
+        if (((end - start) > 2) && (b[end - 1] == (byte) '.')) {
+            if ((b[end - 2] == (byte) '/') 
+                || ((b[end - 2] == (byte) '.') 
+                    && (b[end - 3] == (byte) '/'))) {
+                b[end] = (byte) '/';
+                end++;
+            }
+        }
+
+        uriBC.setEnd(end);
+
+        index = 0;
+
+        // Resolve occurrences of "/./" in the normalized path
+        while (true) {
+            index = uriBC.indexOf("/./", 0, 3, index);
+            if (index < 0)
+                break;
+            copyBytes(b, start + index, start + index + 2, 
+                      end - start - index - 2);
+            end = end - 2;
+            uriBC.setEnd(end);
+        }
+
+        index = 0;
+
+        // Resolve occurrences of "/../" in the normalized path
+        while (true) {
+            index = uriBC.indexOf("/../", 0, 4, index);
+            if (index < 0)
+                break;
+            // Prevent from going outside our context
+            if (index == 0)
+                return false;
+            int index2 = -1;
+            for (pos = start + index - 1; (pos >= 0) && (index2 < 0); pos --) {
+                if (b[pos] == (byte) '/') {
+                    index2 = pos;
+                }
+            }
+            copyBytes(b, start + index2, start + index + 3,
+                      end - start - index - 3);
+            end = end + index2 - index - 3;
+            uriBC.setEnd(end);
+            index = index2;
+        }
+
+        uriBC.setBytes(b, start, end);
+
+        return true;
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Copy an array of bytes to a different position. Used during 
+     * normalization.
+     */
+    protected static void copyBytes(byte[] b, int dest, int src, int len) {
+        for (int pos = 0; pos < len; pos++) {
+            b[pos + dest] = b[pos + src];
+        }
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any)
+     *
+     * @param message Message to be logged
+     */
+    protected void log(String message) {
+
+        Logger logger = connector.getContainer().getLogger();
+        if (logger != null)
+            logger.log("CoyoteAdapter " + message);
+
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any)
+     *
+     * @param message Message to be logged
+     * @param throwable Associated exception
+     */
+    protected void log(String message, Throwable throwable) {
+
+        Logger logger = connector.getContainer().getLogger();
+        if (logger != null)
+            logger.log("CoyoteAdapter " + message, throwable);
+
+    }
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteConnector.java b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteConnector.java
new file mode 100644
index 0000000..6b33f38
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteConnector.java
@@ -0,0 +1,1340 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat4;
+
+
+import java.util.Vector;
+import org.apache.tomcat.util.IntrospectionUtils;
+
+import org.apache.coyote.Adapter;
+import org.apache.coyote.ProtocolHandler;
+
+import org.apache.catalina.Connector;
+import org.apache.catalina.Container;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Logger;
+import org.apache.catalina.Request;
+import org.apache.catalina.Response;
+import org.apache.catalina.Service;
+import org.apache.catalina.net.DefaultServerSocketFactory;
+import org.apache.catalina.net.ServerSocketFactory;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.commons.modeler.Registry;
+
+import javax.management.MBeanRegistration;
+import javax.management.ObjectName;
+import javax.management.MBeanServer;
+
+
+/**
+ * Implementation of a Coyote connector for Tomcat 4.x.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public final class CoyoteConnector
+    implements Connector, Lifecycle, MBeanRegistration {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The <code>Service</code> we are associated with (if any).
+     */
+    private Service service = null;
+
+
+    /**
+     * The accept count for this Connector.
+     */
+    private int acceptCount = 10;
+
+
+    /**
+     * The IP address on which to bind, if any.  If <code>null</code>, all
+     * addresses on the server will be bound.
+     */
+    private String address = null;
+
+
+    /**
+     * Do we allow TRACE ?
+     */
+    private boolean allowTrace = false;
+
+
+    /**
+     * The input buffer size we should create on input streams.
+     */
+    private int bufferSize = 2048;
+
+
+    /**
+     * The Container used for processing requests received by this Connector.
+     */
+    protected Container container = null;
+
+
+    /**
+     * The set of processors that have ever been created.
+     */
+    private Vector created = new Vector();
+
+
+    /**
+     * The current number of processors that have been created.
+     */
+    private int curProcessors = 0;
+
+
+    /**
+     * The debugging detail level for this component.
+     */
+    private int debug = 0;
+
+
+    /**
+     * The "enable DNS lookups" flag for this Connector.
+     */
+    private boolean enableLookups = false;
+
+
+    /**
+     * The server socket factory for this component.
+     */
+    private ServerSocketFactory factory = null;
+
+
+    /**
+     * Descriptive information about this Connector implementation.
+     */
+    private static final String info =
+        "org.apache.coyote.tomcat4.CoyoteConnector2/1.0";
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The minimum number of processors to start at initialization time.
+     */
+    protected int minProcessors = 5;
+
+
+    /**
+     * The maximum amount of spare processors.
+     */
+    protected int maxSpareProcessors = 5;
+
+
+    /**
+     * The maximum number of processors allowed, or <0 for unlimited.
+     */
+    private int maxProcessors = 20;
+
+
+    /**
+     * Linger value on the incoming connection.
+     * Note : a value inferior to 0 means no linger.
+     */
+    private int connectionLinger = Constants.DEFAULT_CONNECTION_LINGER;
+
+
+    /**
+     * Timeout value on the incoming connection.
+     * Note : a value of 0 means no timeout.
+     */
+    private int connectionTimeout = Constants.DEFAULT_CONNECTION_TIMEOUT;
+
+
+    /**
+     * Timeout value on the incoming connection during request processing.
+     * Note : a value of 0 means no timeout.
+     */
+    private int connectionUploadTimeout = 
+        Constants.DEFAULT_CONNECTION_UPLOAD_TIMEOUT;
+
+
+    /**
+     * Timeout value on the server socket.
+     * Note : a value of 0 means no timeout.
+     */
+    private int serverSocketTimeout = Constants.DEFAULT_SERVER_SOCKET_TIMEOUT;
+
+
+    /**
+     * The port number on which we listen for requests.
+     */
+    private int port = 8080;
+
+
+    /**
+     * The server name to which we should pretend requests to this Connector
+     * were directed.  This is useful when operating Tomcat behind a proxy
+     * server, so that redirects get constructed accurately.  If not specified,
+     * the server name included in the <code>Host</code> header is used.
+     */
+    private String proxyName = null;
+
+
+    /**
+     * The server port to which we should pretent requests to this Connector
+     * were directed.  This is useful when operating Tomcat behind a proxy
+     * server, so that redirects get constructed accurately.  If not specified,
+     * the port number specified by the <code>port</code> property is used.
+     */
+    private int proxyPort = 0;
+
+
+    /**
+     * The redirect port for non-SSL to SSL redirects.
+     */
+    private int redirectPort = 443;
+
+
+    /**
+     * The request scheme that will be set on all requests received
+     * through this connector.
+     */
+    private String scheme = "http";
+
+
+    /**
+     * The secure connection flag that will be set on all requests received
+     * through this connector.
+     */
+    private boolean secure = false;
+
+    /** For jk, do tomcat authentication if true, trust server if false 
+     */ 
+    private boolean tomcatAuthentication = true;
+
+    /**
+     * The string manager for this package.
+     */
+    private StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * Has this component been initialized yet?
+     */
+    private boolean initialized = false;
+
+
+    /**
+     * Has this component been started yet?
+     */
+    private boolean started = false;
+
+
+    /**
+     * The shutdown signal to our background thread
+     */
+    private boolean stopped = false;
+
+
+    /**
+     * The background thread.
+     */
+    private Thread thread = null;
+
+
+    /**
+     * Use TCP no delay ?
+     */
+    private boolean tcpNoDelay = true;
+
+
+    /**
+     * Flag to disable setting a seperate time-out for uploads.
+     * If <code>true</code>, then the <code>timeout</code> parameter is
+     * ignored.  If <code>false</code>, then the <code>timeout</code>
+     * parameter is used to control uploads.
+     */
+    private boolean disableUploadTimeout = false;
+
+    /**
+     * Maximum number of Keep-Alive requests to honor per connection.
+     */
+    private int maxKeepAliveRequests = 100;
+
+
+    /**
+     * Compression value.
+     */
+    private String compression = "off";
+
+
+    /**
+     * Coyote Protocol handler class name.
+     * Defaults to the Coyote HTTP/1.1 protocolHandler.
+     */
+    private String protocolHandlerClassName =
+        "org.apache.coyote.http11.Http11Protocol";
+
+
+    /**
+     * Use URI validation for Tomcat 4.0.x.
+     */
+    private boolean useURIValidationHack = true;
+
+
+    /**
+     * Coyote protocol handler.
+     */
+    private ProtocolHandler protocolHandler = null;
+
+
+    /**
+     * Coyote adapter.
+     */
+    private Adapter adapter = null;
+
+
+     /**
+      * URI encoding.
+      */
+     private String URIEncoding = null;
+
+
+     /**
+      * URI encoding as body.
+      */
+     private boolean useBodyEncodingForURI = true;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the <code>Service</code> with which we are associated (if any).
+     */
+    public Service getService() {
+
+        return (this.service);
+
+    }
+
+
+    /**
+     * Set the <code>Service</code> with which we are associated (if any).
+     *
+     * @param service The service that owns this Engine
+     */
+    public void setService(Service service) {
+
+        this.service = service;
+
+    }
+
+
+    /**
+     * Return the connection linger for this Connector.
+     */
+    public int getConnectionLinger() {
+
+        return (connectionLinger);
+
+    }
+
+
+    /**
+     * Set the connection linger for this Connector.
+     *
+     * @param connectionLinger The new connection linger
+     */
+    public void setConnectionLinger(int connectionLinger) {
+
+        this.connectionLinger = connectionLinger;
+
+    }
+
+
+    /**
+     * Return the connection timeout for this Connector.
+     */
+    public int getConnectionTimeout() {
+
+        return (connectionTimeout);
+
+    }
+
+
+    /**
+     * Set the connection timeout for this Connector.
+     *
+     * @param connectionTimeout The new connection timeout
+     */
+    public void setConnectionTimeout(int connectionTimeout) {
+
+        this.connectionTimeout = connectionTimeout;
+
+    }
+
+
+    /**
+     * Return the connection upload timeout for this Connector.
+     */
+    public int getConnectionUploadTimeout() {
+
+        return (connectionUploadTimeout);
+
+    }
+
+
+    /**
+     * Set the connection upload timeout for this Connector.
+     *
+     * @param connectionUploadTimeout The new connection upload timeout
+     */
+    public void setConnectionUploadTimeout(int connectionUploadTimeout) {
+
+        this.connectionUploadTimeout = connectionUploadTimeout;
+
+    }
+
+
+    /**
+     * Return the server socket timeout for this Connector.
+     */
+    public int getServerSocketTimeout() {
+
+        return (serverSocketTimeout);
+
+    }
+
+
+    /**
+     * Set the server socket timeout for this Connector.
+     *
+     * @param serverSocketTimeout The new server socket timeout
+     */
+    public void setServerSocketTimeout(int serverSocketTimeout) {
+
+        this.serverSocketTimeout = serverSocketTimeout;
+
+    }
+
+
+    /**
+     * Return the accept count for this Connector.
+     */
+    public int getAcceptCount() {
+
+        return (acceptCount);
+
+    }
+
+
+    /**
+     * Set the accept count for this Connector.
+     *
+     * @param count The new accept count
+     */
+    public void setAcceptCount(int count) {
+
+        this.acceptCount = count;
+
+    }
+
+
+    /**
+     * Return the bind IP address for this Connector.
+     */
+    public String getAddress() {
+
+        return (this.address);
+
+    }
+
+
+    /**
+     * Set the bind IP address for this Connector.
+     *
+     * @param address The bind IP address
+     */
+    public void setAddress(String address) {
+
+        this.address = address;
+
+    }
+
+
+    /**
+     * True if the TRACE method is allowed.  Default value is "false".
+     */
+    public boolean getAllowTrace() {
+
+        return (this.allowTrace);
+
+    }
+
+
+    /**
+     * Set the allowTrace flag, to disable or enable the TRACE HTTP method.
+     *
+     * @param allowTrace The new allowTrace flag
+     */
+    public void setAllowTrace(boolean allowTrace) {
+
+        this.allowTrace = allowTrace;
+
+    }
+
+    /**
+     * Is this connector available for processing requests?
+     */
+    public boolean isAvailable() {
+
+        return (started);
+
+    }
+
+
+    /**
+     * Return the input buffer size for this Connector.
+     */
+    public int getBufferSize() {
+
+        return (this.bufferSize);
+
+    }
+
+
+    /**
+     * Set the input buffer size for this Connector.
+     *
+     * @param bufferSize The new input buffer size.
+     */
+    public void setBufferSize(int bufferSize) {
+
+        this.bufferSize = bufferSize;
+
+    }
+
+
+    /**
+     * Return the Container used for processing requests received by this
+     * Connector.
+     */
+    public Container getContainer() {
+
+        return (container);
+
+    }
+
+
+    /**
+     * Set the Container used for processing requests received by this
+     * Connector.
+     *
+     * @param container The new Container to use
+     */
+    public void setContainer(Container container) {
+
+        this.container = container;
+
+    }
+
+
+    /**
+     * Get the value of compression.
+     */
+    public String getCompression() {
+
+        return (compression);
+
+    }
+
+
+    /**
+     * Set the value of compression.
+     *
+     * @param compression The new compression value, which can be "on", "off"
+     * or "force"
+     */
+    public void setCompression(String compression) {
+
+        this.compression = compression;
+
+    }
+
+
+    /**
+     * Return the current number of processors that have been created.
+     */
+    public int getCurProcessors() {
+
+        return (curProcessors);
+
+    }
+
+
+    /**
+     * Return the debugging detail level for this component.
+     */
+    public int getDebug() {
+
+        return (debug);
+
+    }
+
+
+    /**
+     * Set the debugging detail level for this component.
+     *
+     * @param debug The new debugging detail level
+     */
+    public void setDebug(int debug) {
+
+        this.debug = debug;
+
+    }
+
+
+    /**
+     * Return the "enable DNS lookups" flag.
+     */
+    public boolean getEnableLookups() {
+
+        return (this.enableLookups);
+
+    }
+
+
+    /**
+     * Set the "enable DNS lookups" flag.
+     *
+     * @param enableLookups The new "enable DNS lookups" flag value
+     */
+    public void setEnableLookups(boolean enableLookups) {
+
+        this.enableLookups = enableLookups;
+
+    }
+
+
+    /**
+     * Return the server socket factory used by this Container.
+     */
+    public ServerSocketFactory getFactory() {
+
+        if (this.factory == null) {
+            synchronized (this) {
+                this.factory = new DefaultServerSocketFactory();
+            }
+        }
+        return (this.factory);
+
+    }
+
+
+    /**
+     * Set the server socket factory used by this Container.
+     *
+     * @param factory The new server socket factory
+     */
+    public void setFactory(ServerSocketFactory factory) {
+
+        this.factory = factory;
+
+    }
+
+
+    /**
+     * Return descriptive information about this Connector implementation.
+     */
+    public String getInfo() {
+
+        return (info);
+
+    }
+
+
+    /**
+     * Return the minimum number of processors to start at initialization.
+     */
+    public int getMinProcessors() {
+
+        return (minProcessors);
+
+    }
+
+
+    /**
+     * Set the minimum number of processors to start at initialization.
+     *
+     * @param minProcessors The new minimum processors
+     */
+    public void setMinProcessors(int minProcessors) {
+
+        this.minProcessors = minProcessors;
+
+    }
+
+
+    /**
+     * Return the maximum number of processors allowed, or <0 for unlimited.
+     */
+    public int getMaxProcessors() {
+
+        return (maxProcessors);
+
+    }
+
+
+    /**
+     * Set the maximum number of processors allowed, or <0 for unlimited.
+     *
+     * @param maxProcessors The new maximum processors
+     */
+    public void setMaxProcessors(int maxProcessors) {
+
+        this.maxProcessors = maxProcessors;
+
+    }
+
+
+    /**
+     * Return the maximum number of spare processors allowed.
+     */
+    public int getMaxSpareProcessors() {
+
+        return (maxSpareProcessors);
+
+    }
+
+
+    /**
+     * Set the maximum number of spare processors allowed.
+     *
+     * @param maxSpareProcessors The new maximum of spare processors
+     */
+    public void setMaxSpareProcessors(int maxSpareProcessors) {
+
+        this.maxSpareProcessors = maxSpareProcessors;
+
+    }
+
+
+    /**
+     * Return the port number on which we listen for requests.
+     */
+    public int getPort() {
+
+        return (this.port);
+
+    }
+
+
+    /**
+     * Set the port number on which we listen for requests.
+     *
+     * @param port The new port number
+     */
+    public void setPort(int port) {
+
+        this.port = port;
+
+    }
+
+
+    /**
+     * Return the class name of the Coyote protocol handler in use.
+     */
+    public String getProtocolHandlerClassName() {
+
+        return (this.protocolHandlerClassName);
+
+    }
+
+
+    /**
+     * Set the class name of the Coyote protocol handler which will be used
+     * by the connector.
+     *
+     * @param protocolHandlerClassName The new class name
+     */
+    public void setProtocolHandlerClassName(String protocolHandlerClassName) {
+
+        this.protocolHandlerClassName = protocolHandlerClassName;
+
+    }
+
+
+    /**
+     * Return the proxy server name for this Connector.
+     */
+    public String getProxyName() {
+
+        return (this.proxyName);
+
+    }
+
+
+    /**
+     * Set the proxy server name for this Connector.
+     *
+     * @param proxyName The new proxy server name
+     */
+    public void setProxyName(String proxyName) {
+
+        if(! "".equals(proxyName) ) {
+            this.proxyName = proxyName;
+        } else {
+            this.proxyName = null;
+        }
+
+    }
+
+
+    /**
+     * Return the proxy server port for this Connector.
+     */
+    public int getProxyPort() {
+
+        return (this.proxyPort);
+
+    }
+
+
+    /**
+     * Set the proxy server port for this Connector.
+     *
+     * @param proxyPort The new proxy server port
+     */
+    public void setProxyPort(int proxyPort) {
+
+        this.proxyPort = proxyPort;
+
+    }
+
+
+    /**
+     * Return the port number to which a request should be redirected if
+     * it comes in on a non-SSL port and is subject to a security constraint
+     * with a transport guarantee that requires SSL.
+     */
+    public int getRedirectPort() {
+
+        return (this.redirectPort);
+
+    }
+
+
+    /**
+     * Set the redirect port number.
+     *
+     * @param redirectPort The redirect port number (non-SSL to SSL)
+     */
+    public void setRedirectPort(int redirectPort) {
+
+        this.redirectPort = redirectPort;
+
+    }
+
+    /**
+     * Return the flag that specifies upload time-out behavior.
+     */
+    public boolean getDisableUploadTimeout() {
+        return disableUploadTimeout;
+    }
+
+    /**
+     * Set the flag to specify upload time-out behavior.
+     *
+     * @param isDisabled If <code>true</code>, then the <code>timeout</code>
+     * parameter is ignored.  If <code>false</code>, then the
+     * <code>timeout</code> parameter is used to control uploads.
+     */
+    public void setDisableUploadTimeout( boolean isDisabled ) {
+        disableUploadTimeout = isDisabled;
+    }
+
+    /**
+     * Return the maximum number of Keep-Alive requests to honor per connection.
+     */
+    public int getMaxKeepAliveRequests() {
+        return maxKeepAliveRequests;
+    }
+
+    /**
+     * Set the maximum number of Keep-Alive requests to honor per connection.
+     */
+    public void setMaxKeepAliveRequests(int mkar) {
+        maxKeepAliveRequests = mkar;
+    }
+
+    /**
+     * Return the scheme that will be assigned to requests received
+     * through this connector.  Default value is "http".
+     */
+    public String getScheme() {
+
+        return (this.scheme);
+
+    }
+
+
+    /**
+     * Set the scheme that will be assigned to requests received through
+     * this connector.
+     *
+     * @param scheme The new scheme
+     */
+    public void setScheme(String scheme) {
+
+        this.scheme = scheme;
+
+    }
+
+
+    /**
+     * Return the secure connection flag that will be assigned to requests
+     * received through this connector.  Default value is "false".
+     */
+    public boolean getSecure() {
+
+        return (this.secure);
+
+    }
+
+
+    /**
+     * Set the secure connection flag that will be assigned to requests
+     * received through this connector.
+     *
+     * @param secure The new secure connection flag
+     */
+    public void setSecure(boolean secure) {
+
+        this.secure = secure;
+
+    }
+
+    public boolean getTomcatAuthentication() {
+        return tomcatAuthentication;
+    }
+
+    public void setTomcatAuthentication(boolean tomcatAuthentication) {
+        this.tomcatAuthentication = tomcatAuthentication;
+    }
+
+    /**
+     * Return the TCP no delay flag value.
+     */
+    public boolean getTcpNoDelay() {
+
+        return (this.tcpNoDelay);
+
+    }
+
+
+    /**
+     * Set the TCP no delay flag which will be set on the socket after
+     * accepting a connection.
+     *
+     * @param tcpNoDelay The new TCP no delay flag
+     */
+    public void setTcpNoDelay(boolean tcpNoDelay) {
+
+        this.tcpNoDelay = tcpNoDelay;
+
+    }
+
+
+     /**
+      * Return the character encoding to be used for the URI.
+      */
+     public String getURIEncoding() {
+
+         return (this.URIEncoding);
+
+     }
+
+
+     /**
+      * Set the URI encoding to be used for the URI.
+      *
+      * @param URIEncoding The new URI character encoding.
+      */
+     public void setURIEncoding(String URIEncoding) {
+
+         this.URIEncoding = URIEncoding;
+
+     }
+
+
+     /**
+      * Return the true if the entity body encoding should be used for the URI.
+      */
+     public boolean getUseBodyEncodingForURI() {
+
+         return (this.useBodyEncodingForURI);
+
+     }
+
+
+     /**
+      * Set if the entity body encoding should be used for the URI.
+      *
+      * @param useBodyEncodingForURI The new value for the flag.
+      */
+     public void setUseBodyEncodingForURI(boolean useBodyEncodingForURI) {
+
+         this.useBodyEncodingForURI = useBodyEncodingForURI;
+
+     }
+
+
+    /**
+     * Return the value of the Uri validation flag.
+     */
+    public boolean getUseURIValidationHack() {
+
+        return (this.useURIValidationHack);
+
+    }
+
+
+    /**
+     * Set the value of the Uri validation flag.
+     *
+     * @param useURIValidationHack The new flag value
+     */
+    public void setUseURIValidationHack(boolean useURIValidationHack) {
+
+        this.useURIValidationHack = useURIValidationHack;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Create (or allocate) and return a Request object suitable for
+     * specifying the contents of a Request to the responsible Container.
+     */
+    public Request createRequest() {
+
+        CoyoteRequest request = new CoyoteRequest();
+        request.setConnector(this);
+        return (request);
+
+    }
+
+
+    /**
+     * Create (or allocate) and return a Response object suitable for
+     * receiving the contents of a Response from the responsible Container.
+     */
+    public Response createResponse() {
+
+        CoyoteResponse response = new CoyoteResponse();
+        response.setConnector(this);
+        return (response);
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any).
+     *
+     * @param message Message to be logged
+     */
+    private void log(String message) {
+
+        Logger logger = container.getLogger();
+        String localName = "CoyoteConnector";
+        if (logger != null)
+            logger.log(localName + " " + message);
+        else
+            System.out.println(localName + " " + message);
+
+    }
+
+
+    /**
+     * Log a message on the Logger associated with our Container (if any).
+     *
+     * @param message Message to be logged
+     * @param throwable Associated exception
+     */
+    private void log(String message, Throwable throwable) {
+
+        Logger logger = container.getLogger();
+        String localName = "CoyoteConnector";
+        if (logger != null)
+            logger.log(localName + " " + message, throwable);
+        else {
+            System.out.println(localName + " " + message);
+            throwable.printStackTrace(System.out);
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.addLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+
+        return null;//lifecycle.findLifecycleListeners();
+
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to add
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+        lifecycle.removeLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Initialize this connector (create ServerSocket here!)
+     */
+    public void initialize()
+        throws LifecycleException {
+
+        if (initialized)
+            throw new LifecycleException
+                (sm.getString("coyoteConnector.alreadyInitialized"));
+
+        this.initialized = true;
+
+        // Initializa adapter
+        adapter = new CoyoteAdapter(this);
+
+        // Instantiate protocol handler
+        try {
+            Class clazz = Class.forName(protocolHandlerClassName);
+            protocolHandler = (ProtocolHandler) clazz.newInstance();
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new LifecycleException
+                (sm.getString
+                 ("coyoteConnector.protocolHandlerInstantiationFailed", e));
+        }
+        protocolHandler.setAdapter(adapter);
+
+        IntrospectionUtils.setProperty(protocolHandler, "jkHome",
+                                       System.getProperty("catalina.base"));
+
+        // Set attributes
+        IntrospectionUtils.setProperty(protocolHandler, "port", "" + port);
+        IntrospectionUtils.setProperty(protocolHandler, "maxThreads",
+                                       "" + maxProcessors);
+        IntrospectionUtils.setProperty(protocolHandler, "minSpareThreads",
+                                       "" + minProcessors);
+        IntrospectionUtils.setProperty(protocolHandler, "maxSpareThreads",
+                                       "" + maxSpareProcessors);
+        IntrospectionUtils.setProperty(protocolHandler, "backlog",
+                                       "" + acceptCount);
+        IntrospectionUtils.setProperty(protocolHandler, "tcpNoDelay",
+                                       "" + tcpNoDelay);
+        IntrospectionUtils.setProperty(protocolHandler, "soLinger",
+                                       "" + connectionLinger);
+        IntrospectionUtils.setProperty(protocolHandler, "soTimeout",
+                                       "" + connectionTimeout);
+        IntrospectionUtils.setProperty(protocolHandler, "timeout",
+                                       "" + connectionUploadTimeout);
+        IntrospectionUtils.setProperty(protocolHandler, "serverSoTimeout",
+                                       "" + serverSocketTimeout);
+        IntrospectionUtils.setProperty(protocolHandler, "disableUploadTimeout",
+                                       "" + disableUploadTimeout);
+        IntrospectionUtils.setProperty(protocolHandler, "maxKeepAliveRequests",
+                                       "" + maxKeepAliveRequests);
+        IntrospectionUtils.setProperty(protocolHandler, "tomcatAuthentication",
+                                       "" + tomcatAuthentication);
+        IntrospectionUtils.setProperty(protocolHandler, "compression",
+                                       compression);
+        if (address != null) {
+            IntrospectionUtils.setProperty(protocolHandler, "address",
+                                           address);
+        }
+
+        // Configure secure socket factory
+        if (factory instanceof CoyoteServerSocketFactory) {
+            IntrospectionUtils.setProperty(protocolHandler, "secure",
+                                           "" + true);
+            CoyoteServerSocketFactory ssf =
+                (CoyoteServerSocketFactory) factory;
+            IntrospectionUtils.setProperty(protocolHandler, "algorithm",
+                                           ssf.getAlgorithm());
+	    IntrospectionUtils.setProperty(protocolHandler, "clientauth",
+                                           ssf.getClientAuth());
+            IntrospectionUtils.setProperty(protocolHandler, "keystore",
+                                           ssf.getKeystoreFile());
+            IntrospectionUtils.setProperty(protocolHandler, "randomfile",
+                                           ssf.getRandomFile());
+            IntrospectionUtils.setProperty(protocolHandler, "rootfile",
+                                           ssf.getRootFile());
+
+            IntrospectionUtils.setProperty(protocolHandler, "keypass",
+                                           ssf.getKeystorePass());
+            IntrospectionUtils.setProperty(protocolHandler, "keytype",
+                                           ssf.getKeystoreType());
+            IntrospectionUtils.setProperty(protocolHandler, "protocol",
+                                           ssf.getProtocol());
+            IntrospectionUtils.setProperty(protocolHandler,
+                                           "sSLImplementation",
+                                           ssf.getSSLImplementation());
+        } else {
+            IntrospectionUtils.setProperty(protocolHandler, "secure",
+                                           "" + false);
+        }
+
+        try {
+            protocolHandler.init();
+        } catch (Exception e) {
+            throw new LifecycleException
+                (sm.getString
+                 ("coyoteConnector.protocolHandlerInitializationFailed", e));
+        }
+    }
+
+
+    /**
+     * Begin processing requests via this Connector.
+     *
+     * @exception LifecycleException if a fatal startup error occurs
+     */
+    public void start() throws LifecycleException {
+
+        // Validate and update our current state
+        if (started)
+            throw new LifecycleException
+                (sm.getString("coyoteConnector.alreadyStarted"));
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+        started = true;
+
+        // We can't register earlier - the JMX registration of this happens
+        // in Server.start callback
+        if( this.oname != null ) {
+            // We are registred - register the adapter as well.
+            try {
+                Registry.getRegistry().registerComponent(protocolHandler,
+                        this.domain + 
+                        ":type=protocolHandler,className=" + protocolHandlerClassName, null);
+            } catch( Exception ex ) {
+                ex.printStackTrace();
+            }
+        } else {
+            log( "Coyote can't register jmx for protocol");
+        }
+
+        try {
+            protocolHandler.start();
+        } catch (Exception e) {
+            throw new LifecycleException
+                (sm.getString
+                 ("coyoteConnector.protocolHandlerStartFailed", e));
+        }
+
+    }
+
+
+    /**
+     * Terminate processing requests via this Connector.
+     *
+     * @exception LifecycleException if a fatal shutdown error occurs
+     */
+    public void stop() throws LifecycleException {
+
+        // Validate and update our current state
+        if (!started)
+            throw new LifecycleException
+                (sm.getString("coyoteConnector.notStarted"));
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+        started = false;
+
+        try {
+            protocolHandler.destroy();
+        } catch (Exception e) {
+            throw new LifecycleException
+                (sm.getString
+                 ("coyoteConnector.protocolHandlerDestroyFailed", e));
+        }
+
+    }
+
+    protected String domain;
+    protected ObjectName oname;
+    protected MBeanServer mserver;
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public ObjectName preRegister(MBeanServer server,
+                                  ObjectName name) throws Exception {
+        oname=name;
+        mserver=server;
+        domain=name.getDomain();
+        return name;
+    }
+
+    public void postRegister(Boolean registrationDone) {
+    }
+
+    public void preDeregister() throws Exception {
+    }
+
+    public void postDeregister() {
+    }
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteInputStream.java b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteInputStream.java
new file mode 100644
index 0000000..faad6d8
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteInputStream.java
@@ -0,0 +1,164 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat4;
+
+import java.io.IOException;
+
+import javax.servlet.ServletInputStream;
+
+import org.apache.coyote.Request;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+
+/**
+ * This class handles the readin input bytes, as well as the input buffering.
+ * 
+ * @author Remy Maucherat
+ */
+public class CoyoteInputStream extends ServletInputStream {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    private boolean closed = false;
+
+    private Request coyoteRequest;
+
+    private ByteChunk readChunk = new ByteChunk();
+
+    /**
+     * Current read position in the byte buffer.
+     */
+    private int pos = -1;
+    private int end = -1;
+    private byte[] readBuffer = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    void setRequest(Request coyoteRequest) {
+        this.coyoteRequest = coyoteRequest;
+    }
+
+
+    /**
+     * Recycle the input stream.
+     */
+    void recycle() {
+        closed = false;
+        pos = -1;
+        end = -1;
+        readBuffer = null;
+    }
+
+
+    // --------------------------------------------- ServletInputStream Methods
+
+
+    public int read()
+        throws IOException {
+
+        if (closed)
+            throw new IOException("Stream closed");
+
+        // Read additional bytes if none are available
+        while (pos >= end) {
+            if (readBytes() < 0)
+                return -1;
+        }
+
+        return (readBuffer[pos++] & 0xFF);
+
+    }
+
+    public int available() throws IOException {
+        if( pos < end ) return end-pos;
+        return 0;
+    }
+
+    public int read(byte[] b) throws IOException {
+
+        return read(b, 0, b.length);
+
+    }
+
+
+    public int read(byte[] b, int off, int len)
+        throws IOException {
+
+        if (closed)
+            throw new IOException("Stream closed");
+
+        // Read additional bytes if none are available
+        while (pos >= end) {
+            if (readBytes() < 0)
+                return -1;
+        }
+
+        int n = -1;
+        if ((end - pos) > len) {
+            n = len;
+        } else {
+            n = end - pos;
+        }
+
+        System.arraycopy(readBuffer, pos, b, off, n);
+
+        pos += n;
+        return n;
+
+    }
+
+
+    public int readLine(byte[] b, int off, int len) throws IOException {
+        return super.readLine(b, off, len);
+    }
+
+
+    /** 
+     * Close the stream
+     * Since we re-cycle, we can't allow the call to super.close()
+     * which would permantely disable us.
+     */
+    public void close() {
+        closed = true;
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Read bytes to the read chunk buffer.
+     */
+    protected int readBytes()
+        throws IOException {
+
+        int result = coyoteRequest.doRead(readChunk);
+        if (result > 0) {
+            readBuffer = readChunk.getBytes();
+            end = readChunk.getEnd();
+            pos = readChunk.getStart();
+        }
+        return result;
+
+    }
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteOutputStream.java b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteOutputStream.java
new file mode 100644
index 0000000..e91fcff
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteOutputStream.java
@@ -0,0 +1,93 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat4;
+
+import java.io.IOException;
+
+import javax.servlet.ServletOutputStream;
+
+/**
+ * Coyote implementation of the servlet output stream.
+ * 
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ */
+final class CoyoteOutputStream 
+    extends ServletOutputStream {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    protected OutputBuffer ob;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    protected CoyoteOutputStream(OutputBuffer ob) {
+        this.ob = ob;
+    }
+
+
+    // --------------------------------------------------- OutputStream Methods
+
+
+    public void write(int i)
+        throws IOException {
+        ob.writeByte(i);
+    }
+
+
+    public void write(byte[] b)
+        throws IOException {
+        write(b, 0, b.length);
+    }
+
+
+    public void write(byte[] b, int off, int len)
+        throws IOException {
+        ob.write(b, off, len);
+    }
+
+
+    /**
+     * Will send the buffer to the client.
+     */
+    public void flush()
+        throws IOException {
+        ob.flush();
+    }
+
+
+    public void close()
+        throws IOException {
+        ob.close();
+    }
+
+
+    // -------------------------------------------- ServletOutputStream Methods
+
+
+    public void print(String s)
+        throws IOException {
+        ob.write(s);
+    }
+
+
+}
+
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyotePrincipal.java b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyotePrincipal.java
new file mode 100644
index 0000000..49afb41
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyotePrincipal.java
@@ -0,0 +1,73 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat4;
+
+import java.security.Principal;
+
+/**
+ * Generic implementation of <strong>java.security.Principal</strong> that
+ * is used to represent principals authenticated at the protocol handler level.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class CoyotePrincipal 
+    implements Principal {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public CoyotePrincipal(String name) {
+
+        this.name = name;
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * The username of the user represented by this Principal.
+     */
+    protected String name = null;
+
+    public String getName() {
+        return (this.name);
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return a String representation of this object, which exposes only
+     * information that should be public.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("CoyotePrincipal[");
+        sb.append(this.name);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteRequest.java b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteRequest.java
new file mode 100644
index 0000000..c3649c2
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteRequest.java
@@ -0,0 +1,2107 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat4;
+
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.security.AccessController;
+import java.security.Principal;
+import java.security.PrivilegedAction;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.catalina.Connector;
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.HttpRequest;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Session;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.util.Enumerator;
+import org.apache.catalina.util.ParameterMap;
+import org.apache.catalina.util.RequestUtil;
+import org.apache.catalina.util.StringManager;
+import org.apache.catalina.util.StringParser;
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.Request;
+import org.apache.tomcat.util.buf.B2CConverter;
+import org.apache.tomcat.util.http.Parameters;
+
+/**
+ * Wrapper object for the Coyote request.
+ *
+ * @author Remy Maucherat
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class CoyoteRequest
+    implements HttpRequest, HttpServletRequest {
+
+
+    // --------------------------------------- PrivilegedGetSession Inner Class
+
+
+    protected class PrivilegedGetSession
+        implements PrivilegedAction {
+
+        private boolean create;
+
+        PrivilegedGetSession(boolean create) {
+            this.create = create;
+        }
+
+        public Object run() {
+            return doGetSession(create);
+        }
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Coyote request.
+     */
+    protected Request coyoteRequest;
+
+    /**
+     * Set the Coyote request.
+     * 
+     * @param coyoteRequest The Coyote request
+     */
+    public void setCoyoteRequest(Request coyoteRequest) {
+        this.coyoteRequest = coyoteRequest;
+        inputStream.setRequest(coyoteRequest);
+    }
+
+    /**
+     * Get the Coyote request.
+     */
+    public Request getCoyoteRequest() {
+        return (this.coyoteRequest);
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    /**
+     * The set of cookies associated with this Request.
+     */
+    protected Cookie[] cookies = null;
+
+
+    /**
+     * The set of SimpleDateFormat formats to use in getDateHeader().
+     */
+    protected SimpleDateFormat formats[] = {
+        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
+        new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
+        new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
+    };
+
+
+    /**
+     * The default Locale if none are specified.
+     */
+    protected static Locale defaultLocale = Locale.getDefault();
+
+
+    /**
+     * The attributes associated with this Request, keyed by attribute name.
+     */
+    protected HashMap attributes = new HashMap();
+
+
+    /**
+     * The preferred Locales assocaited with this Request.
+     */
+    protected ArrayList locales = new ArrayList();
+
+
+    /**
+     * Internal notes associated with this request by Catalina components
+     * and event listeners.
+     */
+    private transient HashMap notes = new HashMap();
+
+
+    /**
+     * Authentication type.
+     */
+    protected String authType = null;
+
+
+    /**
+     * Reader.
+     * Note: At the moment, no attempt is being made at recycling the reader,
+     * but this could be implemented in the future, using a design like the one
+     * used for the output buffer.
+     */
+    protected BufferedReader reader = null;
+
+
+    /**
+     * ServletInputStream.
+     */
+    protected CoyoteInputStream inputStream = new CoyoteInputStream();
+
+
+    /**
+     * Using stream flag.
+     */
+    protected boolean usingInputStream = false;
+
+
+    /**
+     * Using writer flag.
+     */
+    protected boolean usingReader = false;
+
+
+    /**
+     * Context path.
+     */
+    protected String contextPath = "";
+
+
+    /**
+     * Path info.
+     */
+    protected String pathInfo = null;
+
+
+    /**
+     * Servlet path.
+     */
+    protected String servletPath = null;
+
+
+    /**
+     * User principal.
+     */
+    protected Principal userPrincipal = null;
+
+
+    /**
+     * Session parsed flag.
+     */
+    protected boolean sessionParsed = false;
+
+
+    /**
+     * Request parameters parsed flag.
+     */
+    protected boolean requestParametersParsed = false;
+
+
+    /**
+     * Secure flag.
+     */
+    protected boolean secure = false;
+
+
+    /**
+     * Post data buffer.
+     */
+    protected static int CACHED_POST_LEN = 8192;
+    protected byte[] postData = null;
+
+
+    /**
+     * Hash map used in the getParametersMap method.
+     */
+    protected ParameterMap parameterMap = new ParameterMap();
+
+
+    /**
+     * The currently active session for this request.
+     */
+    protected Session session = null;
+
+
+    /**
+     * Was the requested session ID received in a cookie?
+     */
+    protected boolean requestedSessionCookie = false;
+
+
+    /**
+     * The requested session ID (if any) for this request.
+     */
+    protected String requestedSessionId = null;
+
+
+    /**
+     * Was the requested session ID received in a URL?
+     */
+    protected boolean requestedSessionURL = false;
+
+
+    /**
+     * The socket through which this Request was received.
+     */
+    protected Socket socket = null;
+
+
+    /**
+     * Parse locales.
+     */
+    protected boolean localesParsed = false;
+
+
+    /**
+     * The string parser we will use for parsing request lines.
+     */
+    private StringParser parser = new StringParser();
+
+
+    /**
+     * Remote address.
+     */
+    protected String remoteAddr = null;
+
+
+    /**
+     * Remote host.
+     */
+    protected String remoteHost = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Release all object references, and initialize instance variables, in
+     * preparation for reuse of this object.
+     */
+    public void recycle() {
+
+        context = null;
+        wrapper = null;
+
+        authorization = null;
+        authType = null;
+        usingInputStream = false;
+        usingReader = false;
+        contextPath = "";
+        pathInfo = null;
+        servletPath = null;
+        reader = null;
+        inputStream.recycle();
+        userPrincipal = null;
+        sessionParsed = false;
+        authorization = null;
+        requestParametersParsed = false;
+        locales.clear();
+        localesParsed = false;
+        secure = false;
+        remoteAddr = null;
+        remoteHost = null;
+
+        attributes.clear();
+        notes.clear();
+        cookies = null;
+
+        session = null;
+        requestedSessionCookie = false;
+        requestedSessionId = null;
+        requestedSessionURL = false;
+
+        parameterMap.setLocked(false);
+        parameterMap.clear();
+
+        if (facade != null) {
+            facade.clear();
+            facade = null;
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Request Methods
+
+
+    /**
+     * The authorization credentials sent with this Request.
+     */
+    protected String authorization = null;
+
+    /**
+     * Return the authorization credentials sent with this request.
+     */
+    public String getAuthorization() {
+
+        return (this.authorization);
+
+    }
+
+    /**
+     * Set the authorization credentials sent with this request.
+     *
+     * @param authorization The new authorization credentials
+     */
+    public void setAuthorization(String authorization) {
+        this.authorization = authorization;
+    }
+
+
+    /**
+     * Associated Catalina connector.
+     */
+    protected CoyoteConnector connector;
+
+    /**
+     * Return the Connector through which this Request was received.
+     */
+    public Connector getConnector() {
+        return (this.connector);
+    }
+
+    /**
+     * Set the Connector through which this Request was received.
+     *
+     * @param connector The new connector
+     */
+    public void setConnector(Connector connector) {
+        this.connector = (CoyoteConnector) connector;
+    }
+
+    /**
+     * The Context within which this Request is being processed.
+     */
+    protected Context context = null;
+
+    /**
+     * Return the Context within which this Request is being processed.
+     */
+    public Context getContext() {
+        return (this.context);
+    }
+
+    /**
+     * Set the Context within which this Request is being processed.  This
+     * must be called as soon as the appropriate Context is identified, because
+     * it identifies the value to be returned by <code>getContextPath()</code>,
+     * and thus enables parsing of the request URI.
+     *
+     * @param context The newly associated Context
+     */
+    public void setContext(Context context) {
+        this.context = context;
+    }
+
+
+    /**
+     * Descriptive information about this Request implementation.
+     */
+    protected static final String info =
+        "org.apache.coyote.catalina.CoyoteRequest/1.0";
+
+    /**
+     * Return descriptive information about this Request implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+        return (info);
+    }
+
+
+    /**
+     * The facade associated with this request.
+     */
+    protected CoyoteRequestFacade facade = null;
+
+    /**
+     * Return the <code>ServletRequest</code> for which this object
+     * is the facade.  This method must be implemented by a subclass.
+     */
+    public ServletRequest getRequest() {
+        if (facade == null) {
+            facade = new CoyoteRequestFacade(this);
+        }
+        return (facade);
+    }
+
+
+    /**
+     * The response with which this request is associated.
+     */
+    protected org.apache.catalina.Response response = null;
+
+    /**
+     * Return the Response with which this Request is associated.
+     */
+    public org.apache.catalina.Response getResponse() {
+        return (this.response);
+    }
+
+    /**
+     * Set the Response with which this Request is associated.
+     *
+     * @param response The new associated response
+     */
+    public void setResponse(org.apache.catalina.Response response) {
+        this.response = response;
+    }
+
+
+    /**
+     * Return the Socket (if any) through which this Request was received.
+     * This should <strong>only</strong> be used to access underlying state
+     * information about this Socket, such as the SSLSession associated with
+     * an SSLSocket.
+     */
+    public Socket getSocket() {
+        return (socket);
+    }
+
+    /**
+     * Set the Socket (if any) through which this Request was received.
+     *
+     * @param socket The socket through which this request was received
+     */
+    public void setSocket(Socket socket) {
+        this.socket = socket;
+        remoteHost = null;
+        remoteAddr = null;
+    }
+
+
+    /**
+     * Return the input stream associated with this Request.
+     */
+    public InputStream getStream() {
+        return inputStream;
+    }
+
+    /**
+     * Set the input stream associated with this Request.
+     *
+     * @param stream The new input stream
+     */
+    public void setStream(InputStream stream) {
+        // Ignore
+    }
+
+
+    /**
+     * URI byte to char converter (not recycled).
+     */
+    protected B2CConverter URIConverter = null;
+
+    /**
+     * Return the URI converter.
+     */
+    protected B2CConverter getURIConverter() {
+        return URIConverter;
+    }
+
+    /**
+     * Set the URI converter.
+     * 
+     * @param URIConverter the new URI connverter
+     */
+    protected void setURIConverter(B2CConverter URIConverter) {
+        this.URIConverter = URIConverter;
+    }
+
+
+    /**
+     * The Wrapper within which this Request is being processed.
+     */
+    protected Wrapper wrapper = null;
+
+    /**
+     * Return the Wrapper within which this Request is being processed.
+     */
+    public Wrapper getWrapper() {
+        return (this.wrapper);
+    }
+
+    /**
+     * Set the Wrapper within which this Request is being processed.  This
+     * must be called as soon as the appropriate Wrapper is identified, and
+     * before the Request is ultimately passed to an application servlet.
+     *
+     * @param wrapper The newly associated Wrapper
+     */
+    public void setWrapper(Wrapper wrapper) {
+        this.wrapper = wrapper;
+    }
+
+
+    // ------------------------------------------------- Request Public Methods
+
+
+    /**
+     * Create and return a ServletInputStream to read the content
+     * associated with this Request.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public ServletInputStream createInputStream() 
+        throws IOException {
+        return inputStream;
+    }
+
+
+    /**
+     * Perform whatever actions are required to flush and close the input
+     * stream or reader, in a single operation.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void finishRequest() throws IOException {
+        // The reader and input stream don't need to be closed
+    }
+
+
+    /**
+     * Return the object bound with the specified name to the internal notes
+     * for this request, or <code>null</code> if no such binding exists.
+     *
+     * @param name Name of the note to be returned
+     */
+    public Object getNote(String name) {
+        return (notes.get(name));
+    }
+
+
+    /**
+     * Return an Iterator containing the String names of all notes bindings
+     * that exist for this request.
+     */
+    public Iterator getNoteNames() {
+        return (notes.keySet().iterator());
+    }
+
+
+    /**
+     * Remove any object bound to the specified name in the internal notes
+     * for this request.
+     *
+     * @param name Name of the note to be removed
+     */
+    public void removeNote(String name) {
+        notes.remove(name);
+    }
+
+
+    /**
+     * Bind an object to a specified name in the internal notes associated
+     * with this request, replacing any existing binding for this name.
+     *
+     * @param name Name to which the object should be bound
+     * @param value Object to be bound to the specified name
+     */
+    public void setNote(String name, Object value) {
+        notes.put(name, value);
+    }
+
+
+    /**
+     * Set the content length associated with this Request.
+     *
+     * @param length The new content length
+     */
+    public void setContentLength(int length) {
+        // Not used
+    }
+
+
+    /**
+     * Set the content type (and optionally the character encoding)
+     * associated with this Request.  For example,
+     * <code>text/html; charset=ISO-8859-4</code>.
+     *
+     * @param type The new content type
+     */
+    public void setContentType(String type) {
+        // Not used
+    }
+
+
+    /**
+     * Set the protocol name and version associated with this Request.
+     *
+     * @param protocol Protocol name and version
+     */
+    public void setProtocol(String protocol) {
+        // Not used
+    }
+
+
+    /**
+     * Set the IP address of the remote client associated with this Request.
+     *
+     * @param remoteAddr The remote IP address
+     */
+    public void setRemoteAddr(String remoteAddr) {
+        // Not used
+    }
+
+
+    /**
+     * Set the fully qualified name of the remote client associated with this
+     * Request.
+     *
+     * @param remoteHost The remote host name
+     */
+    public void setRemoteHost(String remoteHost) {
+        // Not used
+    }
+
+
+    /**
+     * Set the name of the scheme associated with this request.  Typical values
+     * are <code>http</code>, <code>https</code>, and <code>ftp</code>.
+     *
+     * @param scheme The scheme
+     */
+    public void setScheme(String scheme) {
+        // Not used
+    }
+
+
+    /**
+     * Set the value to be returned by <code>isSecure()</code>
+     * for this Request.
+     *
+     * @param secure The new isSecure value
+     */
+    public void setSecure(boolean secure) {
+        this.secure = secure;
+    }
+
+
+    /**
+     * Set the name of the server (virtual host) to process this request.
+     *
+     * @param name The server name
+     */
+    public void setServerName(String name) {
+        coyoteRequest.serverName().setString(name);
+    }
+
+
+    /**
+     * Set the port number of the server to process this request.
+     *
+     * @param port The server port
+     */
+    public void setServerPort(int port) {
+        coyoteRequest.setServerPort(port);
+    }
+
+
+    // ------------------------------------------------- ServletRequest Methods
+
+
+    /**
+     * Return the specified request attribute if it exists; otherwise, return
+     * <code>null</code>.
+     *
+     * @param name Name of the request attribute to return
+     */
+    public Object getAttribute(String name) {
+        Object attr=attributes.get(name);
+
+        if(attr!=null)
+            return(attr);
+
+        attr =  coyoteRequest.getAttribute(name);
+        if(attr != null) {
+            attributes.put(name, attr);
+            return attr;
+        }
+        // XXX Should move to Globals
+        if(Constants.SSL_CERTIFICATE_ATTR.equals(name)) {
+            coyoteRequest.action(ActionCode.ACTION_REQ_SSL_CERTIFICATE, null);
+            attr = getAttribute(Globals.CERTIFICATES_ATTR);
+            if(attr != null)
+                attributes.put(name, attr);
+        }
+        return attr;
+    }
+
+    /**
+     * Return the names of all request attributes for this Request, or an
+     * empty <code>Enumeration</code> if there are none.
+     */
+    public Enumeration getAttributeNames() {
+        return (new Enumerator(attributes.keySet()));
+    }
+
+
+    /**
+     * Return the character encoding for this Request.
+     */
+    public String getCharacterEncoding() {
+      return (coyoteRequest.getCharacterEncoding());
+    }
+
+
+    /**
+     * Return the content length for this Request.
+     */
+    public int getContentLength() {
+        return (coyoteRequest.getContentLength());
+    }
+
+
+    /**
+     * Return the content type for this Request.
+     */
+    public String getContentType() {
+        return (coyoteRequest.getContentType());
+    }
+
+
+    /**
+     * Return the servlet input stream for this Request.  The default
+     * implementation returns a servlet input stream created by
+     * <code>createInputStream()</code>.
+     *
+     * @exception IllegalStateException if <code>getReader()</code> has
+     *  already been called for this request
+     * @exception IOException if an input/output error occurs
+     */
+    public ServletInputStream getInputStream() throws IOException {
+
+        if (usingReader)
+            throw new IllegalStateException
+                (sm.getString("coyoteRequest.getInputStream.ise"));
+
+        usingInputStream = true;
+        return inputStream;
+
+    }
+
+
+    /**
+     * Return the preferred Locale that the client will accept content in,
+     * based on the value for the first <code>Accept-Language</code> header
+     * that was encountered.  If the request did not specify a preferred
+     * language, the server's default Locale is returned.
+     */
+    public Locale getLocale() {
+
+        if (!localesParsed)
+            parseLocales();
+
+        if (locales.size() > 0) {
+            return ((Locale) locales.get(0));
+        } else {
+            return (defaultLocale);
+        }
+
+    }
+
+
+    /**
+     * Return the set of preferred Locales that the client will accept
+     * content in, based on the values for any <code>Accept-Language</code>
+     * headers that were encountered.  If the request did not specify a
+     * preferred language, the server's default Locale is returned.
+     */
+    public Enumeration getLocales() {
+
+        if (!localesParsed)
+            parseLocales();
+
+        if (locales.size() > 0)
+            return (new Enumerator(locales));
+        ArrayList results = new ArrayList();
+        results.add(defaultLocale);
+        return (new Enumerator(results));
+
+    }
+
+
+    /**
+     * Return the value of the specified request parameter, if any; otherwise,
+     * return <code>null</code>.  If there is more than one value defined,
+     * return only the first one.
+     *
+     * @param name Name of the desired request parameter
+     */
+    public String getParameter(String name) {
+
+        if (!requestParametersParsed)
+            parseRequestParameters();
+
+        return coyoteRequest.getParameters().getParameter(name);
+
+    }
+
+
+
+    /**
+     * Returns a <code>Map</code> of the parameters of this request.
+     * Request parameters are extra information sent with the request.
+     * For HTTP servlets, parameters are contained in the query string
+     * or posted form data.
+     *
+     * @return A <code>Map</code> containing parameter names as keys
+     *  and parameter values as map values.
+     */
+    public Map getParameterMap() {
+
+        if (parameterMap.isLocked())
+            return parameterMap;
+
+        Enumeration paramNames = getParameterNames();
+        while (paramNames.hasMoreElements()) {
+            String name = paramNames.nextElement().toString();
+            String[] values = getParameterValues(name);
+            parameterMap.put(name, values);
+        }
+
+        parameterMap.setLocked(true);
+
+        return parameterMap;
+
+    }
+
+
+    /**
+     * Return the names of all defined request parameters for this request.
+     */
+    public Enumeration getParameterNames() {
+
+        if (!requestParametersParsed)
+            parseRequestParameters();
+
+        return coyoteRequest.getParameters().getParameterNames();
+
+    }
+
+
+    /**
+     * Return the defined values for the specified request parameter, if any;
+     * otherwise, return <code>null</code>.
+     *
+     * @param name Name of the desired request parameter
+     */
+    public String[] getParameterValues(String name) {
+
+        if (!requestParametersParsed)
+            parseRequestParameters();
+
+        return coyoteRequest.getParameters().getParameterValues(name);
+
+    }
+
+
+    /**
+     * Return the protocol and version used to make this Request.
+     */
+    public String getProtocol() {
+        return coyoteRequest.protocol().toString();
+    }
+
+
+    /**
+     * Read the Reader wrapping the input stream for this Request.  The
+     * default implementation wraps a <code>BufferedReader</code> around the
+     * servlet input stream returned by <code>createInputStream()</code>.
+     *
+     * @exception IllegalStateException if <code>getInputStream()</code>
+     *  has already been called for this request
+     * @exception IOException if an input/output error occurs
+     */
+    public BufferedReader getReader() throws IOException {
+
+        if (usingInputStream)
+            throw new IllegalStateException
+                (sm.getString("coyoteRequest.getReader.ise"));
+
+        usingReader = true;
+        if (reader == null) {
+            String encoding = getCharacterEncoding();
+            if (encoding == null) {
+                encoding = 
+                    org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
+            }
+            InputStreamReader r = new InputStreamReader(inputStream, encoding);
+            reader = new BufferedReader(r);
+        }
+        return (reader);
+
+    }
+
+
+    /**
+     * Return the real path of the specified virtual path.
+     *
+     * @param path Path to be translated
+     *
+     * @deprecated As of version 2.1 of the Java Servlet API, use
+     *  <code>ServletContext.getRealPath()</code>.
+     */
+    public String getRealPath(String path) {
+
+        if (context == null)
+            return (null);
+        ServletContext servletContext = context.getServletContext();
+        if (servletContext == null)
+            return (null);
+        else {
+            try {
+                return (servletContext.getRealPath(path));
+            } catch (IllegalArgumentException e) {
+                return (null);
+            }
+        }
+
+    }
+
+
+    /**
+     * Return the remote IP address making this Request.
+     */
+    public String getRemoteAddr() {
+        if (remoteAddr == null) {
+            if (socket != null) {
+                InetAddress inet = socket.getInetAddress();
+                remoteAddr = inet.getHostAddress();
+            } else {
+                coyoteRequest.action
+                    (ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE, coyoteRequest);
+                remoteAddr = coyoteRequest.remoteAddr().toString();
+            }
+        }
+        return remoteAddr;
+    }
+
+
+    /**
+     * Return the remote host name making this Request.
+     */
+    public String getRemoteHost() {
+        if (remoteHost == null) {
+            if (!connector.getEnableLookups()) {
+                remoteHost = getRemoteAddr();
+            } else if (socket != null) {
+                InetAddress inet = socket.getInetAddress();
+                remoteHost = inet.getHostName();
+            } else {
+                coyoteRequest.action
+                    (ActionCode.ACTION_REQ_HOST_ATTRIBUTE, coyoteRequest);
+                remoteHost = coyoteRequest.remoteHost().toString();
+            }
+        }
+        return remoteHost;
+    }
+
+
+    /**
+     * Return a RequestDispatcher that wraps the resource at the specified
+     * path, which may be interpreted as relative to the current request path.
+     *
+     * @param path Path of the resource to be wrapped
+     */
+    public RequestDispatcher getRequestDispatcher(String path) {
+
+        if (context == null)
+            return (null);
+
+        // If the path is already context-relative, just pass it through
+        if (path == null)
+            return (null);
+        else if (path.startsWith("/"))
+            return (context.getServletContext().getRequestDispatcher(path));
+
+        // Convert a request-relative path to a context-relative one
+        String servletPath = (String) getAttribute(Globals.SERVLET_PATH_ATTR);
+        if (servletPath == null)
+            servletPath = getServletPath();
+
+        // Add the path info, if there is any
+        String pathInfo = getPathInfo();
+        String requestPath = null;
+
+        if (pathInfo == null) {
+            requestPath = servletPath;
+        } else {
+            requestPath = servletPath + pathInfo;
+        }
+
+        int pos = requestPath.lastIndexOf('/');
+        String relative = null;
+        if (pos >= 0) {
+            relative = RequestUtil.normalize
+                (requestPath.substring(0, pos + 1) + path);
+        } else {
+            relative = RequestUtil.normalize(requestPath + path);
+        }
+
+        return (context.getServletContext().getRequestDispatcher(relative));
+
+    }
+
+
+    /**
+     * Return the scheme used to make this Request.
+     */
+    public String getScheme() {
+        return (coyoteRequest.scheme().toString());
+    }
+
+
+    /**
+     * Return the server name responding to this Request.
+     */
+    public String getServerName() {
+        return (coyoteRequest.serverName().toString());
+    }
+
+
+    /**
+     * Return the server port responding to this Request.
+     */
+    public int getServerPort() {
+        return (coyoteRequest.getServerPort());
+    }
+
+
+    /**
+     * Was this request received on a secure connection?
+     */
+    public boolean isSecure() {
+        return (secure);
+    }
+
+
+    /**
+     * Remove the specified request attribute if it exists.
+     *
+     * @param name Name of the request attribute to remove
+     */
+    public void removeAttribute(String name) {
+        attributes.remove(name);
+    }
+
+
+    /**
+     * Set the specified request attribute to the specified value.
+     *
+     * @param name Name of the request attribute to set
+     * @param value The associated value
+     */
+    public void setAttribute(String name, Object value) {
+	
+        // Name cannot be null
+        if (name == null)
+            throw new IllegalArgumentException
+                (sm.getString("coyoteRequest.setAttribute.namenull"));
+
+        // Null value is the same as removeAttribute()
+        if (value == null) {
+            removeAttribute(name);
+            return;
+        }
+
+        attributes.put(name, value);
+
+    }
+
+
+    /**
+     * Overrides the name of the character encoding used in the body of
+     * this request.  This method must be called prior to reading request
+     * parameters or reading input using <code>getReader()</code>.
+     *
+     * @param enc The character encoding to be used
+     *
+     * @exception UnsupportedEncodingException if the specified encoding
+     *  is not supported
+     *
+     * @since Servlet 2.3
+     */
+    public void setCharacterEncoding(String enc)
+        throws UnsupportedEncodingException {
+
+        // Ensure that the specified encoding is valid
+        byte buffer[] = new byte[1];
+        buffer[0] = (byte) 'a';
+        String dummy = new String(buffer, enc);
+
+        // Save the validated encoding
+        coyoteRequest.setCharacterEncoding(enc);
+
+    }
+
+
+    // ---------------------------------------------------- HttpRequest Methods
+
+
+    /**
+     * Add a Cookie to the set of Cookies associated with this Request.
+     *
+     * @param cookie The new cookie
+     */
+    public void addCookie(Cookie cookie) {
+
+        // For compatibility only
+
+        int size = 0;
+        if (cookies != null) {
+            size = cookies.length;
+        }
+
+        Cookie[] newCookies = new Cookie[size + 1];
+        for (int i = 0; i < size; i++) {
+            newCookies[i] = cookies[i];
+        }
+        newCookies[size] = cookie;
+
+        cookies = newCookies;
+
+    }
+
+
+    /**
+     * Add a Header to the set of Headers associated with this Request.
+     *
+     * @param name The new header name
+     * @param value The new header value
+     */
+    public void addHeader(String name, String value) {
+        // Not used
+    }
+
+
+    /**
+     * Add a Locale to the set of preferred Locales for this Request.  The
+     * first added Locale will be the first one returned by getLocales().
+     *
+     * @param locale The new preferred Locale
+     */
+    public void addLocale(Locale locale) {
+        locales.add(locale);
+    }
+
+
+    /**
+     * Add a parameter name and corresponding set of values to this Request.
+     * (This is used when restoring the original request on a form based
+     * login).
+     *
+     * @param name Name of this request parameter
+     * @param values Corresponding values for this request parameter
+     */
+    public void addParameter(String name, String values[]) {
+        coyoteRequest.getParameters().addParameterValues(name, values);
+    }
+
+
+    /**
+     * Clear the collection of Cookies associated with this Request.
+     */
+    public void clearCookies() {
+        cookies = null;
+    }
+
+
+    /**
+     * Clear the collection of Headers associated with this Request.
+     */
+    public void clearHeaders() {
+        // Not used
+    }
+
+
+    /**
+     * Clear the collection of Locales associated with this Request.
+     */
+    public void clearLocales() {
+        locales.clear();
+    }
+
+
+    /**
+     * Clear the collection of parameters associated with this Request.
+     */
+    public void clearParameters() {
+        // Not used
+    }
+
+
+    /**
+     * Set the authentication type used for this request, if any; otherwise
+     * set the type to <code>null</code>.  Typical values are "BASIC",
+     * "DIGEST", or "SSL".
+     *
+     * @param type The authentication type used
+     */
+    public void setAuthType(String type) {
+        this.authType = type;
+    }
+
+
+    /**
+     * Set the context path for this Request.  This will normally be called
+     * when the associated Context is mapping the Request to a particular
+     * Wrapper.
+     *
+     * @param path The context path
+     */
+    public void setContextPath(String path) {
+
+        if (path == null) {
+            this.contextPath = "";
+        } else {
+            this.contextPath = path;
+        }
+
+    }
+
+
+    /**
+     * Set the HTTP request method used for this Request.
+     *
+     * @param method The request method
+     */
+    public void setMethod(String method) {
+        // Not used
+    }
+
+
+    /**
+     * Set the query string for this Request.  This will normally be called
+     * by the HTTP Connector, when it parses the request headers.
+     *
+     * @param query The query string
+     */
+    public void setQueryString(String query) {
+        // Not used
+    }
+
+
+    /**
+     * Set the path information for this Request.  This will normally be called
+     * when the associated Context is mapping the Request to a particular
+     * Wrapper.
+     *
+     * @param path The path information
+     */
+    public void setPathInfo(String path) {
+        this.pathInfo = path;
+    }
+
+
+    /**
+     * Set a flag indicating whether or not the requested session ID for this
+     * request came in through a cookie.  This is normally called by the
+     * HTTP Connector, when it parses the request headers.
+     *
+     * @param flag The new flag
+     */
+    public void setRequestedSessionCookie(boolean flag) {
+
+        this.requestedSessionCookie = flag;
+
+    }
+
+
+    /**
+     * Set the requested session ID for this request.  This is normally called
+     * by the HTTP Connector, when it parses the request headers.
+     *
+     * @param id The new session id
+     */
+    public void setRequestedSessionId(String id) {
+
+        this.requestedSessionId = id;
+
+    }
+
+
+    /**
+     * Set a flag indicating whether or not the requested session ID for this
+     * request came in through a URL.  This is normally called by the
+     * HTTP Connector, when it parses the request headers.
+     *
+     * @param flag The new flag
+     */
+    public void setRequestedSessionURL(boolean flag) {
+
+        this.requestedSessionURL = flag;
+
+    }
+
+
+    /**
+     * Set the unparsed request URI for this Request.  This will normally be
+     * called by the HTTP Connector, when it parses the request headers.
+     *
+     * @param uri The request URI
+     */
+    public void setRequestURI(String uri) {
+        // Not used
+    }
+
+
+    /**
+     * Set the decoded request URI.
+     * 
+     * @param uri The decoded request URI
+     */
+    public void setDecodedRequestURI(String uri) {
+        // Not used
+    }
+
+
+    /**
+     * Get the decoded request URI.
+     * 
+     * @return the URL decoded request URI
+     */
+    public String getDecodedRequestURI() {
+        return (coyoteRequest.decodedURI().toString());
+    }
+
+
+    /**
+     * Set the servlet path for this Request.  This will normally be called
+     * when the associated Context is mapping the Request to a particular
+     * Wrapper.
+     *
+     * @param path The servlet path
+     */
+    public void setServletPath(String path) {
+        this.servletPath = path;
+    }
+
+
+    /**
+     * Set the Principal who has been authenticated for this Request.  This
+     * value is also used to calculate the value to be returned by the
+     * <code>getRemoteUser()</code> method.
+     *
+     * @param principal The user Principal
+     */
+    public void setUserPrincipal(Principal principal) {
+        this.userPrincipal = principal;
+    }
+
+
+    // --------------------------------------------- HttpServletRequest Methods
+
+
+    /**
+     * Return the authentication type used for this Request.
+     */
+    public String getAuthType() {
+        return (authType);
+    }
+
+
+    /**
+     * Return the portion of the request URI used to select the Context
+     * of the Request.
+     */
+    public String getContextPath() {
+        return (contextPath);
+    }
+
+
+    /**
+     * Return the set of Cookies received with this Request.
+     */
+    public Cookie[] getCookies() {
+
+        return cookies;
+
+    }
+
+
+    /**
+     * Set the set of cookies recieved with this Request.
+     */
+    public void setCookies(Cookie[] cookies) {
+
+        this.cookies = cookies;
+
+    }
+
+
+    /**
+     * Return the value of the specified date header, if any; otherwise
+     * return -1.
+     *
+     * @param name Name of the requested date header
+     *
+     * @exception IllegalArgumentException if the specified header value
+     *  cannot be converted to a date
+     */
+    public long getDateHeader(String name) {
+
+        String value = getHeader(name);
+        if (value == null)
+            return (-1L);
+
+        // Work around a bug in SimpleDateFormat in pre-JDK1.2b4
+        // (Bug Parade bug #4106807)
+        value += " ";
+
+        // Attempt to convert the date header in a variety of formats
+        for (int i = 0; i < formats.length; i++) {
+            try {
+                Date date = formats[i].parse(value);
+                return (date.getTime());
+            } catch (ParseException e) {
+                ;
+            }
+        }
+        throw new IllegalArgumentException(value);
+
+    }
+
+
+    /**
+     * Return the first value of the specified header, if any; otherwise,
+     * return <code>null</code>
+     *
+     * @param name Name of the requested header
+     */
+    public String getHeader(String name) {
+        return coyoteRequest.getHeader(name);
+    }
+
+
+    /**
+     * Return all of the values of the specified header, if any; otherwise,
+     * return an empty enumeration.
+     *
+     * @param name Name of the requested header
+     */
+    public Enumeration getHeaders(String name) {
+        return coyoteRequest.getMimeHeaders().values(name);
+    }
+
+
+    /**
+     * Return the names of all headers received with this request.
+     */
+    public Enumeration getHeaderNames() {
+        return coyoteRequest.getMimeHeaders().names();
+    }
+
+
+    /**
+     * Return the value of the specified header as an integer, or -1 if there
+     * is no such header for this request.
+     *
+     * @param name Name of the requested header
+     *
+     * @exception IllegalArgumentException if the specified header value
+     *  cannot be converted to an integer
+     */
+    public int getIntHeader(String name) {
+
+        String value = getHeader(name);
+        if (value == null) {
+            return (-1);
+        } else {
+            return (Integer.parseInt(value));
+        }
+
+    }
+
+
+    /**
+     * Return the HTTP request method used in this Request.
+     */
+    public String getMethod() {
+        return coyoteRequest.method().toString();
+    }
+
+
+    /**
+     * Return the path information associated with this Request.
+     */
+    public String getPathInfo() {
+        return (pathInfo);
+    }
+
+
+    /**
+     * Return the extra path information for this request, translated
+     * to a real path.
+     */
+    public String getPathTranslated() {
+
+        if (context == null)
+            return (null);
+
+        if (pathInfo == null) {
+            return (null);
+        } else {
+            return (context.getServletContext().getRealPath(pathInfo));
+        }
+
+    }
+
+
+    /**
+     * Return the query string associated with this request.
+     */
+    public String getQueryString() {
+        String queryString = coyoteRequest.queryString().toString();
+        if (queryString.equals("")) {
+            return (null);
+        } else {
+            return queryString;
+        }
+    }
+
+
+    /**
+     * Return the name of the remote user that has been authenticated
+     * for this Request.
+     */
+    public String getRemoteUser() {
+
+        if (userPrincipal != null) {
+            return (userPrincipal.getName());
+        } else {
+            return (null);
+        }
+
+    }
+
+
+    /**
+     * Return the session identifier included in this request, if any.
+     */
+    public String getRequestedSessionId() {
+        return (requestedSessionId);
+    }
+
+
+    /**
+     * Return the request URI for this request.
+     */
+    public String getRequestURI() {
+        return coyoteRequest.requestURI().toString();
+    }
+
+
+    /**
+     * Reconstructs the URL the client used to make the request.
+     * The returned URL contains a protocol, server name, port
+     * number, and server path, but it does not include query
+     * string parameters.
+     * <p>
+     * Because this method returns a <code>StringBuffer</code>,
+     * not a <code>String</code>, you can modify the URL easily,
+     * for example, to append query parameters.
+     * <p>
+     * This method is useful for creating redirect messages and
+     * for reporting errors.
+     *
+     * @return A <code>StringBuffer</code> object containing the
+     *  reconstructed URL
+     */
+    public StringBuffer getRequestURL() {
+
+        StringBuffer url = new StringBuffer();
+        String scheme = getScheme();
+        int port = getServerPort();
+        if (port < 0)
+            port = 80; // Work around java.net.URL bug
+
+        url.append(scheme);
+        url.append("://");
+        url.append(getServerName());
+        if ((scheme.equals("http") && (port != 80))
+            || (scheme.equals("https") && (port != 443))) {
+            url.append(':');
+            url.append(port);
+        }
+        url.append(getRequestURI());
+
+        return (url);
+
+    }
+
+
+    /**
+     * Return the portion of the request URI used to select the servlet
+     * that will process this request.
+     */
+    public String getServletPath() {
+        return (servletPath);
+    }
+
+
+    /**
+     * Return the session associated with this Request, creating one
+     * if necessary.
+     */
+    public HttpSession getSession() {
+        return (getSession(true));
+    }
+
+
+    /**
+     * Return the session associated with this Request, creating one
+     * if necessary and requested.
+     *
+     * @param create Create a new session if one does not exist
+     */
+    public HttpSession getSession(boolean create) {
+
+        if (System.getSecurityManager() != null) {
+            PrivilegedGetSession dp = new PrivilegedGetSession(create);
+            return (HttpSession) AccessController.doPrivileged(dp);
+        }
+        return doGetSession(create);
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the session identifier included in this
+     * request came from a cookie.
+     */
+    public boolean isRequestedSessionIdFromCookie() {
+
+        if (requestedSessionId != null)
+            return (requestedSessionCookie);
+        else
+            return (false);
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the session identifier included in this
+     * request came from the request URI.
+     */
+    public boolean isRequestedSessionIdFromURL() {
+
+        if (requestedSessionId != null)
+            return (requestedSessionURL);
+        else
+            return (false);
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the session identifier included in this
+     * request came from the request URI.
+     *
+     * @deprecated As of Version 2.1 of the Java Servlet API, use
+     *  <code>isRequestedSessionIdFromURL()</code> instead.
+     */
+    public boolean isRequestedSessionIdFromUrl() {
+        return (isRequestedSessionIdFromURL());
+    }
+
+
+    /**
+     * Return <code>true</code> if the session identifier included in this
+     * request identifies a valid session.
+     */
+    public boolean isRequestedSessionIdValid() {
+
+        if (requestedSessionId == null)
+            return (false);
+        if (context == null)
+            return (false);
+        Manager manager = context.getManager();
+        if (manager == null)
+            return (false);
+        Session session = null;
+        try {
+            session = manager.findSession(requestedSessionId);
+        } catch (IOException e) {
+            session = null;
+        }
+        if ((session != null) && session.isValid())
+            return (true);
+        else
+            return (false);
+
+    }
+
+
+    /**
+     * Return <code>true</code> if the authenticated user principal
+     * possesses the specified role name.
+     *
+     * @param role Role name to be validated
+     */
+    public boolean isUserInRole(String role) {
+
+        // Have we got an authenticated principal at all?
+        if (userPrincipal == null)
+            return (false);
+
+        // Identify the Realm we will use for checking role assignmenets
+        if (context == null)
+            return (false);
+        Realm realm = context.getRealm();
+        if (realm == null)
+            return (false);
+
+        // Check for a role alias defined in a <security-role-ref> element
+        if (wrapper != null) {
+            String realRole = wrapper.findSecurityReference(role);
+            if ((realRole != null) &&
+                realm.hasRole(userPrincipal, realRole))
+                return (true);
+        }
+
+        // Check for a role defined directly as a <security-role>
+        return (realm.hasRole(userPrincipal, role));
+
+    }
+
+
+    /**
+     * Return the principal that has been authenticated for this Request.
+     */
+    public Principal getUserPrincipal() {
+        return (userPrincipal);
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    protected HttpSession doGetSession(boolean create) {
+
+        // There cannot be a session if no context has been assigned yet
+        if (context == null)
+            return (null);
+
+        // Return the current session if it exists and is valid
+        if ((session != null) && !session.isValid())
+            session = null;
+        if (session != null)
+            return (session.getSession());
+
+        // Return the requested session if it exists and is valid
+        Manager manager = null;
+        if (context != null)
+            manager = context.getManager();
+        if (manager == null)
+            return (null);      // Sessions are not supported
+        if (requestedSessionId != null) {
+            try {
+                session = manager.findSession(requestedSessionId);
+            } catch (IOException e) {
+                session = null;
+            }
+            if ((session != null) && !session.isValid())
+                session = null;
+            if (session != null) {
+                return (session.getSession());
+            }
+        }
+
+        // Create a new session if requested and the response is not committed
+        if (!create)
+            return (null);
+        if ((context != null) && (response != null) &&
+            context.getCookies() &&
+            response.getResponse().isCommitted()) {
+            throw new IllegalStateException
+              (sm.getString("coyoteRequest.sessionCreateCommitted"));
+        }
+
+        session = manager.createSession();
+
+        // Creating a new session cookie based on that session
+        if ((session != null) && (getContext() != null)
+            && getContext().getCookies()) {
+            Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,
+                                       session.getId());
+            cookie.setMaxAge(-1);
+            String contextPath = null;
+            if (context != null)
+                contextPath = context.getPath();
+            if ((contextPath != null) && (contextPath.length() > 0))
+                cookie.setPath(contextPath);
+            else
+                cookie.setPath("/");
+            if (isSecure())
+                cookie.setSecure(true);
+            ((HttpServletResponse) response).addCookie(cookie);
+        }
+
+        if (session != null)
+            return (session.getSession());
+        else
+            return (null);
+
+    }
+
+
+    /**
+     * Parse request parameters.
+     */
+    protected void parseRequestParameters() {
+
+        requestParametersParsed = true;
+
+        Parameters parameters = coyoteRequest.getParameters();
+
+        String enc = coyoteRequest.getCharacterEncoding();
+        boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
+        if (enc != null) {
+            parameters.setEncoding(enc);
+            if (useBodyEncodingForURI) {
+                parameters.setQueryStringEncoding(enc);
+            }
+        } else {
+            parameters.setEncoding
+                (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
+            if (useBodyEncodingForURI) {
+                parameters.setQueryStringEncoding
+                    (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
+            }
+        }
+
+        parameters.handleQueryParameters();
+
+        if (usingInputStream || usingReader)
+            return;
+
+        if (!getMethod().equalsIgnoreCase("POST"))
+            return;
+
+        String contentType = getContentType();
+        if (contentType == null)
+            contentType = "";
+        int semicolon = contentType.indexOf(';');
+        if (semicolon >= 0) {
+            contentType = contentType.substring(0, semicolon).trim();
+        } else {
+            contentType = contentType.trim();
+        }
+        if (!("application/x-www-form-urlencoded".equals(contentType)))
+            return;
+
+        int len = getContentLength();
+
+        if (len > 0) {
+            try {
+                byte[] formData = null;
+                if (len < CACHED_POST_LEN) {
+                    if (postData == null)
+                        postData = new byte[CACHED_POST_LEN];
+                    formData = postData;
+                } else {
+                    formData = new byte[len];
+                }
+                int actualLen = readPostBody(formData, len);
+                if (actualLen == len) {
+                    parameters.processParameters(formData, 0, len);
+                }
+            } catch (Throwable t) {
+                ; // Ignore
+            }
+        }
+
+    }
+
+
+    /**
+     * Read post body in an array.
+     */
+    protected int readPostBody(byte body[], int len)
+        throws IOException {
+
+        int offset = 0;
+        do {
+            int inputLen = getStream().read(body, offset, len - offset);
+            if (inputLen <= 0) {
+                return offset;
+            }
+            offset += inputLen;
+        } while ((len - offset) > 0);
+        return len;
+
+    }
+
+
+    /**
+     * Parse request locales.
+     */
+    protected void parseLocales() {
+
+        localesParsed = true;
+
+        Enumeration values = getHeaders("accept-language");
+
+        while (values.hasMoreElements()) {
+            String value = values.nextElement().toString();
+            parseLocalesHeader(value);
+        }
+
+    }
+
+
+    /**
+     * Parse accept-language header value.
+     */
+    protected void parseLocalesHeader(String value) {
+
+        // Store the accumulated languages that have been requested in
+        // a local collection, sorted by the quality value (so we can
+        // add Locales in descending order).  The values will be ArrayLists
+        // containing the corresponding Locales to be added
+        TreeMap locales = new TreeMap();
+
+        // Preprocess the value to remove all whitespace
+        int white = value.indexOf(' ');
+        if (white < 0)
+            white = value.indexOf('\t');
+        if (white >= 0) {
+            StringBuffer sb = new StringBuffer();
+            int len = value.length();
+            for (int i = 0; i < len; i++) {
+                char ch = value.charAt(i);
+                if ((ch != ' ') && (ch != '\t'))
+                    sb.append(ch);
+            }
+            value = sb.toString();
+        }
+
+        // Process each comma-delimited language specification
+        parser.setString(value);        // ASSERT: parser is available to us
+        int length = parser.getLength();
+        while (true) {
+
+            // Extract the next comma-delimited entry
+            int start = parser.getIndex();
+            if (start >= length)
+                break;
+            int end = parser.findChar(',');
+            String entry = parser.extract(start, end).trim();
+            parser.advance();   // For the following entry
+
+            // Extract the quality factor for this entry
+            double quality = 1.0;
+            int semi = entry.indexOf(";q=");
+            if (semi >= 0) {
+                try {
+                    quality = Double.parseDouble(entry.substring(semi + 3));
+                } catch (NumberFormatException e) {
+                    quality = 0.0;
+                }
+                entry = entry.substring(0, semi);
+            }
+
+            // Skip entries we are not going to keep track of
+            if (quality < 0.00005)
+                continue;       // Zero (or effectively zero) quality factors
+            if ("*".equals(entry))
+                continue;       // FIXME - "*" entries are not handled
+
+            // Extract the language and country for this entry
+            String language = null;
+            String country = null;
+            String variant = null;
+            int dash = entry.indexOf('-');
+            if (dash < 0) {
+                language = entry;
+                country = "";
+                variant = "";
+            } else {
+                language = entry.substring(0, dash);
+                country = entry.substring(dash + 1);
+                int vDash = country.indexOf('-');
+                if (vDash > 0) {
+                    String cTemp = country.substring(0, vDash);
+                    variant = country.substring(vDash + 1);
+                    country = cTemp;
+                } else {
+                    variant = "";
+                }
+            }
+
+            // Add a new Locale to the list of Locales for this quality level
+            Locale locale = new Locale(language, country, variant);
+            Double key = new Double(-quality);  // Reverse the order
+            ArrayList values = (ArrayList) locales.get(key);
+            if (values == null) {
+                values = new ArrayList();
+                locales.put(key, values);
+            }
+            values.add(locale);
+
+        }
+
+        // Process the quality values in highest->lowest order (due to
+        // negating the Double value when creating the key)
+        Iterator keys = locales.keySet().iterator();
+        while (keys.hasNext()) {
+            Double key = (Double) keys.next();
+            ArrayList list = (ArrayList) locales.get(key);
+            Iterator values = list.iterator();
+            while (values.hasNext()) {
+                Locale locale = (Locale) values.next();
+                addLocale(locale);
+            }
+        }
+
+    }
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteRequestFacade.java b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteRequestFacade.java
new file mode 100644
index 0000000..f52bcd2
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteRequestFacade.java
@@ -0,0 +1,342 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat4;
+
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.catalina.connector.RequestFacade;
+
+
+/**
+ * Facade class that wraps a Coyote request object.  
+ * All methods are delegated to the wrapped request.
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class CoyoteRequestFacade 
+    extends RequestFacade
+    implements HttpServletRequest {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a wrapper for the specified request.
+     *
+     * @param request The request to be wrapped
+     */
+    public CoyoteRequestFacade(CoyoteRequest request) {
+
+        super(request);
+        this.request = request;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The wrapped request.
+     */
+    protected CoyoteRequest request = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Clear facade.
+     */
+    public void clear() {
+        request = null;
+    }
+
+
+    // ------------------------------------------------- ServletRequest Methods
+
+
+    public Object getAttribute(String name) {
+        return request.getAttribute(name);
+    }
+
+
+    public Enumeration getAttributeNames() {
+        return request.getAttributeNames();
+    }
+
+
+    public String getCharacterEncoding() {
+        return request.getCharacterEncoding();
+    }
+
+
+    public void setCharacterEncoding(String env)
+        throws java.io.UnsupportedEncodingException {
+        request.setCharacterEncoding(env);
+    }
+
+
+    public int getContentLength() {
+        return request.getContentLength();
+    }
+
+
+    public String getContentType() {
+        return request.getContentType();
+    }
+
+
+    public ServletInputStream getInputStream()
+        throws IOException {
+        return request.getInputStream();
+    }
+
+
+    public String getParameter(String name) {
+        return request.getParameter(name);
+    }
+
+
+    public Enumeration getParameterNames() {
+        return request.getParameterNames();
+    }
+
+
+    public String[] getParameterValues(String name) {
+        return request.getParameterValues(name);
+    }
+
+
+    public Map getParameterMap() {
+        return request.getParameterMap();
+    }
+
+
+    public String getProtocol() {
+        return request.getProtocol();
+    }
+
+
+    public String getScheme() {
+        return request.getScheme();
+    }
+
+
+    public String getServerName() {
+        return request.getServerName();
+    }
+
+
+    public int getServerPort() {
+        return request.getServerPort();
+    }
+
+
+    public BufferedReader getReader()
+        throws IOException {
+        return request.getReader();
+    }
+
+
+    public String getRemoteAddr() {
+        return request.getRemoteAddr();
+    }
+
+
+    public String getRemoteHost() {
+        return request.getRemoteHost();
+    }
+
+
+    public void setAttribute(String name, Object o) {
+        request.setAttribute(name, o);
+    }
+
+
+    public void removeAttribute(String name) {
+        request.removeAttribute(name);
+    }
+
+
+    public Locale getLocale() {
+        return request.getLocale();
+    }
+
+
+    public Enumeration getLocales() {
+        return request.getLocales();
+    }
+
+
+    public boolean isSecure() {
+        return request.isSecure();
+    }
+
+
+    public RequestDispatcher getRequestDispatcher(String path) {
+        // TODO : Facade !!
+        return request.getRequestDispatcher(path);
+    }
+
+
+    public String getRealPath(String path) {
+        return request.getRealPath(path);
+    }
+
+
+    public String getAuthType() {
+        return request.getAuthType();
+    }
+
+
+    public Cookie[] getCookies() {
+        return request.getCookies();
+    }
+
+
+    public long getDateHeader(String name) {
+        return request.getDateHeader(name);
+    }
+
+
+    public String getHeader(String name) {
+        return request.getHeader(name);
+    }
+
+
+    public Enumeration getHeaders(String name) {
+        return request.getHeaders(name);
+    }
+
+
+    public Enumeration getHeaderNames() {
+        return request.getHeaderNames();
+    }
+
+
+    public int getIntHeader(String name) {
+        return request.getIntHeader(name);
+    }
+
+
+    public String getMethod() {
+        return request.getMethod();
+    }
+
+
+    public String getPathInfo() {
+        return request.getPathInfo();
+    }
+
+
+    public String getPathTranslated() {
+        return request.getPathTranslated();
+    }
+
+
+    public String getContextPath() {
+        return request.getContextPath();
+    }
+
+
+    public String getQueryString() {
+        return request.getQueryString();
+    }
+
+
+    public String getRemoteUser() {
+        return request.getRemoteUser();
+    }
+
+
+    public boolean isUserInRole(String role) {
+        return request.isUserInRole(role);
+    }
+
+
+    public java.security.Principal getUserPrincipal() {
+        return request.getUserPrincipal();
+    }
+
+
+    public String getRequestedSessionId() {
+        return request.getRequestedSessionId();
+    }
+
+
+    public String getRequestURI() {
+        return request.getRequestURI();
+    }
+
+
+    public StringBuffer getRequestURL() {
+        return request.getRequestURL();
+    }
+
+
+    public String getServletPath() {
+        return request.getServletPath();
+    }
+
+
+    public HttpSession getSession(boolean create) {
+        return request.getSession(create);
+    }
+
+
+    public HttpSession getSession() {
+        return getSession(true);
+    }
+
+
+    public boolean isRequestedSessionIdValid() {
+        return request.isRequestedSessionIdValid();
+    }
+
+
+    public boolean isRequestedSessionIdFromCookie() {
+        return request.isRequestedSessionIdFromCookie();
+    }
+
+
+    public boolean isRequestedSessionIdFromURL() {
+        return request.isRequestedSessionIdFromURL();
+    }
+
+
+    public boolean isRequestedSessionIdFromUrl() {
+        return request.isRequestedSessionIdFromURL();
+    }
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteResponse.java b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteResponse.java
new file mode 100644
index 0000000..828a76e
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteResponse.java
@@ -0,0 +1,1313 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat4;
+
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.Vector;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.apache.catalina.Connector;
+import org.apache.catalina.Context;
+import org.apache.catalina.HttpResponse;
+import org.apache.catalina.util.CharsetMapper;
+import org.apache.catalina.util.StringManager;
+import org.apache.coyote.Response;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.UEncoder;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.http.ServerCookie;
+import org.apache.tomcat.util.net.URL;
+
+
+/**
+ * Wrapper object for the Coyote response.
+ *
+ * @author Remy Maucherat
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public class CoyoteResponse
+    implements HttpResponse, HttpServletResponse {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public CoyoteResponse() {
+
+        format.setTimeZone(TimeZone.getTimeZone("GMT"));
+        urlEncoder.addSafeCharacter('/');
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The date format we will use for creating date headers.
+     */
+    protected final SimpleDateFormat format =
+        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+
+
+    /**
+     * Descriptive information about this Response implementation.
+     */
+    protected static final String info =
+        "org.apache.coyote.tomcat4.CoyoteResponse/1.0";
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Associated Catalina connector.
+     */
+    protected Connector connector;
+
+    /**
+     * Return the Connector through which this Request was received.
+     */
+    public Connector getConnector() {
+        return (this.connector);
+    }
+
+    /**
+     * Set the Connector through which this Request was received.
+     *
+     * @param connector The new connector
+     */
+    public void setConnector(Connector connector) {
+        this.connector = connector;
+    }
+
+
+    /**
+     * Coyote response.
+     */
+    protected Response coyoteResponse;
+
+    /**
+     * Set the Coyote response.
+     * 
+     * @param coyoteResponse The Coyote response
+     */
+    public void setCoyoteResponse(Response coyoteResponse) {
+        this.coyoteResponse = coyoteResponse;
+        outputBuffer.setResponse(coyoteResponse);
+    }
+
+    /**
+     * Get the Coyote response.
+     */
+    public Response getCoyoteResponse() {
+        return (coyoteResponse);
+    }
+
+
+    /**
+     * The Context within which this Request is being processed.
+     */
+    protected Context context = null;
+
+    /**
+     * Return the Context within which this Request is being processed.
+     */
+    public Context getContext() {
+        return (this.context);
+    }
+
+    /**
+     * Set the Context within which this Request is being processed.  This
+     * must be called as soon as the appropriate Context is identified, because
+     * it identifies the value to be returned by <code>getContextPath()</code>,
+     * and thus enables parsing of the request URI.
+     *
+     * @param context The newly associated Context
+     */
+    public void setContext(Context context) {
+        this.context = context;
+    }
+
+
+    /**
+     * The associated output buffer.
+     */
+    protected OutputBuffer outputBuffer = new OutputBuffer();
+
+
+    /**
+     * The associated output stream.
+     */
+    protected CoyoteOutputStream outputStream = 
+        new CoyoteOutputStream(outputBuffer);
+
+
+    /**
+     * The associated writer.
+     */
+    protected CoyoteWriter writer = new CoyoteWriter(outputBuffer);
+
+
+    /**
+     * The application commit flag.
+     */
+    protected boolean appCommitted = false;
+
+
+    /**
+     * The included flag.
+     */
+    protected boolean included = false;
+
+
+    /**
+     * The error flag.
+     */
+    protected boolean error = false;
+
+
+    /**
+     * The set of Cookies associated with this Response.
+     */
+    protected ArrayList cookies = new ArrayList();
+
+
+    /**
+     * Using output stream flag.
+     */
+    protected boolean usingOutputStream = false;
+
+
+    /**
+     * Using writer flag.
+     */
+    protected boolean usingWriter = false;
+
+
+    /**
+     * URL encoder.
+     */
+    protected UEncoder urlEncoder = new UEncoder();
+
+
+    /**
+     * Recyclable buffer to hold the redirect URL.
+     */
+    protected CharChunk redirectURLCC = new CharChunk();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Release all object references, and initialize instance variables, in
+     * preparation for reuse of this object.
+     */
+    public void recycle() {
+
+        outputBuffer.recycle();
+        usingOutputStream = false;
+        usingWriter = false;
+        appCommitted = false;
+        included = false;
+        error = false;
+        cookies.clear();
+
+        if (facade != null) {
+            facade.clear();
+            facade = null;
+        }
+
+        writer.recycle();
+
+    }
+
+
+    // ------------------------------------------------------- Response Methods
+
+
+    /**
+     * Return the number of bytes actually written to the output stream.
+     */
+    public int getContentCount() {
+        return outputBuffer.getContentWritten();
+    }
+
+
+    /**
+     * Set the application commit flag.
+     * 
+     * @param appCommitted The new application committed flag value
+     */
+    public void setAppCommitted(boolean appCommitted) {
+        this.appCommitted = appCommitted;
+    }
+
+
+    /**
+     * Application commit flag accessor.
+     */
+    public boolean isAppCommitted() {
+        return (this.appCommitted || isCommitted() || isSuspended());
+    }
+
+
+    /**
+     * Return the "processing inside an include" flag.
+     */
+    public boolean getIncluded() {
+        return included;
+    }
+
+
+    /**
+     * Set the "processing inside an include" flag.
+     *
+     * @param included <code>true</code> if we are currently inside a
+     *  RequestDispatcher.include(), else <code>false</code>
+     */
+    public void setIncluded(boolean included) {
+        this.included = included;
+    }
+
+
+    /**
+     * Return descriptive information about this Response implementation and
+     * the corresponding version number, in the format
+     * <code>&lt;description&gt;/&lt;version&gt;</code>.
+     */
+    public String getInfo() {
+        return (info);
+    }
+
+
+    /**
+     * The request with which this response is associated.
+     */
+    protected CoyoteRequest request = null;
+
+    /**
+     * Return the Request with which this Response is associated.
+     */
+    public org.apache.catalina.Request getRequest() {
+        return (this.request);
+    }
+
+    /**
+     * Set the Request with which this Response is associated.
+     *
+     * @param request The new associated request
+     */
+    public void setRequest(org.apache.catalina.Request request) {
+        this.request = (CoyoteRequest) request;
+    }
+
+
+    /**
+     * The facade associated with this response.
+     */
+    protected CoyoteResponseFacade facade = null;
+
+    /**
+     * Return the <code>ServletResponse</code> for which this object
+     * is the facade.
+     */
+    public ServletResponse getResponse() {
+        if (facade == null) {
+            facade = new CoyoteResponseFacade(this);
+        }
+        return (facade);
+    }
+
+
+    /**
+     * Return the output stream associated with this Response.
+     */
+    public OutputStream getStream() {
+        return outputStream;
+    }
+
+
+    /**
+     * Set the output stream associated with this Response.
+     *
+     * @param stream The new output stream
+     */
+    public void setStream(OutputStream stream) {
+        // This method is evil
+    }
+
+
+    /**
+     * Set the suspended flag.
+     * 
+     * @param suspended The new suspended flag value
+     */
+    public void setSuspended(boolean suspended) {
+        outputBuffer.setSuspended(suspended);
+    }
+
+
+    /**
+     * Suspended flag accessor.
+     */
+    public boolean isSuspended() {
+        return outputBuffer.isSuspended();
+    }
+
+
+    /**
+     * Set the error flag.
+     */
+    public void setError() {
+        error = true;
+    }
+
+
+    /**
+     * Error flag accessor.
+     */
+    public boolean isError() {
+        return error;
+    }
+
+
+    /**
+     * Create and return a ServletOutputStream to write the content
+     * associated with this Response.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public ServletOutputStream createOutputStream() 
+        throws IOException {
+        // Probably useless
+        return outputStream;
+    }
+
+
+    /**
+     * Perform whatever actions are required to flush and close the output
+     * stream or writer, in a single operation.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void finishResponse() 
+        throws IOException {
+        // Writing leftover bytes
+        try {
+            outputBuffer.close();
+        } catch(IOException e) {
+            ;
+        } catch(Throwable t) {
+            t.printStackTrace();
+        }
+    }
+
+
+    /**
+     * Return the content length that was set or calculated for this Response.
+     */
+    public int getContentLength() {
+        return (coyoteResponse.getContentLength());
+    }
+
+
+    /**
+     * Return the content type that was set or calculated for this response,
+     * or <code>null</code> if no content type was set.
+     */
+    public String getContentType() {
+        return (coyoteResponse.getContentType());
+    }
+
+
+    /**
+     * Return a PrintWriter that can be used to render error messages,
+     * regardless of whether a stream or writer has already been acquired.
+     *
+     * @return Writer which can be used for error reports. If the response is
+     * not an error report returned using sendError or triggered by an
+     * unexpected exception thrown during the servlet processing
+     * (and only in that case), null will be returned if the response stream
+     * has already been used.
+     */
+    public PrintWriter getReporter() {
+        if (outputBuffer.isNew()) {
+            return writer;
+        } else {
+            return null;
+        }
+    }
+
+
+    // ------------------------------------------------ ServletResponse Methods
+
+
+    /**
+     * Flush the buffer and commit this response.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void flushBuffer() 
+        throws IOException {
+        outputBuffer.flush();
+    }
+
+
+    /**
+     * Return the actual buffer size used for this Response.
+     */
+    public int getBufferSize() {
+        return outputBuffer.getBufferSize();
+    }
+
+
+    /**
+     * Return the character encoding used for this Response.
+     */
+    public String getCharacterEncoding() {
+        return (coyoteResponse.getCharacterEncoding());
+    }
+
+
+    /**
+     * Return the servlet output stream associated with this Response.
+     *
+     * @exception IllegalStateException if <code>getWriter</code> has
+     *  already been called for this response
+     * @exception IOException if an input/output error occurs
+     */
+    public ServletOutputStream getOutputStream() 
+        throws IOException {
+
+        if (usingWriter)
+            throw new IllegalStateException
+                (sm.getString("coyoteResponse.getOutputStream.ise"));
+
+        usingOutputStream = true;
+        return outputStream;
+
+    }
+
+
+    /**
+     * Return the Locale assigned to this response.
+     */
+    public Locale getLocale() {
+        return (coyoteResponse.getLocale());
+    }
+
+
+    /**
+     * Return the writer associated with this Response.
+     *
+     * @exception IllegalStateException if <code>getOutputStream</code> has
+     *  already been called for this response
+     * @exception IOException if an input/output error occurs
+     */
+    public PrintWriter getWriter() 
+        throws IOException {
+
+        if (usingOutputStream)
+            throw new IllegalStateException
+                (sm.getString("coyoteResponse.getWriter.ise"));
+
+        usingWriter = true;
+        return writer;
+
+    }
+
+
+    /**
+     * Has the output of this response already been committed?
+     */
+    public boolean isCommitted() {
+        return (coyoteResponse.isCommitted());
+    }
+
+
+    /**
+     * Clear any content written to the buffer.
+     *
+     * @exception IllegalStateException if this response has already
+     *  been committed
+     */
+    public void reset() {
+
+        if (included)
+            return;     // Ignore any call from an included servlet
+
+        coyoteResponse.reset();
+        outputBuffer.reset();
+
+    }
+
+
+    /**
+     * Reset the data buffer but not any status or header information.
+     *
+     * @exception IllegalStateException if the response has already
+     *  been committed
+     */
+    public void resetBuffer() {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (sm.getString("coyoteResponse.resetBuffer.ise"));
+
+        outputBuffer.reset();
+
+    }
+
+
+    /**
+     * Set the buffer size to be used for this Response.
+     *
+     * @param size The new buffer size
+     *
+     * @exception IllegalStateException if this method is called after
+     *  output has been committed for this response
+     */
+    public void setBufferSize(int size) {
+
+        if (isCommitted() || !outputBuffer.isNew())
+            throw new IllegalStateException
+                (sm.getString("coyoteResponse.setBufferSize.ise"));
+
+        outputBuffer.setBufferSize(size);
+
+    }
+
+
+    /**
+     * Set the content length (in bytes) for this Response.
+     *
+     * @param length The new content length
+     */
+    public void setContentLength(int length) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        coyoteResponse.setContentLength(length);
+
+    }
+
+
+    /**
+     * Set the content type for this Response.
+     *
+     * @param type The new content type
+     */
+    public void setContentType(String type) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        coyoteResponse.setContentType(type);
+
+    }
+
+
+    /**
+     * Set the Locale that is appropriate for this response, including
+     * setting the appropriate character encoding.
+     *
+     * @param locale The new locale
+     */
+    public void setLocale(Locale locale) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        coyoteResponse.setLocale(locale);
+        
+        // Set the specified locale's default encoding of a response
+        CharsetMapper cm = context.getCharsetMapper();
+        String charset = cm.getCharset(locale);
+
+        if (charset != null) {
+            coyoteResponse.setCharacterEncoding(charset);
+        }
+    
+    }
+
+
+    // --------------------------------------------------- HttpResponse Methods
+
+
+    /**
+     * Return an array of all cookies set for this response, or
+     * a zero-length array if no cookies have been set.
+     */
+    public Cookie[] getCookies() {
+        return ((Cookie[]) cookies.toArray(new Cookie[cookies.size()]));
+    }
+
+
+    /**
+     * Return the value for the specified header, or <code>null</code> if this
+     * header has not been set.  If more than one value was added for this
+     * name, only the first is returned; use getHeaderValues() to retrieve all
+     * of them.
+     *
+     * @param name Header name to look up
+     */
+    public String getHeader(String name) {
+        return coyoteResponse.getMimeHeaders().getHeader(name);
+    }
+
+
+    /**
+     * Return an array of all the header names set for this response, or
+     * a zero-length array if no headers have been set.
+     */
+    public String[] getHeaderNames() {
+
+        MimeHeaders headers = coyoteResponse.getMimeHeaders();
+        int n = headers.size();
+        String[] result = new String[n];
+        for (int i = 0; i < n; i++) {
+            result[i] = headers.getName(i).toString();
+        }
+        return result;
+
+    }
+
+
+    /**
+     * Return an array of all the header values associated with the
+     * specified header name, or an zero-length array if there are no such
+     * header values.
+     *
+     * @param name Header name to look up
+     */
+    public String[] getHeaderValues(String name) {
+
+        Enumeration headerValues = coyoteResponse.getMimeHeaders().values(name);
+        Vector result = new Vector();
+        while (headerValues.hasMoreElements()) {
+            result.addElement(headerValues.nextElement());
+        }
+        String[] resultArray = new String[result.size()];
+        result.copyInto(resultArray);
+        return resultArray;
+
+    }
+
+
+    /**
+     * Return the error message that was set with <code>sendError()</code>
+     * for this Response.
+     */
+    public String getMessage() {
+        return coyoteResponse.getMessage();
+    }
+
+
+    /**
+     * Return the HTTP status code associated with this Response.
+     */
+    public int getStatus() {
+        return coyoteResponse.getStatus();
+    }
+
+
+    /**
+     * Reset this response, and specify the values for the HTTP status code
+     * and corresponding message.
+     *
+     * @exception IllegalStateException if this response has already been
+     *  committed
+     */
+    public void reset(int status, String message) {
+        reset();
+        setStatus(status, message);
+    }
+
+
+    // -------------------------------------------- HttpServletResponse Methods
+
+
+    /**
+     * Add the specified Cookie to those that will be included with
+     * this Response.
+     *
+     * @param cookie Cookie to be added
+     */
+    public void addCookie(Cookie cookie) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        cookies.add(cookie);
+
+        StringBuffer sb = new StringBuffer();
+        ServerCookie.appendCookieValue
+            (sb, cookie.getVersion(), cookie.getName(), cookie.getValue(),
+             cookie.getPath(), cookie.getDomain(), cookie.getComment(), 
+             cookie.getMaxAge(), cookie.getSecure());
+        // the header name is Set-Cookie for both "old" and v.1 ( RFC2109 )
+        // RFC2965 is not supported by browsers and the Servlet spec
+        // asks for 2109.
+        addHeader("Set-Cookie", sb.toString());
+
+    }
+
+
+    /**
+     * Add the specified date header to the specified value.
+     *
+     * @param name Name of the header to set
+     * @param value Date value to be set
+     */
+    public void addDateHeader(String name, long value) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        addHeader(name, format.format(new Date(value)));
+
+    }
+
+
+    /**
+     * Add the specified header to the specified value.
+     *
+     * @param name Name of the header to set
+     * @param value Value to be set
+     */
+    public void addHeader(String name, String value) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        coyoteResponse.addHeader(name, value);
+
+    }
+
+
+    /**
+     * Add the specified integer header to the specified value.
+     *
+     * @param name Name of the header to set
+     * @param value Integer value to be set
+     */
+    public void addIntHeader(String name, int value) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        addHeader(name, "" + value);
+
+    }
+
+
+    /**
+     * Has the specified header been set already in this response?
+     *
+     * @param name Name of the header to check
+     */
+    public boolean containsHeader(String name) {
+        return coyoteResponse.containsHeader(name);
+    }
+
+
+    /**
+     * Encode the session identifier associated with this response
+     * into the specified redirect URL, if necessary.
+     *
+     * @param url URL to be encoded
+     */
+    public String encodeRedirectURL(String url) {
+
+        if (isEncodeable(toAbsolute(url))) {
+            HttpServletRequest hreq =
+                (HttpServletRequest) request.getRequest();
+            return (toEncoded(url, hreq.getSession().getId()));
+        } else {
+            return (url);
+        }
+
+    }
+
+
+    /**
+     * Encode the session identifier associated with this response
+     * into the specified redirect URL, if necessary.
+     *
+     * @param url URL to be encoded
+     *
+     * @deprecated As of Version 2.1 of the Java Servlet API, use
+     *  <code>encodeRedirectURL()</code> instead.
+     */
+    public String encodeRedirectUrl(String url) {
+        return (encodeRedirectURL(url));
+    }
+
+
+    /**
+     * Encode the session identifier associated with this response
+     * into the specified URL, if necessary.
+     *
+     * @param url URL to be encoded
+     */
+    public String encodeURL(String url) {
+        
+        String absolute = toAbsolute(url);
+        if (isEncodeable(absolute)) {
+            HttpServletRequest hreq =
+                (HttpServletRequest) request.getRequest();
+            
+            // W3c spec clearly said 
+            if (url.equalsIgnoreCase("")){
+                url = absolute;
+            }
+            return (toEncoded(url, hreq.getSession().getId()));
+        } else {
+            return (url);
+        }
+
+    }
+
+
+    /**
+     * Encode the session identifier associated with this response
+     * into the specified URL, if necessary.
+     *
+     * @param url URL to be encoded
+     *
+     * @deprecated As of Version 2.1 of the Java Servlet API, use
+     *  <code>encodeURL()</code> instead.
+     */
+    public String encodeUrl(String url) {
+        return (encodeURL(url));
+    }
+
+
+    /**
+     * Send an acknowledgment of a request.
+     * 
+     * @exception IOException if an input/output error occurs
+     */
+    public void sendAcknowledgement()
+        throws IOException {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return; 
+
+        coyoteResponse.acknowledge();
+
+    }
+
+
+    /**
+     * Send an error response with the specified status and a
+     * default message.
+     *
+     * @param status HTTP status code to send
+     *
+     * @exception IllegalStateException if this response has
+     *  already been committed
+     * @exception IOException if an input/output error occurs
+     */
+    public void sendError(int status) 
+        throws IOException {
+        sendError(status, null);
+    }
+
+
+    /**
+     * Send an error response with the specified status and message.
+     *
+     * @param status HTTP status code to send
+     * @param message Corresponding message to send
+     *
+     * @exception IllegalStateException if this response has
+     *  already been committed
+     * @exception IOException if an input/output error occurs
+     */
+    public void sendError(int status, String message) 
+        throws IOException {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (sm.getString("coyoteResponse.sendError.ise"));
+
+        // Ignore any call from an included servlet
+        if (included)
+            return; 
+
+        setError();
+
+        coyoteResponse.setStatus(status);
+        coyoteResponse.setMessage(message);
+
+        // Clear any data content that has been buffered
+        resetBuffer();
+
+        // Cause the response to be finished (from the application perspective)
+        setSuspended(true);
+
+    }
+
+
+    /**
+     * Send a temporary redirect to the specified redirect location URL.
+     *
+     * @param location Location URL to redirect to
+     *
+     * @exception IllegalStateException if this response has
+     *  already been committed
+     * @exception IOException if an input/output error occurs
+     */
+    public void sendRedirect(String location) 
+        throws IOException {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (sm.getString("coyoteResponse.sendRedirect.ise"));
+
+        // Ignore any call from an included servlet
+        if (included)
+            return; 
+
+        // Clear any data content that has been buffered
+        resetBuffer();
+
+        // Generate a temporary redirect to the specified location
+        try {
+            String absolute = toAbsolute(location);
+            setStatus(SC_MOVED_TEMPORARILY);
+            setHeader("Location", absolute);
+        } catch (IllegalArgumentException e) {
+            setStatus(SC_NOT_FOUND);
+        }
+
+        // Cause the response to be finished (from the application perspective)
+        setSuspended(true);
+
+    }
+
+
+    /**
+     * Set the specified date header to the specified value.
+     *
+     * @param name Name of the header to set
+     * @param value Date value to be set
+     */
+    public void setDateHeader(String name, long value) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        setHeader(name, format.format(new Date(value)));
+
+    }
+
+
+    /**
+     * Set the specified header to the specified value.
+     *
+     * @param name Name of the header to set
+     * @param value Value to be set
+     */
+    public void setHeader(String name, String value) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        coyoteResponse.setHeader(name, value);
+
+    }
+
+
+    /**
+     * Set the specified integer header to the specified value.
+     *
+     * @param name Name of the header to set
+     * @param value Integer value to be set
+     */
+    public void setIntHeader(String name, int value) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        setHeader(name, "" + value);
+
+    }
+
+
+    /**
+     * Set the HTTP status to be returned with this response.
+     *
+     * @param status The new HTTP status
+     */
+    public void setStatus(int status) {
+        setStatus(status, null);
+    }
+
+
+    /**
+     * Set the HTTP status and message to be returned with this response.
+     *
+     * @param status The new HTTP status
+     * @param message The associated text message
+     *
+     * @deprecated As of Version 2.1 of the Java Servlet API, this method
+     *  has been deprecated due to the ambiguous meaning of the message
+     *  parameter.
+     */
+    public void setStatus(int status, String message) {
+
+        if (isCommitted())
+            return;
+
+        // Ignore any call from an included servlet
+        if (included)
+            return;
+
+        coyoteResponse.setStatus(status);
+        coyoteResponse.setMessage(message);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return <code>true</code> if the specified URL should be encoded with
+     * a session identifier.  This will be true if all of the following
+     * conditions are met:
+     * <ul>
+     * <li>The request we are responding to asked for a valid session
+     * <li>The requested session ID was not received via a cookie
+     * <li>The specified URL points back to somewhere within the web
+     *     application that is responding to this request
+     * </ul>
+     *
+     * @param location Absolute URL to be validated
+     */
+    protected boolean isEncodeable(String location) {
+
+        if (location == null)
+            return (false);
+
+        // Is this an intra-document reference?
+        if (location.startsWith("#"))
+            return (false);
+
+        // Are we in a valid session that is not using cookies?
+        HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
+        HttpSession session = hreq.getSession(false);
+        if (session == null)
+            return (false);
+        if (hreq.isRequestedSessionIdFromCookie())
+            return (false);
+
+        // Is this a valid absolute URL?
+        URL url = null;
+        try {
+            url = new URL(location);
+        } catch (MalformedURLException e) {
+            return (false);
+        }
+
+        // Does this URL match down to (and including) the context path?
+        if (!hreq.getScheme().equalsIgnoreCase(url.getProtocol()))
+            return (false);
+        if (!hreq.getServerName().equalsIgnoreCase(url.getHost()))
+            return (false);
+        int serverPort = hreq.getServerPort();
+        if (serverPort == -1) {
+            if ("https".equals(hreq.getScheme()))
+                serverPort = 443;
+            else
+                serverPort = 80;
+        }
+        int urlPort = url.getPort();
+        if (urlPort == -1) {
+            if ("https".equals(url.getProtocol()))
+                urlPort = 443;
+            else
+                urlPort = 80;
+        }
+        if (serverPort != urlPort)
+            return (false);
+
+        String contextPath = getContext().getPath();
+        if (contextPath != null) {
+            String file = url.getFile();
+            if ((file == null) || !file.startsWith(contextPath))
+                return (false);
+            if( file.indexOf(";jsessionid=" + session.getId()) >= 0 )
+                return (false);
+        }
+
+        // This URL belongs to our web application, so it is encodeable
+        return (true);
+
+    }
+
+
+    /**
+     * Convert (if necessary) and return the absolute URL that represents the
+     * resource referenced by this possibly relative URL.  If this URL is
+     * already absolute, return it unchanged.
+     *
+     * @param location URL to be (possibly) converted and then returned
+     *
+     * @exception IllegalArgumentException if a MalformedURLException is
+     *  thrown when converting the relative URL to an absolute one
+     */
+    private String toAbsolute(String location) {
+
+        if (location == null)
+            return (location);
+
+        // Construct a new absolute URL if possible (cribbed from
+        // the DefaultErrorPage servlet)
+        URL url = null;
+        try {
+            url = new URL(location);
+
+            if (url.getAuthority() == null)
+                return location;
+
+        } catch (MalformedURLException e1) {
+            HttpServletRequest hreq =
+                (HttpServletRequest) request.getRequest();
+            String requrl = request.getRequestURL().toString();
+            try {
+                url = new URL(new URL(requrl), location);
+            } catch (MalformedURLException e2) {
+                throw new IllegalArgumentException(location);
+            }
+        }
+        return (url.toExternalForm());
+    }
+
+
+    /**
+     * Return the specified URL with the specified session identifier
+     * suitably encoded.
+     *
+     * @param url URL to be encoded with the session id
+     * @param sessionId Session id to be included in the encoded URL
+     */
+    private String toEncoded(String url, String sessionId) {
+
+        if ((url == null) || (sessionId == null))
+            return (url);
+
+        String path = url;
+        String query = "";
+        String anchor = "";
+        int question = url.indexOf('?');
+        if (question >= 0) {
+            path = url.substring(0, question);
+            query = url.substring(question);
+        }
+        int pound = path.indexOf('#');
+        if (pound >= 0) {
+            anchor = path.substring(pound);
+            path = path.substring(0, pound);
+        }
+        StringBuffer sb = new StringBuffer(path);
+        if( sb.length() > 0 ) { // jsessionid can't be first.
+            sb.append(";jsessionid=");
+            sb.append(sessionId);
+        }
+        sb.append(anchor);
+        sb.append(query);
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteResponseFacade.java b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteResponseFacade.java
new file mode 100644
index 0000000..de8e327
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteResponseFacade.java
@@ -0,0 +1,380 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat4;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Locale;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.connector.ResponseFacade;
+
+/**
+ * Facade class that wraps a Coyote response object. 
+ * All methods are delegated to the wrapped response.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class CoyoteResponseFacade 
+    extends ResponseFacade
+    implements HttpServletResponse {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a wrapper for the specified response.
+     *
+     * @param response The response to be wrapped
+     */
+    public CoyoteResponseFacade(CoyoteResponse response) {
+
+        super(response);
+        this.response = response;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The wrapped response.
+     */
+    protected CoyoteResponse response = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Clear facade.
+     */
+    public void clear() {
+        response = null;
+    }
+
+
+    public void finish() {
+
+        response.setSuspended(true);
+
+    }
+
+
+    public boolean isFinished() {
+
+        return response.isSuspended();
+
+    }
+
+
+    // ------------------------------------------------ ServletResponse Methods
+
+
+    public String getCharacterEncoding() {
+        return response.getCharacterEncoding();
+    }
+
+
+    public ServletOutputStream getOutputStream()
+        throws IOException {
+
+        //        if (isFinished())
+        //            throw new IllegalStateException
+        //                (/*sm.getString("responseFacade.finished")*/);
+
+        ServletOutputStream sos = response.getOutputStream();
+        if (isFinished())
+            response.setSuspended(true);
+        return (sos);
+
+    }
+
+
+    public PrintWriter getWriter()
+        throws IOException {
+
+        //        if (isFinished())
+        //            throw new IllegalStateException
+        //                (/*sm.getString("responseFacade.finished")*/);
+
+        PrintWriter writer = response.getWriter();
+        if (isFinished())
+            response.setSuspended(true);
+        return (writer);
+
+    }
+
+
+    public void setContentLength(int len) {
+
+        if (isCommitted())
+            return;
+
+        response.setContentLength(len);
+
+    }
+
+
+    public void setContentType(String type) {
+
+        if (isCommitted())
+            return;
+
+        response.setContentType(type);
+
+    }
+
+
+    public void setBufferSize(int size) {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.setBufferSize(size);
+
+    }
+
+
+    public int getBufferSize() {
+        return response.getBufferSize();
+    }
+
+
+    public void flushBuffer()
+        throws IOException {
+
+        if (isFinished())
+            //            throw new IllegalStateException
+            //                (/*sm.getString("responseFacade.finished")*/);
+            return;
+
+        response.setAppCommitted(true);
+
+        response.flushBuffer();
+
+    }
+
+
+    public void resetBuffer() {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.resetBuffer();
+
+    }
+
+
+    public boolean isCommitted() {
+        return (response.isAppCommitted());
+    }
+
+
+    public void reset() {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.reset();
+
+    }
+
+
+    public void setLocale(Locale loc) {
+
+        if (isCommitted())
+            return;
+
+        response.setLocale(loc);
+    }
+
+
+    public Locale getLocale() {
+        return response.getLocale();
+    }
+
+
+    public void addCookie(Cookie cookie) {
+
+        if (isCommitted())
+            return;
+
+        response.addCookie(cookie);
+
+    }
+
+
+    public boolean containsHeader(String name) {
+        return response.containsHeader(name);
+    }
+
+
+    public String encodeURL(String url) {
+        return response.encodeURL(url);
+    }
+
+
+    public String encodeRedirectURL(String url) {
+        return response.encodeRedirectURL(url);
+    }
+
+
+    public String encodeUrl(String url) {
+        return response.encodeURL(url);
+    }
+
+
+    public String encodeRedirectUrl(String url) {
+        return response.encodeRedirectURL(url);
+    }
+
+
+    public void sendError(int sc, String msg)
+        throws IOException {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.setAppCommitted(true);
+
+        response.sendError(sc, msg);
+
+    }
+
+
+    public void sendError(int sc)
+        throws IOException {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.setAppCommitted(true);
+
+        response.sendError(sc);
+
+    }
+
+
+    public void sendRedirect(String location)
+        throws IOException {
+
+        if (isCommitted())
+            throw new IllegalStateException
+                (/*sm.getString("responseBase.reset.ise")*/);
+
+        response.setAppCommitted(true);
+
+        response.sendRedirect(location);
+
+    }
+
+
+    public void setDateHeader(String name, long date) {
+
+        if (isCommitted())
+            return;
+
+        response.setDateHeader(name, date);
+
+    }
+
+
+    public void addDateHeader(String name, long date) {
+
+        if (isCommitted())
+            return;
+
+        response.addDateHeader(name, date);
+
+    }
+
+
+    public void setHeader(String name, String value) {
+
+        if (isCommitted())
+            return;
+
+        response.setHeader(name, value);
+
+    }
+
+
+    public void addHeader(String name, String value) {
+
+        if (isCommitted())
+            return;
+
+        response.addHeader(name, value);
+
+    }
+
+
+    public void setIntHeader(String name, int value) {
+
+        if (isCommitted())
+            return;
+
+        response.setIntHeader(name, value);
+
+    }
+
+
+    public void addIntHeader(String name, int value) {
+
+        if (isCommitted())
+            return;
+
+        response.addIntHeader(name, value);
+
+    }
+
+
+    public void setStatus(int sc) {
+
+        if (isCommitted())
+            return;
+
+        response.setStatus(sc);
+
+    }
+
+
+    public void setStatus(int sc, String sm) {
+
+        if (isCommitted())
+            return;
+
+        response.setStatus(sc, sm);
+
+    }
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteServerSocketFactory.java b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteServerSocketFactory.java
new file mode 100644
index 0000000..59b1c4d
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteServerSocketFactory.java
@@ -0,0 +1,222 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat4;
+
+import java.io.File;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+
+
+/**
+ * This socket factory holds secure socket factory parameters. Besides the usual
+ * configuration mechanism based on setting JavaBeans properties, this
+ * component may also be configured by passing a series of attributes set
+ * with calls to <code>setAttribute()</code>.  The following attribute
+ * names are recognized, with default values in square brackets:
+ * <ul>
+ * <li><strong>algorithm</strong> - Certificate encoding algorithm
+ *     to use. [SunX509]</li>
+ * <li><strong>clientAuth</strong> - Require client authentication if
+ *     set to <code>true</code>. Want client authentication if set to
+ *     <code>want</code>. (Note: Only supported in the JSSE included with 
+ *     J2SDK 1.4 and above.  Prior versions of JSSE and PureTLS will treat 
+ *     'want' as 'false'.) [false]</li>
+ * <li><strong>keystoreFile</strong> - Pathname to the Key Store file to be
+ *     loaded.  This must be an absolute path, or a relative path that
+ *     is resolved against the "catalina.base" system property.
+ *     ["./keystore" in the user home directory]</li>
+ * <li><strong>keystorePass</strong> - Password for the Key Store file to be
+ *     loaded. ["changeit"]</li>
+ * <li><strong>keystoreType</strong> - Type of the Key Store file to be
+ *     loaded. ["JKS"]</li>
+ * <li><strong>protocol</strong> - SSL protocol to use. [TLS]</li>
+ * </ul>
+ *
+ * @author Harish Prabandham
+ * @author Costin Manolache
+ * @author Craig McClanahan
+ */
+
+public class CoyoteServerSocketFactory
+    implements org.apache.catalina.net.ServerSocketFactory {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Certificate encoding algorithm to be used.
+     */
+    private String algorithm = null;
+
+    public String getAlgorithm() {
+        return (this.algorithm);
+    }
+
+    public void setAlgorithm(String algorithm) {
+        this.algorithm = algorithm;
+    }
+
+
+    /**
+     * Should we require client authentication?
+     */
+    private String clientAuth = "false";
+
+    public String getClientAuth() {
+        return (this.clientAuth);
+    }
+
+    public void setClientAuth(String clientAuth) {
+        this.clientAuth = clientAuth;
+    }
+
+
+    /**
+     * Pathname to the key store file to be used.
+     */
+    private String keystoreFile =
+        System.getProperty("user.home") + File.separator + ".keystore";
+
+    public String getKeystoreFile() {
+        return (this.keystoreFile);
+    }
+
+    public void setKeystoreFile(String keystoreFile) {
+      
+        File file = new File(keystoreFile);
+        if (!file.isAbsolute())
+            file = new File(System.getProperty("catalina.base"),
+                            keystoreFile);
+        this.keystoreFile = file.getAbsolutePath();
+    }
+
+    /**
+     * Pathname to the random file to be used.
+     */
+    private String randomFile =
+        System.getProperty("user.home") + File.separator + "random.pem";
+
+    public String getRandomFile() {
+        return (this.randomFile);
+    }
+
+    public void setRandomFile(String randomFile) {
+      
+        File file = new File(randomFile);
+        if (!file.isAbsolute())
+            file = new File(System.getProperty("catalina.base"),
+                            randomFile);
+        this.randomFile = file.getAbsolutePath();
+    }
+
+    /**
+     * Pathname to the root list to be used.
+     */
+    private String rootFile =
+        System.getProperty("user.home") + File.separator + "root.pem";
+
+    public String getRootFile() {
+        return (this.rootFile);
+    }
+
+    public void setRootFile(String rootFile) {
+      
+        File file = new File(rootFile);
+        if (!file.isAbsolute())
+            file = new File(System.getProperty("catalina.base"),
+                            rootFile);
+        this.rootFile = file.getAbsolutePath();
+    }
+     
+    /**
+     * Password for accessing the key store file.
+     */
+    private String keystorePass = "changeit";
+
+    public String getKeystorePass() {
+        return (this.keystorePass);
+    }
+
+    public void setKeystorePass(String keystorePass) {
+        this.keystorePass = keystorePass;
+    }
+
+
+    /**
+     * Storeage type of the key store file to be used.
+     */
+    private String keystoreType = "JKS";
+
+    public String getKeystoreType() {
+        return (this.keystoreType);
+    }
+
+    public void setKeystoreType(String keystoreType) {
+        this.keystoreType = keystoreType;
+    }
+
+
+    /**
+     * SSL protocol variant to use.
+     */
+    private String protocol = "TLS";
+
+    public String getProtocol() {
+        return (this.protocol);
+    }
+
+    public void setProtocol(String protocol) {
+        this.protocol = protocol;
+    }
+
+
+    /**
+     * SSL implementation to use.
+     */
+    private String sslImplementation = null;
+
+    public String getSSLImplementation() {
+        return (this.sslImplementation);
+    }
+
+    public void setSSLImplementation(String sslImplementation) {
+        this.sslImplementation = sslImplementation;
+    }
+
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    public ServerSocket createSocket(int port) {
+        return (null);
+    }
+
+
+    public ServerSocket createSocket(int port, int backlog) {
+        return (null);
+    }
+
+
+    public ServerSocket createSocket(int port, int backlog,
+                                     InetAddress ifAddress) {
+        return (null);
+    }
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteWriter.java b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteWriter.java
new file mode 100644
index 0000000..3bdba6e
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/CoyoteWriter.java
@@ -0,0 +1,267 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat4;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * Coyote implementation of the servlet writer.
+ * 
+ * @author Remy Maucherat
+ */
+final class CoyoteWriter
+    extends PrintWriter {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    private static final char[] LINE_SEP = { '\r', '\n' };
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    protected OutputBuffer ob;
+    protected boolean error = false;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public CoyoteWriter(OutputBuffer ob) {
+        super(ob);
+        this.ob = ob;
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Recycle.
+     */
+    void recycle() {
+        error = false;
+    }
+
+
+    // --------------------------------------------------------- Writer Methods
+
+
+    public void flush() {
+
+        if (error)
+            return;
+
+        try {
+            ob.flush();
+        } catch (IOException e) {
+            error = true;
+        }
+
+    }
+
+
+    public void close() {
+
+        // We don't close the PrintWriter - super() is not called,
+        // so the stream can be reused. We close ob.
+        try {
+            ob.close();
+        } catch (IOException ex ) {
+            ;
+        }
+        error = false;
+
+    }
+
+
+    public boolean checkError() {
+        flush();
+        return error;
+    }
+
+
+    public void write(int c) {
+
+        if (error)
+            return;
+
+        try {
+            ob.write(c);
+        } catch (IOException e) {
+            error = true;
+        }
+
+    }
+
+
+    public void write(char buf[], int off, int len) {
+
+        if (error)
+            return;
+
+        try {
+            ob.write(buf, off, len);
+        } catch (IOException e) {
+            error = true;
+        }
+
+    }
+
+
+    public void write(char buf[]) {
+	write(buf, 0, buf.length);
+    }
+
+
+    public void write(String s, int off, int len) {
+
+        if (error)
+            return;
+
+        try {
+            ob.write(s, off, len);
+        } catch (IOException e) {
+            error = true;
+        }
+
+    }
+
+
+    public void write(String s) {
+        write(s, 0, s.length());
+    }
+
+
+    // ---------------------------------------------------- PrintWriter Methods
+
+
+    public void print(boolean b) {
+        if (b) {
+            write("true");
+        } else {
+            write("false");
+        }
+    }
+
+
+    public void print(char c) {
+        write(c);
+    }
+
+
+    public void print(int i) {
+        write(String.valueOf(i));
+    }
+
+
+    public void print(long l) {
+        write(String.valueOf(l));
+    }
+
+
+    public void print(float f) {
+        write(String.valueOf(f));
+    }
+
+
+    public void print(double d) {
+        write(String.valueOf(d));
+    }
+
+
+    public void print(char s[]) {
+        write(s);
+    }
+
+
+    public void print(String s) {
+        if (s == null) {
+            s = "null";
+        }
+        write(s);
+    }
+
+
+    public void print(Object obj) {
+        write(String.valueOf(obj));
+    }
+
+
+    public void println() {
+        write(LINE_SEP);
+    }
+
+
+    public void println(boolean b) {
+        print(b);
+        println();
+    }
+
+
+    public void println(char c) {
+        print(c);
+        println();
+    }
+
+
+    public void println(int i) {
+        print(i);
+        println();
+    }
+
+
+    public void println(long l) {
+        print(l);
+        println();
+    }
+
+
+    public void println(float f) {
+        print(f);
+        println();
+    }
+
+
+    public void println(double d) {
+        print(d);
+        println();
+    }
+
+
+    public void println(char c[]) {
+        print(c);
+        println();
+    }
+
+
+    public void println(String s) {
+        print(s);
+        println();
+    }
+
+
+    public void println(Object o) {
+        print(o);
+        println();
+    }
+
+
+}
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/LocalStrings.properties b/connectors/coyote/src/java/org/apache/coyote/tomcat4/LocalStrings.properties
new file mode 100644
index 0000000..4621738
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/LocalStrings.properties
@@ -0,0 +1,38 @@
+
+#
+# CoyoteConnector
+#
+
+coyoteConnector.alreadyInitialized=The connector has already been initialized
+coyoteConnector.alreadyStarted=The connector has already been started
+coyoteConnector.notStarted=Coyote connector has not been started
+coyoteConnector.protocolHandlerDestroyFailed=Protocol handler destroy failed: {0}
+coyoteConnector.protocolHandlerInitializationFailed=Protocol handler initialization failed: {0}
+coyoteConnector.protocolHandlerInstantiationFailed=Protocol handler instantiation failed: {0}
+coyoteConnector.protocolHandlerStartFailed=Protocol handler start failed: {0}
+
+#
+# CoyoteAdapter
+#
+
+coyoteAdapter.service=An exception or error occurred in the container during the request processing
+
+#
+# CoyoteResponse
+#
+
+coyoteResponse.getOutputStream.ise=getWriter() has already been called for this response
+coyoteResponse.getWriter.ise=getOutputStream() has already been called for this response
+coyoteResponse.resetBuffer.ise=Cannot reset buffer after response has been committed
+coyoteResponse.sendError.ise=Cannot call sendError() after the response has been committed
+coyoteResponse.sendRedirect.ise=Cannot call sendRedirect() after the response has been committed
+coyoteResponse.setBufferSize.ise=Cannot change buffer size after data has been written
+
+#
+# CoyoteRequest
+#
+
+coyoteRequest.getInputStream.ise=getReader() has already been called for this request
+coyoteRequest.getReader.ise=getInputStream() has already been called for this request
+coyoteRequest.sessionCreateCommitted=Cannot create a session after the response has been committed
+coyoteRequest.setAttribute.namenull=Cannot call setAttribute with a null name
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/LocalStrings_es.properties b/connectors/coyote/src/java/org/apache/coyote/tomcat4/LocalStrings_es.properties
new file mode 100644
index 0000000..2e94616
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/LocalStrings_es.properties
@@ -0,0 +1,38 @@
+
+#
+# CoyoteConnector
+#
+
+coyoteConnector.alreadyInitialized=Ya ha sido inicializado el conector
+coyoteConnector.alreadyStarted=Ya ha sido arrancado el conector
+coyoteConnector.notStarted=El conector Coyote no ha sido arrancado
+coyoteConnector.protocolHandlerDestroyFailed=Falló la destrucción del manejador de protocolo: {0}
+coyoteConnector.protocolHandlerInitializationFailed=Falló la inicialización del manejador de protocolo: {0}
+coyoteConnector.protocolHandlerInstantiationFailed=Falló la instanciación del manejador de protocolo: {0}
+coyoteConnector.protocolHandlerStartFailed=Falló el arranque del manejador de protocolo: {0}
+
+#
+# CoyoteAdapter
+#
+
+coyoteAdapter.service=Ha tenido lugar una excepción o error en el contenedor durante el procesamiento del requerimiento
+
+#
+# CoyoteResponse
+#
+
+coyoteResponse.getOutputStream.ise=getWriter() ya ha sido llamado para esta respuesta
+coyoteResponse.getWriter.ise=getOutputStream() ya ha sido llamado para esta respuesta
+coyoteResponse.resetBuffer.ise=No puedo limpiar el búfer después de que la repuesta ha sido llevada a cabo
+coyoteResponse.sendError.ise=No puedo llamar a sendError() tras llevar a cabo la respuesta
+coyoteResponse.sendRedirect.ise=No puedo llamar a sendRedirect() tras llevar a cabo la respuesta
+coyoteResponse.setBufferSize.ise=No puedo cambiar la medida del búfer tras escribir los datos
+
+#
+# CoyoteRequest
+#
+
+coyoteRequest.getInputStream.ise=getReader() ya ha sido llamado para este requerimiento
+coyoteRequest.getReader.ise=getInputStream() ya ha sido llamado para este requerimiento
+coyoteRequest.sessionCreateCommitted=No puedo crear una sesión después de llevar a cabo la respueta
+coyoteRequest.setAttribute.namenull=No pudeo llamar a setAttribute con un nombre nulo
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/LocalStrings_fr.properties b/connectors/coyote/src/java/org/apache/coyote/tomcat4/LocalStrings_fr.properties
new file mode 100644
index 0000000..105a5bd
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/LocalStrings_fr.properties
@@ -0,0 +1,38 @@
+
+#
+# CoyoteConnector
+#
+
+coyoteConnector.alreadyInitialized=Le connecteur a déjà été initialisé
+coyoteConnector.alreadyStarted=Le connecteur a déjà été démarré
+coyoteConnector.notStarted=Le connecteur Coyote n''a pas été démarré
+coyoteConnector.protocolHandlerDestroyFailed=La destruction du gestionnaire de protocole a échoué: {0}
+coyoteConnector.protocolHandlerInitializationFailed=L''initialisation du gestionnaire de protocole a échoué: {0}
+coyoteConnector.protocolHandlerInstantiationFailed=L''instantiation du gestionnaire de protocole a échoué: {0}
+coyoteConnector.protocolHandlerStartFailed=Le démarrage du gestionnaire de protocole a échoué: {0}
+
+#
+# CoyoteAdapter
+#
+
+coyoteAdapter.service=Une exception ou une erreur s''est produite dans le conteneur durant le traitement de la requête
+
+#
+# CoyoteResponse
+#
+
+coyoteResponse.getOutputStream.ise="getWriter()" a déjà été appelé pour cette réponse
+coyoteResponse.getWriter.ise="getOutputStream()" a déjà été appelé pour cette réponse
+coyoteResponse.resetBuffer.ise=Impossible de remettre à zéro le tampon après que la réponse ait été envoyée
+coyoteResponse.sendError.ise=Impossible d''appeler "sendError()" après que la réponse ait été envoyée
+coyoteResponse.sendRedirect.ise=Impossible d''appeler "sendRedirect()" après que la réponse ait été envoyée
+coyoteResponse.setBufferSize.ise=Impossible de changer la taille du tampon après que les données aient été écrites
+
+#
+# CoyoteRequest
+#
+
+coyoteRequest.getInputStream.ise="getReader()" a déjà été appelé pour cette requête
+coyoteRequest.getReader.ise="getInputStream()" a déjà été appelé pour cette requête
+coyoteRequest.sessionCreateCommitted=Impossible de créer une sessionaprès que la réponse ait été envoyée
+coyoteRequest.setAttribute.namenull=Impossible d''appeler "setAttribute" avec un nom nul
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/LocalStrings_ja.properties b/connectors/coyote/src/java/org/apache/coyote/tomcat4/LocalStrings_ja.properties
new file mode 100644
index 0000000..c3ab099
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/LocalStrings_ja.properties
@@ -0,0 +1,38 @@
+
+#
+# CoyoteConnector
+#
+
+coyoteConnector.alreadyInitialized=\u30b3\u30cd\u30af\u30bf\u306f\u65e2\u306b\u521d\u671f\u5316\u3055\u308c\u3066\u3044\u307e\u3059
+coyoteConnector.alreadyStarted=\u30b3\u30cd\u30af\u30bf\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+coyoteConnector.notStarted=Coyote\u30b3\u30cd\u30af\u30bf\u306f\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+coyoteConnector.protocolHandlerDestroyFailed=\u30d7\u30ed\u30c8\u30b3\u30eb\u30cf\u30f3\u30c9\u30e9\u306e\u5ec3\u68c4\u306b\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+coyoteConnector.protocolHandlerInitializationFailed=\u30d7\u30ed\u30c8\u30b3\u30eb\u30cf\u30f3\u30c9\u30e9\u306e\u521d\u671f\u5316\u306b\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+coyoteConnector.protocolHandlerInstantiationFailed=\u30d7\u30ed\u30c8\u30b3\u30eb\u30cf\u30f3\u30c9\u30e9\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u5316\u306b\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+coyoteConnector.protocolHandlerStartFailed=\u30d7\u30ed\u30c8\u30b3\u30eb\u30cf\u30f3\u30c9\u30e9\u306e\u8d77\u52d5\u306b\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+
+#
+# CoyoteAdapter
+#
+
+coyoteAdapter.service=\u30ea\u30af\u30a8\u30b9\u30c8\u306e\u51e6\u7406\u4e2d\u306b\u30b3\u30cd\u30af\u30bf\u3067\u4f8b\u5916\u53c8\u306f\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
+
+#
+# CoyoteResponse
+#
+
+coyoteResponse.getOutputStream.ise=getWriter()\u306f\u3053\u306e\u30ec\u30b9\u30dd\u30f3\u30b9\u306b\u5bfe\u3057\u3066\u65e2\u306b\u547c\u3073\u51fa\u3055\u308c\u3066\u3044\u307e\u3059
+coyoteResponse.getWriter.ise=getOutputStream()\u306f\u3053\u306e\u30ec\u30b9\u30dd\u30f3\u30b9\u306b\u5bfe\u3057\u3066\u65e2\u306b\u547c\u3073\u51fa\u3055\u308c\u3066\u3044\u307e\u3059
+coyoteResponse.resetBuffer.ise=\u30ec\u30b9\u30dd\u30f3\u30b9\u304c\u30b3\u30df\u30c3\u30c8\u3055\u308c\u305f\u5f8c\u3067\u30d0\u30c3\u30d5\u30a1\u3092\u30ea\u30bb\u30c3\u30c8\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+coyoteResponse.sendError.ise=\u30ec\u30b9\u30dd\u30f3\u30b9\u304c\u30b3\u30df\u30c3\u30c8\u3055\u308c\u305f\u5f8c\u3067sendError()\u3092\u547c\u3073\u51fa\u3059\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+coyoteResponse.sendRedirect.ise=\u30ec\u30b9\u30dd\u30f3\u30b9\u304c\u30b3\u30df\u30c3\u30c8\u3055\u308c\u305f\u5f8c\u3067sendRedirect()\u3092\u547c\u3073\u51fa\u3059\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+coyoteResponse.setBufferSize.ise=\u30c7\u30fc\u30bf\u304c\u65e2\u306b\u66f8\u304d\u8fbc\u307e\u308c\u305f\u5f8c\u3067\u30d0\u30c3\u30d5\u30a1\u30b5\u30a4\u30ba\u3092\u5909\u66f4\u3059\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
+
+#
+# CoyoteRequest
+#
+
+coyoteRequest.getInputStream.ise=getReader()\u306f\u3053\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u306b\u5bfe\u3057\u3066\u65e2\u306b\u547c\u3073\u51fa\u3055\u308c\u3066\u3044\u307e\u3059
+coyoteRequest.getReader.ise=getInputStream()\u306f\u3053\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u306b\u5bfe\u3057\u3066\u65e2\u306b\u547c\u3073\u51fa\u3055\u308c\u3066\u3044\u307e\u3059
+coyoteRequest.sessionCreateCommitted=\u30ec\u30b9\u30dd\u30f3\u30b9\u3092\u30b3\u30df\u30c3\u30c8\u3057\u305f\u5f8c\u3067\u30bb\u30c3\u30b7\u30e7\u30f3\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093
+coyoteRequest.setAttribute.namenull=setAttribute\u3092\u540d\u524d\u3092\u6307\u5b9a\u305b\u305a\u306b\u547c\u3073\u51fa\u3059\u3053\u3068\u306f\u3067\u304d\u307e\u305b\u3093
diff --git a/connectors/coyote/src/java/org/apache/coyote/tomcat4/OutputBuffer.java b/connectors/coyote/src/java/org/apache/coyote/tomcat4/OutputBuffer.java
new file mode 100644
index 0000000..d59fbf0
--- /dev/null
+++ b/connectors/coyote/src/java/org/apache/coyote/tomcat4/OutputBuffer.java
@@ -0,0 +1,671 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.tomcat4;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Hashtable;
+
+import org.apache.catalina.connector.ClientAbortException;
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.Response;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.C2BConverter;
+import org.apache.tomcat.util.buf.CharChunk;
+
+
+/**
+ * The buffer used by Tomcat response. This is a derivative of the Tomcat 3.3
+ * OutputBuffer, with the removal of some of the state handling (which in 
+ * Coyote is mostly the Processor's responsability).
+ *
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ */
+public class OutputBuffer extends Writer
+    implements ByteChunk.ByteOutputChannel, CharChunk.CharOutputChannel {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final String DEFAULT_ENCODING = 
+        org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
+    public static final int DEFAULT_BUFFER_SIZE = 8*1024;
+    static final int debug = 0;
+
+
+    // The buffer can be used for byte[] and char[] writing
+    // ( this is needed to support ServletOutputStream and for
+    // efficient implementations of templating systems )
+    public final int INITIAL_STATE = 0;
+    public final int CHAR_STATE = 1;
+    public final int BYTE_STATE = 2;
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The byte buffer.
+     */
+    private ByteChunk bb;
+
+
+    /**
+     * The chunk buffer.
+     */
+    private CharChunk cb;
+
+
+    /**
+     * State of the output buffer.
+     */
+    private int state = 0;
+
+
+    /**
+     * Number of bytes written.
+     */
+    private int bytesWritten = 0;
+
+
+    /**
+     * Number of chars written.
+     */
+    private int charsWritten = 0;
+
+
+    /**
+     * Flag which indicates if the output buffer is closed.
+     */
+    private boolean closed = false;
+
+
+    /**
+     * Do a flush on the next operation.
+     */
+    private boolean doFlush = false;
+
+
+    /**
+     * Byte chunk used to output bytes.
+     */
+    private ByteChunk outputChunk = new ByteChunk();
+
+
+    /**
+     * Encoding to use.
+     */
+    private String enc;
+
+
+    /**
+     * Encoder is set.
+     */
+    private boolean gotEnc = false;
+
+
+    /**
+     * List of encoders.
+     */
+    protected Hashtable encoders = new Hashtable();
+
+
+    /**
+     * Current char to byte converter.
+     */
+    protected C2BConverter conv;
+
+
+    /**
+     * Associated Coyote response.
+     */
+    private Response coyoteResponse;
+
+
+    /**
+     * Suspended flag. All output bytes will be swallowed if this is true.
+     */
+    private boolean suspended = false;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor. Allocate the buffer with the default buffer size.
+     */
+    public OutputBuffer() {
+
+        this(DEFAULT_BUFFER_SIZE);
+
+    }
+
+
+    /**
+     * Alternate constructor which allows specifying the initial buffer size.
+     * 
+     * @param size Buffer size to use
+     */
+    public OutputBuffer(int size) {
+
+        bb = new ByteChunk(size);
+        bb.setLimit(size);
+        bb.setByteOutputChannel(this);
+        cb = new CharChunk(size);
+        cb.setCharOutputChannel(this);
+        cb.setLimit(size);
+
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Associated Coyote response.
+     * 
+     * @param coyoteResponse Associated Coyote response
+     */
+    public void setResponse(Response coyoteResponse) {
+	this.coyoteResponse = coyoteResponse;
+    }
+
+
+    /**
+     * Get associated Coyote response.
+     * 
+     * @return the associated Coyote response
+     */
+    public Response getResponse() {
+        return this.coyoteResponse;
+    }
+
+
+    /**
+     * Is the response output suspended ?
+     * 
+     * @return suspended flag value
+     */
+    public boolean isSuspended() {
+        return this.suspended;
+    }
+
+
+    /**
+     * Set the suspended flag.
+     * 
+     * @param suspended New suspended flag value
+     */
+    public void setSuspended(boolean suspended) {
+        this.suspended = suspended;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Recycle the output buffer.
+     */
+    public void recycle() {
+
+	if (debug > 0)
+            log("recycle()");
+
+	state = INITIAL_STATE;
+	bytesWritten = 0;
+	charsWritten = 0;
+
+        cb.recycle();
+        bb.recycle(); 
+        closed = false;
+        suspended = false;
+
+        if (conv!= null) {
+            conv.recycle();
+        }
+
+        gotEnc = false;
+        enc = null;
+
+    }
+
+
+    /**
+     * Close the output buffer. This tries to calculate the response size if 
+     * the response has not been committed yet.
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public void close()
+        throws IOException {
+
+        if (closed)
+            return;
+        if (suspended)
+            return;
+
+        if ((!coyoteResponse.isCommitted()) 
+            && (coyoteResponse.getContentLength() == -1)) {
+            // Flushing the char buffer
+            if (state == CHAR_STATE) {
+                cb.flushBuffer();
+                state = BYTE_STATE;
+            }
+            // If this didn't cause a commit of the response, the final content
+            // length can be calculated
+            if (!coyoteResponse.isCommitted()) {
+                coyoteResponse.setContentLength(bb.getLength());
+            }
+        }
+
+        doFlush(false);
+        closed = true;
+
+        coyoteResponse.finish();
+
+    }
+
+
+    /**
+     * Flush bytes or chars contained in the buffer.
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public void flush()
+        throws IOException {
+        doFlush(true);
+    }
+
+
+    /**
+     * Flush bytes or chars contained in the buffer.
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    protected void doFlush(boolean realFlush)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        doFlush = true;
+        if (state == CHAR_STATE) {
+            cb.flushBuffer();
+            bb.flushBuffer();
+            state = BYTE_STATE;
+        } else if (state == BYTE_STATE) {
+            bb.flushBuffer();
+        } else if (state == INITIAL_STATE)
+            realWriteBytes(null, 0, 0);       // nothing written yet
+        doFlush = false;
+
+        if (realFlush) {
+            coyoteResponse.action(ActionCode.ACTION_CLIENT_FLUSH, 
+                                  coyoteResponse);
+            // If some exception occurred earlier, or if some IOE occurred
+            // here, notify the servlet with an IOE
+            if (coyoteResponse.isExceptionPresent()) {
+                throw new ClientAbortException
+                    (coyoteResponse.getErrorException());
+            }
+        }
+
+    }
+
+
+    // ------------------------------------------------- Bytes Handling Methods
+
+
+    /** 
+     * Sends the buffer data to the client output, checking the
+     * state of Response and calling the right interceptors.
+     * 
+     * @param buf Byte buffer to be written to the response
+     * @param off Offset
+     * @param cnt Length
+     * 
+     * @throws IOException An underlying IOException occurred
+     */
+    public void realWriteBytes(byte buf[], int off, int cnt)
+	throws IOException {
+
+        if (debug > 2)
+            log("realWrite(b, " + off + ", " + cnt + ") " + coyoteResponse);
+
+        if (closed)
+            return;
+        if (coyoteResponse == null)
+            return;
+
+        // If we really have something to write
+        if (cnt > 0) {
+            // real write to the adapter
+            outputChunk.setBytes(buf, off, cnt);
+            try {
+                coyoteResponse.doWrite(outputChunk);
+            } catch (IOException e) {
+                // An IOException on a write is almost always due to
+                // the remote client aborting the request.  Wrap this
+                // so that it can be handled better by the error dispatcher.
+                throw new ClientAbortException(e);
+            }
+        }
+
+    }
+
+
+    public void write(byte b[], int off, int len) throws IOException {
+
+        if (suspended)
+            return;
+
+        if (state == CHAR_STATE)
+            cb.flushBuffer();
+        state = BYTE_STATE;
+        writeBytes(b, off, len);
+
+    }
+
+
+    private void writeBytes(byte b[], int off, int len) 
+        throws IOException {
+
+        if (closed)
+            return;
+        if (debug > 0)
+            log("write(b,off,len)");
+
+        bb.append(b, off, len);
+        bytesWritten += len;
+
+        // if called from within flush(), then immediately flush
+        // remaining bytes
+        if (doFlush) {
+            bb.flushBuffer();
+        }
+
+    }
+
+
+    // XXX Char or byte ?
+    public void writeByte(int b)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        if (state == CHAR_STATE)
+            cb.flushBuffer();
+        state = BYTE_STATE;
+
+        if (debug > 0)
+            log("write(b)");
+
+        bb.append( (byte)b );
+        bytesWritten++;
+
+    }
+
+
+    // ------------------------------------------------- Chars Handling Methods
+
+
+    public void write(int c)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state = CHAR_STATE;
+
+        if (debug > 0)
+            log("writeChar(b)");
+
+        cb.append((char) c);
+        charsWritten++;
+
+    }
+
+
+    public void write(char c[])
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        write(c, 0, c.length);
+
+    }
+
+
+    public void write(char c[], int off, int len)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state = CHAR_STATE;
+
+        if (debug > 0)
+            log("write(c,off,len)" + cb.getLength() + " " + cb.getLimit());
+
+        cb.append(c, off, len);
+        charsWritten += len;
+
+    }
+
+
+    public void write(StringBuffer sb)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state = CHAR_STATE;
+
+        if (debug > 1)
+            log("write(s,off,len)");
+
+        int len = sb.length();
+        charsWritten += len;
+        cb.append(sb);
+
+    }
+
+
+    /**
+     * Append a string to the buffer
+     */
+    public void write(String s, int off, int len)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state=CHAR_STATE;
+
+        if (debug > 1)
+            log("write(s,off,len)");
+
+        charsWritten += len;
+        if (s==null)
+            s="null";
+        cb.append( s, off, len );
+
+    }
+
+
+    public void write(String s)
+        throws IOException {
+
+        if (suspended)
+            return;
+
+        state = CHAR_STATE;
+        if (s==null)
+            s="null";
+        write(s, 0, s.length());
+
+    } 
+
+
+    public void flushChars()
+        throws IOException {
+
+        if (debug > 0)
+            log("flushChars() " + cb.getLength());
+
+        cb.flushBuffer();
+        state = BYTE_STATE;
+
+    }
+
+
+    public boolean flushCharsNeeded() {
+        return state == CHAR_STATE;
+    }
+
+
+    public void setEncoding(String s) {
+        enc = s;
+    }
+
+
+    public void realWriteChars(char c[], int off, int len) 
+        throws IOException {
+
+        if (debug > 0)
+            log("realWrite(c,o,l) " + cb.getOffset() + " " + len);
+
+        if (!gotEnc)
+            setConverter();
+
+        if (debug > 0)
+            log("encoder:  " + conv + " " + gotEnc);
+
+        conv.convert(c, off, len);
+        conv.flushBuffer();	// ???
+
+    }
+
+
+    protected void setConverter() {
+
+        if (coyoteResponse != null)
+            enc = coyoteResponse.getCharacterEncoding();
+
+        if (debug > 0)
+            log("Got encoding: " + enc);
+
+        gotEnc = true;
+        if (enc == null)
+            enc = DEFAULT_ENCODING;
+        conv = (C2BConverter) encoders.get(enc);
+        if (conv == null) {
+            try {
+                conv = new C2BConverter(bb, enc);
+                encoders.put(enc, conv);
+            } catch (IOException e) {
+                conv = (C2BConverter) encoders.get(DEFAULT_ENCODING);
+                if (conv == null) {
+                    try {
+                        conv = new C2BConverter(bb, DEFAULT_ENCODING);
+                        encoders.put(DEFAULT_ENCODING, conv);
+                    } catch (IOException ex) {
+                        // Ignore
+                    }
+                }
+            }
+        }
+    }
+
+    
+    // --------------------  BufferedOutputStream compatibility
+
+
+    /**
+     * Real write - this buffer will be sent to the client
+     */
+    public void flushBytes()
+        throws IOException {
+
+        if (debug > 0)
+            log("flushBytes() " + bb.getLength());
+        bb.flushBuffer();
+
+    }
+
+
+    public int getBytesWritten() {
+        return bytesWritten;
+    }
+
+
+    public int getCharsWritten() {
+        return charsWritten;
+    }
+
+
+    public int getContentWritten() {
+        return bytesWritten + charsWritten;
+    }
+
+
+    /** 
+     * True if this buffer hasn't been used ( since recycle() ) -
+     * i.e. no chars or bytes have been added to the buffer.  
+     */
+    public boolean isNew() {
+        return (bytesWritten == 0) && (charsWritten == 0);
+    }
+
+
+    public void setBufferSize(int size) {
+        if (size > bb.getLimit()) {// ??????
+	    bb.setLimit(size);
+	}
+    }
+
+
+    public void reset() {
+
+        //count=0;
+        bb.recycle();
+        bytesWritten = 0;
+        cb.recycle();
+        charsWritten = 0;
+        gotEnc = false;
+        enc = null;
+
+    }
+
+
+    public int getBufferSize() {
+	return bb.getLimit();
+    }
+
+
+
+    protected void log( String s ) {
+	System.out.println("OutputBuffer: " + s);
+    }
+
+
+}
diff --git a/connectors/coyote/src/test/org/apache/coyote/SimpleAdapter.java b/connectors/coyote/src/test/org/apache/coyote/SimpleAdapter.java
new file mode 100644
index 0000000..615e4c2
--- /dev/null
+++ b/connectors/coyote/src/test/org/apache/coyote/SimpleAdapter.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+package org.apache.coyote;
+
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.MimeHeaders;
+
+/**
+ * Simple adapter.
+ *
+ * @author Remy Maucherat
+ */
+public class Adapter {
+
+
+    public static final String CRLF = "\r\n";
+
+
+    /** 
+     * Service method, which dumps the request to the console.
+     */
+    public void service(Request req, Response res)
+	throws Exception {
+        
+        StringBuffer buf = new StringBuffer();
+        buf.append(req.method());
+        buf.append(" ");
+        buf.append(req.unparsedURI());
+        buf.append(" ");
+        buf.append(req.protocol());
+        buf.append(CRLF);
+        
+        
+        
+    }
+
+
+}
diff --git a/connectors/doc/install_tomcat33.html b/connectors/doc/install_tomcat33.html
new file mode 100644
index 0000000..1a52334
--- /dev/null
+++ b/connectors/doc/install_tomcat33.html
@@ -0,0 +1,24 @@
+<html>
+<head>
+<title>Installing tomcat-connector in tomcat3.3</title>
+</head>
+<body>
+<h2>Installing tomcat-connector in tomcat3.3</h2>
+<ul>
+<li> copy mx4j-jmx.jar ( or jmxri.jar ) to lib/common</li>
+<li> copy tomcat-coyote.jar, tomcat33-coyote.jar, tomcat-jk2.jar,
+tomcat-http11.jar to lib/container</li>
+<li> replace lib/common/connector-util.jar with tomcat-util.jar</li>
+<li> copy commons-logging-api.jar and tomcat33-resources.jar to lib/common</li>
+<li> edit conf/modules.xml and insert:<br />
+&nbsp; &lt;module name="CoyoteConnector"
+&nbsp; javaClass="org.apache.coyote.tomcat3.CoyoteInterceptor2"/&gt; </li>
+<li>edit conf/server.xml and replace Http10Connector and Ajp13Connector  with: <br />
+&nbsp; &lt;CoyoteConnector port="8080" /&gt;
+&nbsp; <br /> and<br />
+&nbsp; &lt;CoyoteConnector port="8009"
+&nbsp; processorClassName="org.apache.jk.server.JkCoyoteHandler"/&gt;
+</li>
+</ul>
+</body>
+</html>
diff --git a/connectors/doc/install_tomcat40.html b/connectors/doc/install_tomcat40.html
new file mode 100644
index 0000000..49bb995
--- /dev/null
+++ b/connectors/doc/install_tomcat40.html
@@ -0,0 +1,28 @@
+<html>
+<head>
+<title>Installing tomcat-connector in tomcat4.0</title>
+</head>
+<body>
+<h2>Installing tomcat-connector in tomcat4.0</h2>
+</body>
+<ul>
+<li>replace tomcat-coyote.jar, tomcat-http11.jar, tomcat-jk2.jar
+and tomcat-util.jar in server/lib</li>
+<li>add tomcat4-coyote.jar to server/lib</li>
+<li>copy mx4j-jmx.jar ( or jmxri.jar ) to common/lib. </li>
+<li>edit server.xml, comment out the original connector (
+org.apache.catalina.connector.http.HttpConnector ) and uncomment the
+coyote connector ( in &lt;Connector className="org.apache.coyote.tomcat4.CoyoteConnector" /&gt; )
+</li>
+<li>also in server.xml, comment out the ajp connector (
+org.apache.ajp.tomcat4.Ajp13Connector ) and add a jk2 connector:
+<br />&nbsp; &lt;Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
+&nbsp; port="8009" minProcessors="5" maxProcessors="75"
+&nbsp; enableLookups="true" redirectPort="8443"
+&nbsp; acceptCount="10" debug="0" connectionTimeout="0"
+&nbsp; useURIValidationHack="false"
+&nbsp; protocolHandlerClassName="org.apache.jk.server.JkCoyoteHandler"/&gt;
+</li>
+</ul>
+</body>
+</html>
\ No newline at end of file
diff --git a/connectors/doc/install_tomcat41.html b/connectors/doc/install_tomcat41.html
new file mode 100644
index 0000000..c764256
--- /dev/null
+++ b/connectors/doc/install_tomcat41.html
@@ -0,0 +1,20 @@
+<html>
+<head>
+<title>Installing tomcat-connector in tomcat4.1</title>
+</head>
+<body>
+<h2>Installing tomcat-connector in tomcat4.1</h2>
+<ul>
+<li>get the new connector ( I'll post the URL soon )</li>
+<li>move mx4j-jmx.jar from server/lib to common/lib. This is not required, but allows you to use JMX. If you run
+ tomcat in a sandbox - you can replace mx4j-jmx with jmxri-1.2, which has policy-based security.</li>
+<li>in server/lib, replace tomcat-util.jar, tomcat-jk2.jar, tomcat-coyote.jar and tomcat-http11.jar with the new
+ versions.</li>
+<li>copy tomcat4-coyote.jar to server/lib. This is a new file containing the coyote adapter for tomcat4, used to
+ be in tomcat-coyote.jar</li>
+<li>remove bin/tomcat-jni.jar ( it is an odd file that causes several
+problems - if you use in-process tomcat we'll provide a different
+mechanism )</li>
+</ul>
+</body>
+</html>
diff --git a/connectors/http11/.cvsignore b/connectors/http11/.cvsignore
new file mode 100644
index 0000000..3995981
--- /dev/null
+++ b/connectors/http11/.cvsignore
@@ -0,0 +1,2 @@
+build
+build.properties
\ No newline at end of file
diff --git a/connectors/http11/build.properties.sample b/connectors/http11/build.properties.sample
new file mode 100644
index 0000000..c793453
--- /dev/null
+++ b/connectors/http11/build.properties.sample
@@ -0,0 +1,37 @@
+# -------------------------------------------------------------------
+# build.properties.sample
+#
+# This is an example "build.properties" file, used to customize 
+# building Jakarta Coyote for your local environment.  
+# Make any changes you need, and rename this file to 
+# "build.properties" 
+#
+# $Id$
+# -------------------------------------------------------------------
+
+
+# -------------------------------------------------------------------
+# CONFIGURATION OPTIONS
+# -------------------------------------------------------------------
+
+# 
+
+
+# -------------------------------------------------------------------
+# EXTERNAL DEPENDENCIES 
+# -------------------------------------------------------------------
+
+# tomcat-util.jar -- Tomcat util package
+tomcat-util.jar=../util/build/lib/tomcat-util.jar
+
+# coyote.jar -- Coyote package
+coyote.jar=../coyote/dist/coyote.jar
+
+# junit.jar -- JUnit classes (http://junit.org)
+junit.jar=/java/junit/junit.jar
+
+# commons-logging.jar -- Commons Logging 1.0 package
+commons-logging.jar=../lib/commons-logging.jar
+
+# ----- Jakarta Regular Expressions Library, version 1.3 -----
+regexp.jar=../lib/jakarta-regexp-1.3.jar
diff --git a/connectors/http11/build.xml b/connectors/http11/build.xml
new file mode 100644
index 0000000..01f71d5
--- /dev/null
+++ b/connectors/http11/build.xml
@@ -0,0 +1,239 @@
+<project name="Coyote" default="compile" basedir=".">
+
+
+<!--
+        "Coyote" connector framework for Jakarta Tomcat
+        $Id$
+-->
+
+
+<!-- ========== Initialize Properties ===================================== -->
+
+
+  <property file="build.properties"/>                <!-- Component local   -->
+  <property file="../build.properties"/>             <!-- Commons local     -->
+  <property file="${user.home}/build.properties"/>   <!-- User local        -->
+
+
+<!-- ========== External Dependencies ===================================== -->
+
+
+  <!-- The directories corresponding to your necessary dependencies -->
+  <property name="junit.home"              value="/usr/local/junit3.5"/>
+
+  <!-- Dependencies within jakarta-tomcat-connectors -->
+  <property name="util.home" value="../util"/>
+  <property name="coyote.home" value="../coyote"/>
+
+
+<!-- ========== Derived Values ============================================ -->
+
+
+  <!-- The locations of necessary jar files -->
+  <property name="tomcat-util.jar"  value="${util.home}/build/lib/tomcat-util.jar"/>
+  <property name="tomcat-coyote.jar" value="${coyote.home}/build/lib/tomcat-coyote.jar"/>
+  <property name="tomcat33-coyote.jar" 
+          value="${coyote.home}/build/lib/tomcat33-coyote.jar"/>
+  <property name="junit.jar"        value="${junit.home}/junit.jar"/>
+  <property name="jmx.jar" location="../lib/mx4j.jar" />
+  <property name="commons-modeler.jar" location="../../jakarta-commons/modeler/dist/commons-modeler.jar" />
+  <property name="commons-logging.jar" value="../lib/commons-logging.jar" />
+  <property name="regexp.jar" value="../lib/jakarta-regexp-1.3.jar" />
+
+<!-- ========== Component Declarations ==================================== -->
+
+
+  <!-- The name of this component -->
+  <property name="component.name"          value="http11"/>
+
+  <!-- The title of this component -->
+  <property name="component.title"         value="Coyote HTTP/1.1 Connector"/>
+
+  <!-- The current version number of this component -->
+  <property name="component.version"       value="1.0-dev"/>
+
+  <!-- The base directory for compilation targets -->
+  <property name="build.home"              value="build"/>
+
+  <!-- The base directory for component configuration files -->
+  <property name="conf.home"               value="src/conf"/>
+
+  <!-- The base directory for component sources -->
+  <property name="source.home"             value="src/java"/>
+
+  <!-- The base directory for unit test sources -->
+  <property name="test.home"               value="src/test"/>
+
+<!-- ========== Compiler Defaults ========================================= -->
+
+
+  <!-- Should Java compilations set the 'debug' compiler option? -->
+  <property name="compile.debug"           value="true"/>
+
+  <!-- Should Java compilations set the 'deprecation' compiler option? -->
+  <property name="compile.deprecation"     value="false"/>
+
+  <!-- Should Java compilations set the 'optimize' compiler option? -->
+  <property name="compile.optimize"        value="true"/>
+
+  <!-- Construct compile classpath -->
+  <path id="compile.classpath">
+    <pathelement location="${build.home}/classes"/>
+    <pathelement location="${tomcat-util.jar}"/>
+    <pathelement location="${tomcat-coyote.jar}"/>
+    <pathelement location="${commons-logging.jar}"/>
+    <pathelement location="${commons-modeler.jar}"/>
+    <pathelement location="${regexp.jar}"/>
+    <pathelement location="${jmx.jar}"/>
+  </path>
+
+
+<!-- ========== Test Execution Defaults =================================== -->
+
+
+  <!-- Construct unit test classpath -->
+  <path id="test.classpath">
+    <pathelement location="${build.home}/classes"/>
+    <pathelement location="${build.home}/tests"/>
+    <pathelement location="${tomcat-util.jar}"/>
+    <pathelement location="${tomcat-coyote.jar}"/>
+    <pathelement location="${commons-logging.jar}"/>
+    <pathelement location="${junit.jar}"/>
+  </path>
+
+  <!-- Should all tests fail if one does? -->
+  <property name="test.failonerror"        value="true"/>
+
+  <!-- The test runner to execute -->
+  <property name="test.runner"             value="junit.textui.TestRunner"/>
+  <property name="test.entry" value="org.apache.coyote.http11.TestAll"/>
+
+
+<!-- ========== Executable Targets ======================================== -->
+
+
+  <target name="init"
+   description="Initialize and evaluate conditionals">
+    <echo message="-------- ${component.title} ${component.version} --------"/>
+    <filter  token="name"                  value="${component.name}"/>
+    <filter  token="version"               value="${component.version}"/>
+  </target>
+
+
+  <target name="prepare" depends="init"
+   description="Prepare build directory">
+    <mkdir dir="${build.home}"/>
+    <mkdir dir="${build.home}/classes"/>
+    <mkdir dir="${build.home}/conf"/>
+    <mkdir dir="${build.home}/lib"/>
+    <mkdir dir="${build.home}/docs"/>
+    <mkdir dir="${build.home}/docs/api"/>
+    <mkdir dir="${build.home}/tests"/>
+  </target>
+
+
+  <target name="static" depends="prepare"
+   description="Copy static files to build directory">
+    <tstamp/>
+    <copy  todir="${build.home}/conf" filtering="on">
+      <fileset dir="${conf.home}" includes="*.MF"/>
+    </copy>
+  </target>
+
+
+  <target name="javadoc" unless="docs-uptodate"
+   description="Create component Javadoc documentation">
+    <mkdir dir="${build.home}/docs/api"/>
+    <javadoc sourcepath="${source.home}"
+                destdir="${build.home}/docs/api"
+           packagenames="org.apache.coyote.*"
+                 author="true"
+                private="true"
+                version="true"
+               doctitle="&lt;h1&gt;${component.title}&lt;/h1&gt;"
+            windowtitle="${component.title} (Version ${component.version})"
+                 bottom="Copyright (c) 2001 - Apache Software Foundation">
+      <classpath refid="compile.classpath"/>
+    </javadoc>
+  </target>
+
+  <target name="compile-only" 
+          description="Compile shareable components">
+
+    <javac  srcdir="${source.home}"
+           destdir="${build.home}/classes"
+             debug="${compile.debug}"
+       deprecation="${compile.deprecation}"
+          optimize="${compile.optimize}">
+      <classpath refid="compile.classpath"/>
+    </javac>
+    <copy    todir="${build.home}/classes" filtering="on">
+      <fileset dir="${source.home}" excludes="**/*.java"/>
+    </copy>
+    <property name="tomcat-http11.jar" value="${build.home}/lib/tomcat-${component.name}.jar"/>
+    <jar    jarfile="${tomcat-http11.jar}"
+             index="true"
+            basedir="${build.home}/classes"
+             manifest="${build.home}/conf/MANIFEST.MF">
+      <include name="org/apache/coyote/http11/**"/>
+    </jar>
+  </target>
+
+  <target name="compile" depends="static,compile-only"
+          description="Compile shareable components">
+    <jar jarfile="${build.home}/lib/tomcat33-resource.jar"
+         index="true"
+         basedir="${build.home}/classes" 
+         includes="**/*.properties" />
+  </target>
+
+
+  <target name="compile.tests" depends="compile"
+   description="Compile unit test cases">
+    <javac  srcdir="${test.home}/java"
+           destdir="${build.home}/tests"
+             debug="${compile.debug}"
+       deprecation="${compile.deprecation}"
+          optimize="${compile.optimize}">
+      <classpath refid="test.classpath"/>
+    </javac>
+    <copy    todir="${build.home}/tests" filtering="on">
+      <fileset dir="${test.home}" excludes="**/*.java"/>
+    </copy>
+  </target>
+
+
+  <target name="clean"
+   description="Clean build and distribution directories">
+    <delete    dir="${build.home}"/>
+    <delete    dir="${dist.home}"/>
+  </target>
+
+
+  <target name="all" depends="clean,compile,compile.tests"
+   description="Clean and compile all components"/>
+
+
+<!-- ========== Unit Test Targets ========================================= -->
+
+
+  <target name="test"  depends="compile.tests" if="test.entry"
+   description="Run all unit test cases">
+      <!--
+      <junit printsummary="yes" fork="on" haltonfailure="yes">
+      	<formatter type="plain" usefile="false"/>
+      	<test name="${test.entry}"/>
+        <classpath refid="test.classpath"/>
+      </junit>
+      -->
+
+      <java classname="${test.runner}" fork="yes"
+       failonerror="${test.failonerror}">
+        <jvmarg value="${java.protocol.handler.pkgs}"/>
+        <arg value="${test.entry}"/>
+        <classpath refid="test.classpath"/>
+      </java>
+  </target>
+
+
+</project>
\ No newline at end of file
diff --git a/connectors/http11/src/conf/MANIFEST.MF b/connectors/http11/src/conf/MANIFEST.MF
new file mode 100644
index 0000000..379ca08
--- /dev/null
+++ b/connectors/http11/src/conf/MANIFEST.MF
@@ -0,0 +1,6 @@
+Extension-Name: @name@
+Specification-Vendor: Apache Software Foundation
+Specification-Version: 1.1
+Implementation-Vendor: Apache Software Foundation
+Implementation-Version: @version@
+
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/Constants.java b/connectors/http11/src/java/org/apache/coyote/http11/Constants.java
new file mode 100644
index 0000000..d4cdb0a
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/Constants.java
@@ -0,0 +1,253 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.http11;
+
+
+/**
+ * Constants.
+ *
+ * @author Remy Maucherat
+ */
+public final class Constants {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     * Package name.
+     */
+    public static final String Package = "org.apache.coyote.http11";
+
+    public static final int DEFAULT_CONNECTION_LINGER = -1;
+    public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
+    public static final int DEFAULT_CONNECTION_UPLOAD_TIMEOUT = 300000;
+    public static final int DEFAULT_SERVER_SOCKET_TIMEOUT = 0;
+    public static final boolean DEFAULT_TCP_NO_DELAY = true;
+    
+    /**
+     * Server string.
+     */
+    public static final String SERVER = "Apache-Coyote/1.1";
+
+
+    /**
+     * CR.
+     */
+    public static final byte CR = (byte) '\r';
+
+
+    /**
+     * LF.
+     */
+    public static final byte LF = (byte) '\n';
+
+
+    /**
+     * SP.
+     */
+    public static final byte SP = (byte) ' ';
+
+
+    /**
+     * HT.
+     */
+    public static final byte HT = (byte) '\t';
+
+
+    /**
+     * COLON.
+     */
+    public static final byte COLON = (byte) ':';
+
+
+    /**
+     * 'A'.
+     */
+    public static final byte A = (byte) 'A';
+
+
+    /**
+     * 'a'.
+     */
+    public static final byte a = (byte) 'a';
+
+
+    /**
+     * 'Z'.
+     */
+    public static final byte Z = (byte) 'Z';
+
+
+    /**
+     * '?'.
+     */
+    public static final byte QUESTION = (byte) '?';
+
+
+    /**
+     * Lower case offset.
+     */
+    public static final byte LC_OFFSET = A - a;
+
+
+    /**
+     * Default HTTP header buffer size.
+     */
+    public static final int DEFAULT_HTTP_HEADER_BUFFER_SIZE = 48 * 1024;
+
+
+    /**
+     * CRLF.
+     */
+    public static final String CRLF = "\r\n";
+
+
+    /**
+     * CRLF bytes.
+     */
+    public static final byte[] CRLF_BYTES = {(byte) '\r', (byte) '\n'};
+
+
+    /**
+     * Colon bytes.
+     */
+    public static final byte[] COLON_BYTES = {(byte) ':', (byte) ' '};
+
+
+    /**
+     * Close bytes.
+     */
+    public static final byte[] CLOSE_BYTES = {
+        (byte) 'c',
+        (byte) 'l',
+        (byte) 'o',
+        (byte) 's',
+        (byte) 'e'
+    };
+
+
+    /**
+     * Keep-alive bytes.
+     */
+    public static final byte[] KEEPALIVE_BYTES = {
+        (byte) 'k',
+        (byte) 'e',
+        (byte) 'e',
+        (byte) 'p',
+        (byte) '-',
+        (byte) 'a',
+        (byte) 'l',
+        (byte) 'i',
+        (byte) 'v',
+        (byte) 'e'
+    };
+
+
+    /**
+     * Identity filters (input and output).
+     */
+    public static final int IDENTITY_FILTER = 0;
+
+
+    /**
+     * Chunked filters (input and output).
+     */
+    public static final int CHUNKED_FILTER = 1;
+
+
+    /**
+     * Void filters (input and output).
+     */
+    public static final int VOID_FILTER = 2;
+
+
+    /**
+     * GZIP filter (output).
+     */
+    public static final int GZIP_FILTER = 3;
+
+
+    /**
+     * Buffered filter (input)
+     */
+    public static final int BUFFERED_FILTER = 3;
+
+
+    /**
+     * HTTP/1.0.
+     */
+    public static final String HTTP_10 = "HTTP/1.0";
+
+
+    /**
+     * HTTP/1.1.
+     */
+    public static final String HTTP_11 = "HTTP/1.1";
+
+
+    /**
+     * GET.
+     */
+    public static final String GET = "GET";
+
+
+    /**
+     * HEAD.
+     */
+    public static final String HEAD = "HEAD";
+
+
+    /**
+     * POST.
+     */
+    public static final String POST = "POST";
+
+
+    /**
+     * Ack string when pipelining HTTP requests.
+     */
+    public static final byte[] ACK_BYTES = {
+        (byte) 'H',
+        (byte) 'T',
+        (byte) 'T',
+        (byte) 'P',
+        (byte) '/',
+        (byte) '1',
+        (byte) '.',
+        (byte) '1',
+        (byte) ' ',
+        (byte) '1',
+        (byte) '0',
+        (byte) '0',
+        (byte) ' ',
+        (byte) 'C',
+        (byte) 'o',
+        (byte) 'n',
+        (byte) 't',
+        (byte) 'i',
+        (byte) 'n',
+        (byte) 'u',
+        (byte) 'e',
+        (byte) '\r',
+        (byte) '\n',
+        (byte) '\r',
+        (byte) '\n'
+    };
+
+
+}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/Http11Processor.java b/connectors/http11/src/java/org/apache/coyote/http11/Http11Processor.java
new file mode 100644
index 0000000..3041df6
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/Http11Processor.java
@@ -0,0 +1,1646 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.http11;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.ActionHook;
+import org.apache.coyote.Adapter;
+import org.apache.coyote.Processor;
+import org.apache.coyote.Request;
+import org.apache.coyote.RequestInfo;
+import org.apache.coyote.Response;
+import org.apache.coyote.http11.filters.ChunkedInputFilter;
+import org.apache.coyote.http11.filters.ChunkedOutputFilter;
+import org.apache.coyote.http11.filters.GzipOutputFilter;
+import org.apache.coyote.http11.filters.IdentityInputFilter;
+import org.apache.coyote.http11.filters.IdentityOutputFilter;
+import org.apache.coyote.http11.filters.VoidInputFilter;
+import org.apache.coyote.http11.filters.VoidOutputFilter;
+import org.apache.coyote.http11.filters.BufferedInputFilter;
+import org.apache.tomcat.util.buf.Ascii;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.HexUtils;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.FastHttpDateFormat;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.threads.ThreadPool;
+import org.apache.tomcat.util.threads.ThreadWithAttributes;
+
+
+/**
+ * Processes HTTP requests.
+ * 
+ * @author Remy Maucherat
+ */
+public class Http11Processor implements Processor, ActionHook {
+
+
+    /**
+     * Logger.
+     */
+    protected static org.apache.commons.logging.Log log 
+        = org.apache.commons.logging.LogFactory.getLog(Http11Processor.class);
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor.
+     */
+    public Http11Processor() {
+        this(Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
+    }
+
+
+    public Http11Processor(int headerBufferSize) {
+
+        request = new Request();
+        inputBuffer = new InternalInputBuffer(request, headerBufferSize);
+        request.setInputBuffer(inputBuffer);
+
+        response = new Response();
+        response.setHook(this);
+        outputBuffer = new InternalOutputBuffer(response, headerBufferSize);
+        response.setOutputBuffer(outputBuffer);
+        request.setResponse(response);
+
+        initializeFilters();
+
+        // Cause loading of HexUtils
+        int foo = HexUtils.DEC[0];
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated adapter.
+     */
+    protected Adapter adapter = null;
+
+
+    /**
+     * Request object.
+     */
+    protected Request request = null;
+
+
+    /**
+     * Response object.
+     */
+    protected Response response = null;
+
+
+    /**
+     * Input.
+     */
+    protected InternalInputBuffer inputBuffer = null;
+
+
+    /**
+     * Output.
+     */
+    protected InternalOutputBuffer outputBuffer = null;
+
+
+    /**
+     * State flag.
+     */
+    protected boolean started = false;
+
+
+    /**
+     * Error flag.
+     */
+    protected boolean error = false;
+
+
+    /**
+     * Keep-alive.
+     */
+    protected boolean keepAlive = true;
+
+
+    /**
+     * HTTP/1.1 flag.
+     */
+    protected boolean http11 = true;
+
+
+    /**
+     * HTTP/0.9 flag.
+     */
+    protected boolean http09 = false;
+
+
+    /**
+     * Content delimitator for the request (if false, the connection will
+     * be closed at the end of the request).
+     */
+    protected boolean contentDelimitation = true;
+
+
+    /**
+     * Is there an expectation ?
+     */
+    protected boolean expectation = false;
+
+
+    /**
+     * List of restricted user agents.
+     */
+    protected Pattern[] restrictedUserAgents = null;
+
+
+    /**
+     * Maximum number of Keep-Alive requests to honor.
+     */
+    protected int maxKeepAliveRequests = -1;
+
+
+    /**
+     * SSL information.
+     */
+    protected SSLSupport sslSupport;
+
+
+    /**
+     * Socket associated with the current connection.
+     */
+    protected Socket socket;
+
+
+    /**
+     * Remote Address associated with the current connection.
+     */
+    protected String remoteAddr = null;
+
+
+    /**
+     * Remote Host associated with the current connection.
+     */
+    protected String remoteHost = null;
+    
+    
+    /**
+     * Local Host associated with the current connection.
+     */
+    protected String localName = null;
+    
+    
+    
+    /**
+     * Local port to which the socket is connected
+     */
+    protected int localPort = -1;
+    
+    
+    /**
+     * Remote port to which the socket is connected
+     */
+    protected int remotePort = -1;
+    
+    
+    /**
+     * The local Host address.
+     */
+    protected String localAddr = null; 
+
+
+    /**
+     * Maximum timeout on uploads. 5 minutes as in Apache HTTPD server.
+     */
+    protected int timeout = 300000;
+
+
+    /**
+     * Flag to disable setting a different time-out on uploads.
+     */
+    protected boolean disableUploadTimeout = false;
+
+
+    /**
+     * Allowed compression level.
+     */
+    protected int compressionLevel = 0;
+
+
+    /**
+     * Minimum contentsize to make compression.
+     */
+    protected int compressionMinSize = 2048;
+
+
+    /**
+     * Socket buffering.
+     */
+    protected int socketBuffer = -1;
+
+
+    /**
+     * Max post size.
+     */
+    protected int maxPostSize = 2 * 1024 * 1024;
+
+
+    /**
+     * List of user agents to not use gzip with
+     */
+    protected Pattern noCompressionUserAgents[] = null;
+    
+    /**
+     * List of MIMES which could be gzipped
+     */
+    protected String[] compressableMimeTypes = 
+    { "text/html", "text/xml", "text/plain" };
+
+
+    /**
+     * Host name (used to avoid useless B2C conversion on the host name).
+     */
+    protected char[] hostNameC = new char[0];
+
+
+    /**
+     * Associated thread pool.
+     */
+    protected ThreadPool threadPool;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return compression level.
+     */
+    public String getCompression() {
+        switch (compressionLevel) {
+        case 0:
+            return "off";
+        case 1:
+            return "on";
+        case 2:
+            return "force";
+        }
+        return "off";
+    }
+
+
+    /**
+     * Set compression level.
+     */
+    public void setCompression(String compression) {
+        if (compression.equals("on")) {
+            this.compressionLevel = 1;
+        } else if (compression.equals("force")) {
+            this.compressionLevel = 2;
+        } else if (compression.equals("off")) {
+            this.compressionLevel = 0;
+        } else {
+            try {
+                // Try to parse compression as an int, which would give the
+                // minimum compression size
+                compressionMinSize = Integer.parseInt(compression);
+                this.compressionLevel = 1;
+            } catch (Exception e) {
+                this.compressionLevel = 0;
+            }
+        }
+    }
+
+    /**
+     * Set Minimum size to trigger compression.
+     */
+    public void setCompressionMinSize(int compressionMinSize) {
+        this.compressionMinSize = compressionMinSize;
+    }
+    
+
+    public void setThreadPool(ThreadPool threadPool) {
+        this.threadPool = threadPool;
+    }
+
+    /**
+     * Add user-agent for which gzip compression didn't works
+     * The user agent String given will be exactly matched
+     * to the user-agent header submitted by the client.
+     * 
+     * @param userAgent user-agent string
+     */
+    public void addNoCompressionUserAgent(String userAgent) {
+        try {
+            Pattern nRule = Pattern.compile(userAgent);
+            noCompressionUserAgents = 
+                addREArray(noCompressionUserAgents, nRule);
+        } catch (PatternSyntaxException pse) {
+            log.error("Error parsing regular expression: " + userAgent, pse);
+        }
+    }
+
+
+    /**
+     * Set no compression user agent list (this method is best when used with 
+     * a large number of connectors, where it would be better to have all of 
+     * them referenced a single array).
+     */
+    public void setNoCompressionUserAgents(Pattern[] noCompressionUserAgents) {
+        this.noCompressionUserAgents = noCompressionUserAgents;
+    }
+
+
+    /**
+     * Set no compression user agent list.
+     * List contains users agents separated by ',' :
+     * 
+     * ie: "gorilla,desesplorer,tigrus"
+     */
+    public void setNoCompressionUserAgents(String noCompressionUserAgents) {
+        if (noCompressionUserAgents != null) {
+            StringTokenizer st = new StringTokenizer(noCompressionUserAgents, ",");
+        
+            while (st.hasMoreTokens()) {
+                addNoCompressionUserAgent(st.nextToken().trim());
+            }
+        }
+    }
+
+    /**
+     * Add a mime-type which will be compressable
+     * The mime-type String will be exactly matched
+     * in the response mime-type header .
+     * 
+     * @param mimeType mime-type string
+     */
+    public void addCompressableMimeType(String mimeType) {
+        compressableMimeTypes = 
+            addStringArray(compressableMimeTypes, mimeType);
+    }
+
+
+    /**
+     * Set compressable mime-type list (this method is best when used with 
+     * a large number of connectors, where it would be better to have all of 
+     * them referenced a single array).
+     */
+    public void setCompressableMimeTypes(String[] compressableMimeTypes) {
+        this.compressableMimeTypes = compressableMimeTypes;
+    }
+
+
+    /**
+     * Set compressable mime-type list
+     * List contains users agents separated by ',' :
+     * 
+     * ie: "text/html,text/xml,text/plain"
+     */
+    public void setCompressableMimeTypes(String compressableMimeTypes) {
+        if (compressableMimeTypes != null) {
+            StringTokenizer st = new StringTokenizer(compressableMimeTypes, ",");
+
+            while (st.hasMoreTokens()) {
+                addCompressableMimeType(st.nextToken().trim());
+            }
+        }
+    }
+
+
+    /**
+     * Return the list of restricted user agents.
+     */
+    public String[] findCompressableMimeTypes() {
+        return (compressableMimeTypes);
+    }
+
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add input or output filter.
+     * 
+     * @param className class name of the filter
+     */
+    protected void addFilter(String className) {
+        try {
+            Class clazz = Class.forName(className);
+            Object obj = clazz.newInstance();
+            if (obj instanceof InputFilter) {
+                inputBuffer.addFilter((InputFilter) obj);
+            } else if (obj instanceof OutputFilter) {
+                outputBuffer.addFilter((OutputFilter) obj);
+            } else {
+                log.warn("Unknown filter: " + className);
+            }
+        } catch (Exception e) {
+            log.error("Error intializing filter: " + className, e);
+        }
+    }
+
+
+    /**
+     * General use method
+     * 
+     * @param sArray the StringArray 
+     * @param value string
+     */
+    private String[] addStringArray(String sArray[], String value) {
+        String[] result = null;
+        if (sArray == null) {
+            result = new String[1];
+            result[0] = value;
+        }
+        else {
+            result = new String[sArray.length + 1];
+            for (int i = 0; i < sArray.length; i++)
+                result[i] = sArray[i];
+            result[sArray.length] = value;
+        }
+        return result;
+    }
+
+
+    /**
+     * General use method
+     * 
+     * @param rArray the REArray 
+     * @param value Obj
+     */
+    private Pattern[] addREArray(Pattern rArray[], Pattern value) {
+        Pattern[] result = null;
+        if (rArray == null) {
+            result = new Pattern[1];
+            result[0] = value;
+        }
+        else {    
+            result = new Pattern[rArray.length + 1];
+            for (int i = 0; i < rArray.length; i++)
+                result[i] = rArray[i];
+            result[rArray.length] = value;
+        }
+        return result;
+    }
+
+
+    /**
+     * General use method
+     * 
+     * @param sArray the StringArray 
+     * @param value string
+     */
+    private boolean inStringArray(String sArray[], String value) {
+        for (int i = 0; i < sArray.length; i++) {
+            if (sArray[i].equals(value)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * Checks if any entry in the string array starts with the specified value
+     *
+     * @param sArray the StringArray
+     * @param value string
+     */
+    private boolean startsWithStringArray(String sArray[], String value) {
+        if (value == null)
+           return false;
+        for (int i = 0; i < sArray.length; i++) {
+            if (value.startsWith(sArray[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * Add restricted user-agent (which will downgrade the connector 
+     * to HTTP/1.0 mode). The user agent String given will be matched
+     * via regexp to the user-agent header submitted by the client.
+     * 
+     * @param userAgent user-agent string
+     */
+    public void addRestrictedUserAgent(String userAgent) {
+        try {
+            Pattern nRule = Pattern.compile(userAgent);
+            restrictedUserAgents = addREArray(restrictedUserAgents, nRule);
+        } catch (PatternSyntaxException pse) {
+            log.error("Error parsing regular expression: " + userAgent, pse);
+        }
+    }
+
+
+    /**
+     * Set restricted user agent list (this method is best when used with 
+     * a large number of connectors, where it would be better to have all of 
+     * them referenced a single array).
+     */
+    public void setRestrictedUserAgents(Pattern[] restrictedUserAgents) {
+        this.restrictedUserAgents = restrictedUserAgents;
+    }
+
+
+    /**
+     * Set restricted user agent list (which will downgrade the connector
+     * to HTTP/1.0 mode). List contains users agents separated by ',' :
+     * 
+     * ie: "gorilla,desesplorer,tigrus"
+     */
+    public void setRestrictedUserAgents(String restrictedUserAgents) {
+        if (restrictedUserAgents != null) {
+            StringTokenizer st = 
+                new StringTokenizer(restrictedUserAgents, ",");
+            while (st.hasMoreTokens()) {
+                addRestrictedUserAgent(st.nextToken().trim());
+            }
+        }
+    }
+
+
+    /**
+     * Return the list of restricted user agents.
+     */
+    public String[] findRestrictedUserAgents() {
+        String[] sarr = new String [restrictedUserAgents.length];
+        
+        for (int i = 0; i < restrictedUserAgents.length; i++)
+            sarr[i] = restrictedUserAgents[i].toString();
+            
+        return (sarr);
+    }
+
+
+    /**
+     * Set the maximum number of Keep-Alive requests to honor.
+     * This is to safeguard from DoS attacks.  Setting to a negative
+     * value disables the check.
+     */
+    public void setMaxKeepAliveRequests(int mkar) {
+        maxKeepAliveRequests = mkar;
+    }
+
+
+    /**
+     * Return the number of Keep-Alive requests that we will honor.
+     */
+    public int getMaxKeepAliveRequests() {
+        return maxKeepAliveRequests;
+    }
+
+
+    /**
+     * Set the maximum size of a POST which will be buffered in SSL mode.
+     */
+    public void setMaxPostSize(int mps) {
+        maxPostSize = mps;
+    }
+
+
+    /**
+     * Return the maximum size of a POST which will be buffered in SSL mode.
+     */
+    public int getMaxPostSize() {
+        return maxPostSize;
+    }
+
+
+    /**
+     * Set the SSL information for this HTTP connection.
+     */
+    public void setSSLSupport(SSLSupport sslSupport) {
+        this.sslSupport = sslSupport;
+    }
+
+
+    /**
+     * Set the socket associated with this HTTP connection.
+     */
+    public void setSocket(Socket socket)
+        throws IOException {
+        this.socket = socket;
+    }
+
+    /**
+     * Set the flag to control upload time-outs.
+     */
+    public void setDisableUploadTimeout(boolean isDisabled) {
+        disableUploadTimeout = isDisabled;
+    }
+
+    /**
+     * Get the flag that controls upload time-outs.
+     */
+    public boolean getDisableUploadTimeout() {
+        return disableUploadTimeout;
+    }
+
+    /**
+     * Set the socket buffer flag.
+     */
+    public void setSocketBuffer(int socketBuffer) {
+        this.socketBuffer = socketBuffer;
+        outputBuffer.setSocketBuffer(socketBuffer);
+    }
+
+    /**
+     * Get the socket buffer flag.
+     */
+    public int getSocketBuffer() {
+        return socketBuffer;
+    }
+
+    /**
+     * Set the upload timeout.
+     */
+    public void setTimeout( int timeouts ) {
+        timeout = timeouts ;
+    }
+
+    /**
+     * Get the upload timeout.
+     */
+    public int getTimeout() {
+        return timeout;
+    }
+
+    /** Get the request associated with this processor.
+     *
+     * @return The request
+     */
+    public Request getRequest() {
+        return request;
+    }
+
+    /**
+     * Process pipelined HTTP requests using the specified input and output
+     * streams.
+     * 
+     * @param input stream from which the HTTP requests will be read
+     * @param output stream which will be used to output the HTTP
+     * responses
+     * @throws IOException error during an I/O operation
+     */
+    public void process(InputStream input, OutputStream output)
+        throws IOException {
+        ThreadWithAttributes thrA=
+                (ThreadWithAttributes)Thread.currentThread();
+        RequestInfo rp = request.getRequestProcessor();
+        thrA.setCurrentStage(threadPool, "parsing http request");
+        rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
+
+        // Set the remote address
+        remoteAddr = null;
+        remoteHost = null;
+        localAddr = null;
+        remotePort = -1;
+        localPort = -1;
+        
+        // Setting up the I/O
+        inputBuffer.setInputStream(input);
+        outputBuffer.setOutputStream(output);
+
+        // Error flag
+        error = false;
+        keepAlive = true;
+
+        int keepAliveLeft = maxKeepAliveRequests;
+        int soTimeout = socket.getSoTimeout();
+
+        float threadRatio = 
+            (float) threadPool.getCurrentThreadsBusy() 
+            / (float) threadPool.getMaxThreads();
+        if ((threadRatio > 0.33) && (threadRatio <= 0.66)) {
+            soTimeout = soTimeout / 2;
+        } else if (threadRatio > 0.66) {
+            soTimeout = soTimeout / 3;
+            keepAliveLeft = 1;
+        }
+
+        boolean keptAlive = false;
+
+        while (started && !error && keepAlive) {
+
+            // Parsing the request header
+            try {
+                if( !disableUploadTimeout && keptAlive && soTimeout > 0 ) {
+                    socket.setSoTimeout(soTimeout);
+                }
+                inputBuffer.parseRequestLine();
+                request.setStartTime(System.currentTimeMillis());
+                thrA.setParam( threadPool, request.requestURI() );
+                keptAlive = true;
+                if (!disableUploadTimeout) {
+                    socket.setSoTimeout(timeout);
+                }
+                inputBuffer.parseHeaders();
+            } catch (IOException e) {
+                error = true;
+                break;
+            } catch (Throwable t) {
+                log.debug("Error parsing HTTP request", t);
+                // 400 - Bad Request
+                response.setStatus(400);
+                error = true;
+            }
+
+            // Setting up filters, and parse some request headers
+            thrA.setCurrentStage(threadPool, "prepareRequest");
+            rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
+            try {
+                prepareRequest();
+            } catch (Throwable t) {
+                log.debug("Error preparing request", t);
+                // 400 - Internal Server Error
+                response.setStatus(400);
+                error = true;
+            }
+
+            if (maxKeepAliveRequests > 0 && --keepAliveLeft == 0)
+                keepAlive = false;
+
+            // Process the request in the adapter
+            if (!error) {
+                try {
+                    thrA.setCurrentStage(threadPool, "service");
+                    rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
+                    adapter.service(request, response);
+                    // Handle when the response was committed before a serious
+                    // error occurred.  Throwing a ServletException should both
+                    // set the status to 500 and set the errorException.
+                    // If we fail here, then the response is likely already 
+                    // committed, so we can't try and set headers.
+                    if(keepAlive && !error) { // Avoid checking twice.
+                        error = response.getErrorException() != null ||
+                                statusDropsConnection(response.getStatus());
+                    }
+
+                } catch (InterruptedIOException e) {
+                    error = true;
+                } catch (Throwable t) {
+                    log.error("Error processing request", t);
+                    // 500 - Internal Server Error
+                    response.setStatus(500);
+                    error = true;
+                }
+            }
+
+            // Finish the handling of the request
+            try {
+                thrA.setCurrentStage(threadPool, "endRequestIB");
+                rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
+                inputBuffer.endRequest();
+            } catch (IOException e) {
+                error = true;
+            } catch (Throwable t) {
+                log.error("Error finishing request", t);
+                // 500 - Internal Server Error
+                response.setStatus(500);
+                error = true;
+            }
+            try {
+                thrA.setCurrentStage(threadPool, "endRequestOB");
+                rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
+                outputBuffer.endRequest();
+            } catch (IOException e) {
+                error = true;
+            } catch (Throwable t) {
+                log.error("Error finishing response", t);
+                error = true;
+            }
+
+            // If there was an error, make sure the request is counted as
+            // and error, and update the statistics counter
+            if (error) {
+                response.setStatus(500);
+            }
+            request.updateCounters();
+
+            thrA.setCurrentStage(threadPool, "ended");
+            rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
+
+            // Don't reset the param - we'll see it as ended. Next request
+            // will reset it
+            // thrA.setParam(null);
+            // Next request
+            inputBuffer.nextRequest();
+            outputBuffer.nextRequest();
+
+        }
+
+        rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+
+        // Recycle
+        inputBuffer.recycle();
+        outputBuffer.recycle();
+
+        // Recycle ssl info
+        sslSupport = null;
+    }
+
+
+    // ----------------------------------------------------- ActionHook Methods
+
+
+    /**
+     * Send an action to the connector.
+     * 
+     * @param actionCode Type of the action
+     * @param param Action parameter
+     */
+    public void action(ActionCode actionCode, Object param) {
+
+        if (actionCode == ActionCode.ACTION_COMMIT) {
+            // Commit current response
+
+            if (response.isCommitted())
+                return;
+
+            // Validate and write response headers
+            prepareResponse();
+            try {
+                outputBuffer.commit();
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+            }
+
+        } else if (actionCode == ActionCode.ACTION_ACK) {
+
+            // Acknowlege request
+
+            // Send a 100 status back if it makes sense (response not committed
+            // yet, and client specified an expectation for 100-continue)
+
+            if ((response.isCommitted()) || !expectation)
+                return;
+
+            inputBuffer.setSwallowInput(true);
+            try {
+                outputBuffer.sendAck();
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+            }
+
+        } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
+
+            try {
+                outputBuffer.flush();
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+                response.setErrorException(e);
+            }
+
+        } else if (actionCode == ActionCode.ACTION_CLOSE) {
+            // Close
+
+            // End the processing of the current request, and stop any further
+            // transactions with the client
+
+            try {
+                outputBuffer.endRequest();
+            } catch (IOException e) {
+                // Set error flag
+                error = true;
+            }
+
+        } else if (actionCode == ActionCode.ACTION_RESET) {
+
+            // Reset response
+
+            // Note: This must be called before the response is committed
+
+            outputBuffer.reset();
+
+        } else if (actionCode == ActionCode.ACTION_CUSTOM) {
+
+            // Do nothing
+
+        } else if (actionCode == ActionCode.ACTION_START) {
+
+            started = true;
+
+        } else if (actionCode == ActionCode.ACTION_STOP) {
+
+            started = false;
+
+        } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
+
+            try {
+                if (sslSupport != null) {
+                    Object sslO = sslSupport.getCipherSuite();
+                    if (sslO != null)
+                        request.setAttribute
+                            (SSLSupport.CIPHER_SUITE_KEY, sslO);
+                    sslO = sslSupport.getPeerCertificateChain(false);
+                    if (sslO != null)
+                        request.setAttribute
+                            (SSLSupport.CERTIFICATE_KEY, sslO);
+                    sslO = sslSupport.getKeySize();
+                    if (sslO != null)
+                        request.setAttribute
+                            (SSLSupport.KEY_SIZE_KEY, sslO);
+                    sslO = sslSupport.getSessionId();
+                    if (sslO != null)
+                        request.setAttribute
+                            (SSLSupport.SESSION_ID_KEY, sslO);
+                }
+            } catch (Exception e) {
+                log.warn("Exception getting SSL attributes " ,e);
+            }
+
+        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE) {
+
+            if ((remoteAddr == null) && (socket != null)) {
+                InetAddress inetAddr = socket.getInetAddress();
+                if (inetAddr != null) {
+                    remoteAddr = inetAddr.getHostAddress();
+                }   
+            }
+            request.remoteAddr().setString(remoteAddr);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
+            
+            if ((localName == null) && (socket != null)) {
+                InetAddress inetAddr = socket.getLocalAddress();
+                if (inetAddr != null) {
+                    localName = inetAddr.getHostName();
+                }
+            }
+            request.localName().setString(localName);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
+            
+            if ((remoteHost == null) && (socket != null)) {
+                InetAddress inetAddr = socket.getInetAddress();
+                if (inetAddr != null) {
+                    remoteHost = inetAddr.getHostName();
+                }
+            }
+            request.remoteHost().setString(remoteHost);
+            
+        } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
+                       
+            if (localAddr == null)
+               localAddr = socket.getLocalAddress().getHostAddress();
+
+            request.localAddr().setString(localAddr);
+            
+        } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
+            
+            if ((remotePort == -1 ) && (socket !=null)) {
+                remotePort = socket.getPort(); 
+            }    
+            request.setRemotePort(remotePort);
+
+        } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
+            
+            if ((localPort == -1 ) && (socket !=null)) {
+                localPort = socket.getLocalPort(); 
+            }            
+            request.setLocalPort(localPort);
+       
+        } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
+            if( sslSupport != null) {
+                /*
+                 * Consume and buffer the request body, so that it does not
+                 * interfere with the client's handshake messages
+                 */
+                InputFilter[] inputFilters = inputBuffer.getFilters();
+                ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER])
+                    .setLimit(maxPostSize);
+                inputBuffer.addActiveFilter
+                    (inputFilters[Constants.BUFFERED_FILTER]);
+                try {
+                    Object sslO = sslSupport.getPeerCertificateChain(true);
+                    if( sslO != null) {
+                        request.setAttribute
+                            (SSLSupport.CERTIFICATE_KEY, sslO);
+                    }
+                } catch (Exception e) {
+                    log.warn("Exception getting SSL Cert",e);
+                }
+            }
+        }
+
+    }
+
+
+    // ------------------------------------------------------ Connector Methods
+
+
+    /**
+     * Set the associated adapter.
+     * 
+     * @param adapter the new adapter
+     */
+    public void setAdapter(Adapter adapter) {
+        this.adapter = adapter;
+    }
+
+
+    /**
+     * Get the associated adapter.
+     * 
+     * @return the associated adapter
+     */
+    public Adapter getAdapter() {
+        return adapter;
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * After reading the request headers, we have to setup the request filters.
+     */
+    protected void prepareRequest() {
+
+        http11 = true;
+        http09 = false;
+        contentDelimitation = false;
+        expectation = false;
+        if (sslSupport != null) {
+            request.scheme().setString("https");
+        }
+        MessageBytes protocolMB = request.protocol();
+        if (protocolMB.equals(Constants.HTTP_11)) {
+            http11 = true;
+            protocolMB.setString(Constants.HTTP_11);
+        } else if (protocolMB.equals(Constants.HTTP_10)) {
+            http11 = false;
+            keepAlive = false;
+            protocolMB.setString(Constants.HTTP_10);
+        } else if (protocolMB.equals("")) {
+            // HTTP/0.9
+            http09 = true;
+            http11 = false;
+            keepAlive = false;
+        } else {
+            // Unsupported protocol
+            http11 = false;
+            error = true;
+            // Send 505; Unsupported HTTP version
+            response.setStatus(505);
+        }
+
+        MessageBytes methodMB = request.method();
+        if (methodMB.equals(Constants.GET)) {
+            methodMB.setString(Constants.GET);
+        } else if (methodMB.equals(Constants.POST)) {
+            methodMB.setString(Constants.POST);
+        }
+
+        // Check connection header
+        MessageBytes connectionValueMB = 
+            request.getMimeHeaders().getValue("connection");
+        if (connectionValueMB != null) {
+            ByteChunk connectionValueBC = connectionValueMB.getByteChunk();
+            if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {
+                keepAlive = false;
+            } else if (findBytes(connectionValueBC, 
+                                 Constants.KEEPALIVE_BYTES) != -1) {
+                keepAlive = true;
+            }
+        }
+
+        MessageBytes expectMB = null;
+        if (http11)
+            expectMB = request.getMimeHeaders().getValue("expect");
+        if ((expectMB != null)
+            && (expectMB.indexOfIgnoreCase("100-continue", 0) != -1)) {
+            inputBuffer.setSwallowInput(false);
+            expectation = true;
+        }
+
+        // Check user-agent header
+        if ((restrictedUserAgents != null) && ((http11) || (keepAlive))) {
+            MessageBytes userAgentValueMB =  
+                request.getMimeHeaders().getValue("user-agent");
+            // Check in the restricted list, and adjust the http11 
+            // and keepAlive flags accordingly
+            if(userAgentValueMB != null) {
+                String userAgentValue = userAgentValueMB.toString();
+                for (int i = 0; i < restrictedUserAgents.length; i++) {
+                    if (restrictedUserAgents[i].matcher(userAgentValue).matches()) {
+                        http11 = false;
+                        keepAlive = false;
+                        break;
+                    }
+                }
+            }
+        }
+
+        // Check for a full URI (including protocol://host:port/)
+        ByteChunk uriBC = request.requestURI().getByteChunk();
+        if (uriBC.startsWithIgnoreCase("http", 0)) {
+
+            int pos = uriBC.indexOf("://", 0, 3, 4);
+            int uriBCStart = uriBC.getStart();
+            int slashPos = -1;
+            if (pos != -1) {
+                byte[] uriB = uriBC.getBytes();
+                slashPos = uriBC.indexOf('/', pos + 3);
+                if (slashPos == -1) {
+                    slashPos = uriBC.getLength();
+                    // Set URI as "/"
+                    request.requestURI().setBytes
+                        (uriB, uriBCStart + pos + 1, 1);
+                } else {
+                    request.requestURI().setBytes
+                        (uriB, uriBCStart + slashPos, 
+                         uriBC.getLength() - slashPos);
+                }
+                MessageBytes hostMB = 
+                    request.getMimeHeaders().setValue("host");
+                hostMB.setBytes(uriB, uriBCStart + pos + 3, 
+                                slashPos - pos - 3);
+            }
+
+        }
+
+        // Input filter setup
+        InputFilter[] inputFilters = inputBuffer.getFilters();
+
+        // Parse content-length header
+        long contentLength = request.getContentLengthLong();
+        if (contentLength >= 0) {
+            inputBuffer.addActiveFilter
+                (inputFilters[Constants.IDENTITY_FILTER]);
+            contentDelimitation = true;
+        }
+
+        // Parse transfer-encoding header
+        MessageBytes transferEncodingValueMB = null;
+        if (http11)
+            transferEncodingValueMB = 
+                request.getMimeHeaders().getValue("transfer-encoding");
+        if (transferEncodingValueMB != null) {
+            String transferEncodingValue = transferEncodingValueMB.toString();
+            // Parse the comma separated list. "identity" codings are ignored
+            int startPos = 0;
+            int commaPos = transferEncodingValue.indexOf(',');
+            String encodingName = null;
+            while (commaPos != -1) {
+                encodingName = transferEncodingValue.substring
+                    (startPos, commaPos).toLowerCase().trim();
+                if (!addInputFilter(inputFilters, encodingName)) {
+                    // Unsupported transfer encoding
+                    error = true;
+                    // 501 - Unimplemented
+                    response.setStatus(501);
+                }
+                startPos = commaPos + 1;
+                commaPos = transferEncodingValue.indexOf(',', startPos);
+            }
+            encodingName = transferEncodingValue.substring(startPos)
+                .toLowerCase().trim();
+            if (!addInputFilter(inputFilters, encodingName)) {
+                // Unsupported transfer encoding
+                error = true;
+                // 501 - Unimplemented
+                response.setStatus(501);
+            }
+        }
+
+        MessageBytes valueMB = request.getMimeHeaders().getValue("host");
+
+        // Check host header
+        if (http11 && (valueMB == null)) {
+            error = true;
+            // 400 - Bad request
+            response.setStatus(400);
+        }
+
+        parseHost(valueMB);
+
+        if (!contentDelimitation) {
+            // If there's no content length and we're using keep-alive 
+            // (HTTP/1.0 with keep-alive or HTTP/1.1), assume
+            // the client is not broken and didn't send a body
+            if (keepAlive) {
+                inputBuffer.addActiveFilter
+                    (inputFilters[Constants.VOID_FILTER]);
+                contentDelimitation = true;
+            }
+        }
+
+        if (!contentDelimitation)
+            keepAlive = false;
+
+    }
+
+
+    /**
+     * Parse host.
+     */
+    public void parseHost(MessageBytes valueMB) {
+
+        if (valueMB == null || valueMB.isNull()) {
+            // HTTP/1.0
+            // Default is what the socket tells us. Overriden if a host is 
+            // found/parsed
+            request.setServerPort(socket.getLocalPort());
+            InetAddress localAddress = socket.getLocalAddress();
+            // Setting the socket-related fields. The adapter doesn't know 
+            // about socket.
+            request.setLocalHost(localAddress.getHostName());
+            request.serverName().setString(localAddress.getHostName());
+            return;
+        }
+
+        ByteChunk valueBC = valueMB.getByteChunk();
+        byte[] valueB = valueBC.getBytes();
+        int valueL = valueBC.getLength();
+        int valueS = valueBC.getStart();
+        int colonPos = -1;
+        if (hostNameC.length < valueL) {
+            hostNameC = new char[valueL];
+        }
+
+        boolean ipv6 = (valueB[valueS] == '[');
+        boolean bracketClosed = false;
+        for (int i = 0; i < valueL; i++) {
+            char b = (char) valueB[i + valueS];
+            hostNameC[i] = b;
+            if (b == ']') {
+                bracketClosed = true;
+            } else if (b == ':') {
+                if (!ipv6 || bracketClosed) {
+                    colonPos = i;
+                    break;
+                }
+            }
+        }
+
+        if (colonPos < 0) {
+            if (sslSupport == null) {
+                // 80 - Default HTTTP port
+                request.setServerPort(80);
+            } else {
+                // 443 - Default HTTPS port
+                request.setServerPort(443);
+            }
+            request.serverName().setChars(hostNameC, 0, valueL);
+        } else {
+
+            request.serverName().setChars(hostNameC, 0, colonPos);
+
+            int port = 0;
+            int mult = 1;
+            for (int i = valueL - 1; i > colonPos; i--) {
+                int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
+                if (charValue == -1) {
+                    // Invalid character
+                    error = true;
+                    // 400 - Bad request
+                    response.setStatus(400);
+                    break;
+                }
+                port = port + (charValue * mult);
+                mult = 10 * mult;
+            }
+            request.setServerPort(port);
+
+        }
+
+    }
+
+
+    /**
+     * Check for compression
+     */
+    private boolean isCompressable() {
+
+        // Nope Compression could works in HTTP 1.0 also
+        // cf: mod_deflate
+        
+        // Compression only since HTTP 1.1
+        // if (! http11)
+        //    return false;
+
+        // Check if browser support gzip encoding
+        MessageBytes acceptEncodingMB = 
+            request.getMimeHeaders().getValue("accept-encoding");
+            
+        if ((acceptEncodingMB == null) 
+            || (acceptEncodingMB.indexOf("gzip") == -1))
+            return false;
+
+        // Check if content is not allready gzipped
+        MessageBytes contentEncodingMB =
+            response.getMimeHeaders().getValue("Content-Encoding");
+
+        if ((contentEncodingMB != null) 
+            && (contentEncodingMB.indexOf("gzip") != -1))
+            return false;
+
+        // If force mode, allways compress (test purposes only)
+        if (compressionLevel == 2)
+           return true;
+
+        // Check for incompatible Browser
+        if (noCompressionUserAgents != null) {
+            MessageBytes userAgentValueMB =  
+                request.getMimeHeaders().getValue("user-agent");
+            if(userAgentValueMB != null) {
+                String userAgentValue = userAgentValueMB.toString();
+
+                // If one Regexp rule match, disable compression
+                for (int i = 0; i < noCompressionUserAgents.length; i++)
+                    if (noCompressionUserAgents[i].matcher(userAgentValue).matches())
+                        return false;
+            }
+        }
+
+        // Check if suffisant len to trig the compression        
+        int contentLength = response.getContentLength();
+        if ((contentLength == -1) 
+            || (contentLength > compressionMinSize)) {
+            // Check for compatible MIME-TYPE
+            if (compressableMimeTypes != null) {
+                return (startsWithStringArray(compressableMimeTypes, 
+                                              response.getContentType()));
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * When committing the response, we have to validate the set of headers, as
+     * well as setup the response filters.
+     */
+    protected void prepareResponse() {
+
+        boolean entityBody = true;
+        contentDelimitation = false;
+
+        OutputFilter[] outputFilters = outputBuffer.getFilters();
+
+        if (http09 == true) {
+            // HTTP/0.9
+            outputBuffer.addActiveFilter
+                (outputFilters[Constants.IDENTITY_FILTER]);
+            return;
+        }
+
+        int statusCode = response.getStatus();
+        if ((statusCode == 204) || (statusCode == 205) 
+            || (statusCode == 304)) {
+            // No entity body
+            outputBuffer.addActiveFilter
+                (outputFilters[Constants.VOID_FILTER]);
+            entityBody = false;
+            contentDelimitation = true;
+        }
+
+        MessageBytes methodMB = request.method();
+        if (methodMB.equals("HEAD")) {
+            // No entity body
+            outputBuffer.addActiveFilter
+                (outputFilters[Constants.VOID_FILTER]);
+            contentDelimitation = true;
+        }
+
+        // Check for compression
+        boolean useCompression = false;
+        if (entityBody && (compressionLevel > 0)) {
+            useCompression = isCompressable();
+            
+            // Change content-length to -1 to force chunking
+            if (useCompression) {
+                response.setContentLength(-1);
+            }
+        }
+
+        MimeHeaders headers = response.getMimeHeaders();
+        if (!entityBody) {
+            response.setContentLength(-1);
+        } else {
+            String contentType = response.getContentType();
+            if (contentType != null) {
+                headers.setValue("Content-Type").setString(contentType);
+            }
+            String contentLanguage = response.getContentLanguage();
+            if (contentLanguage != null) {
+                headers.setValue("Content-Language")
+                    .setString(contentLanguage);
+            }
+        }
+
+        int contentLength = response.getContentLength();
+        if (contentLength != -1) {
+            response.getMimeHeaders().setValue("Content-Length")
+                .setInt(contentLength);
+            outputBuffer.addActiveFilter
+                (outputFilters[Constants.IDENTITY_FILTER]);
+            contentDelimitation = true;
+        } else {
+            if (entityBody && http11 && keepAlive) {
+                outputBuffer.addActiveFilter
+                    (outputFilters[Constants.CHUNKED_FILTER]);
+                contentDelimitation = true;
+                response.addHeader("Transfer-Encoding", "chunked");
+            } else {
+                outputBuffer.addActiveFilter
+                    (outputFilters[Constants.IDENTITY_FILTER]);
+            }
+        }
+
+        if (useCompression) {
+            outputBuffer.addActiveFilter(outputFilters[Constants.GZIP_FILTER]);
+            // FIXME: Make content-encoding generation dynamic
+            response.setHeader("Content-Encoding", "gzip");
+            // Make Proxies happy via Vary (from mod_deflate)
+            response.setHeader("Vary", "Accept-Encoding");
+        }
+
+        // Add date header
+        if (! response.containsHeader("Date")){
+          
+          String date = null;
+          if (System.getSecurityManager() != null){
+            date = (String)AccessController.doPrivileged( 
+                new PrivilegedAction() {
+                    public Object run(){
+                        return FastHttpDateFormat.getCurrentDate();
+                    }
+                }
+            );
+          } else {
+            date = FastHttpDateFormat.getCurrentDate();
+          }
+          response.addHeader("Date", date);
+        }
+         
+        // Add server header
+        if (! response.containsHeader("Server")){
+            response.addHeader("Server", Constants.SERVER);
+        }
+
+        // Add transfer encoding header
+        // FIXME
+
+        if ((entityBody) && (!contentDelimitation)) {
+            // Mark as close the connection after the request, and add the 
+            // connection: close header
+            keepAlive = false;
+        }
+
+        // If we know that the request is bad this early, add the
+        // Connection: close header.
+        keepAlive = keepAlive && !statusDropsConnection(statusCode);
+        if (!keepAlive) {
+            response.addHeader("Connection", "close");
+        } else if (!http11) {
+            response.addHeader("Connection", "Keep-Alive");
+        }
+
+        // Build the response header
+        outputBuffer.sendStatus();
+
+        int size = headers.size();
+        for (int i = 0; i < size; i++) {
+            outputBuffer.sendHeader(headers.getName(i), headers.getValue(i));
+        }
+        outputBuffer.endHeaders();
+
+    }
+
+
+    /**
+     * Initialize standard input and output filters.
+     */
+    protected void initializeFilters() {
+
+        // Create and add the identity filters.
+        inputBuffer.addFilter(new IdentityInputFilter());
+        outputBuffer.addFilter(new IdentityOutputFilter());
+
+        // Create and add the chunked filters.
+        inputBuffer.addFilter(new ChunkedInputFilter());
+        outputBuffer.addFilter(new ChunkedOutputFilter());
+
+        // Create and add the void filters.
+        inputBuffer.addFilter(new VoidInputFilter());
+        outputBuffer.addFilter(new VoidOutputFilter());
+
+        // Create and add buffered input filter
+        inputBuffer.addFilter(new BufferedInputFilter());
+
+        // Create and add the chunked filters.
+        //inputBuffer.addFilter(new GzipInputFilter());
+        outputBuffer.addFilter(new GzipOutputFilter());
+
+    }
+
+
+    /**
+     * Add an input filter to the current request.
+     * 
+     * @return false if the encoding was not found (which would mean it is 
+     * unsupported)
+     */
+    protected boolean addInputFilter(InputFilter[] inputFilters, 
+                                     String encodingName) {
+        if (encodingName.equals("identity")) {
+            // Skip
+        } else if (encodingName.equals("chunked")) {
+            inputBuffer.addActiveFilter
+                (inputFilters[Constants.CHUNKED_FILTER]);
+            contentDelimitation = true;
+        } else {
+            for (int i = 2; i < inputFilters.length; i++) {
+                if (inputFilters[i].getEncodingName()
+                    .toString().equals(encodingName)) {
+                    inputBuffer.addActiveFilter(inputFilters[i]);
+                    return true;
+                }
+            }
+            return false;
+        }
+        return true;
+    }
+
+
+    /**
+     * Specialized utility method: find a sequence of lower case bytes inside
+     * a ByteChunk.
+     */
+    protected int findBytes(ByteChunk bc, byte[] b) {
+
+        byte first = b[0];
+        byte[] buff = bc.getBuffer();
+        int start = bc.getStart();
+        int end = bc.getEnd();
+
+    // Look for first char 
+    int srcEnd = b.length;
+        
+    for (int i = start; i <= (end - srcEnd); i++) {
+        if (Ascii.toLower(buff[i]) != first) continue;
+        // found first char, now look for a match
+            int myPos = i+1;
+        for (int srcPos = 1; srcPos < srcEnd; ) {
+                if (Ascii.toLower(buff[myPos++]) != b[srcPos++])
+            break;
+                if (srcPos == srcEnd) return i - start; // found it
+        }
+    }
+    return -1;
+
+    }
+
+    /**
+     * Determine if we must drop the connection because of the HTTP status
+     * code.  Use the same list of codes as Apache/httpd.
+     */
+    protected boolean statusDropsConnection(int status) {
+        return status == 400 /* SC_BAD_REQUEST */ ||
+               status == 408 /* SC_REQUEST_TIMEOUT */ ||
+               status == 411 /* SC_LENGTH_REQUIRED */ ||
+               status == 413 /* SC_REQUEST_ENTITY_TOO_LARGE */ ||
+               status == 414 /* SC_REQUEST_URI_TOO_LARGE */ ||
+               status == 500 /* SC_INTERNAL_SERVER_ERROR */ ||
+               status == 503 /* SC_SERVICE_UNAVAILABLE */ ||
+               status == 501 /* SC_NOT_IMPLEMENTED */;
+    }
+
+}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/Http11Protocol.java b/connectors/http11/src/java/org/apache/coyote/http11/Http11Protocol.java
new file mode 100644
index 0000000..9dd789f
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/Http11Protocol.java
@@ -0,0 +1,841 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.http11;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.URLEncoder;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.commons.modeler.Registry;
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.ActionHook;
+import org.apache.coyote.Adapter;
+import org.apache.coyote.ProtocolHandler;
+import org.apache.coyote.RequestGroupInfo;
+import org.apache.coyote.RequestInfo;
+import org.apache.tomcat.util.net.PoolTcpEndpoint;
+import org.apache.tomcat.util.net.SSLImplementation;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.ServerSocketFactory;
+import org.apache.tomcat.util.net.TcpConnection;
+import org.apache.tomcat.util.net.TcpConnectionHandler;
+import org.apache.tomcat.util.res.StringManager;
+import org.apache.tomcat.util.threads.ThreadPool;
+import org.apache.tomcat.util.threads.ThreadWithAttributes;
+
+
+/**
+ * Abstract the protocol implementation, including threading, etc.
+ * Processor is single threaded and specific to stream-based protocols,
+ * will not fit Jk protocols like JNI.
+ *
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ */
+public class Http11Protocol implements ProtocolHandler, MBeanRegistration
+{
+    public Http11Protocol() {
+        cHandler = new Http11ConnectionHandler( this );
+        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
+        setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
+        setServerSoTimeout(Constants.DEFAULT_SERVER_SOCKET_TIMEOUT);
+        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
+    }
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+    /** Pass config info
+     */
+    public void setAttribute( String name, Object value ) {
+        if( log.isTraceEnabled())
+            log.trace(sm.getString("http11protocol.setattribute", name, value));
+
+        attributes.put(name, value);
+    }
+
+    public Object getAttribute( String key ) {
+        if( log.isTraceEnabled())
+            log.trace(sm.getString("http11protocol.getattribute", key));
+        return attributes.get(key);
+    }
+
+    /**
+     * Set a property.
+     */
+    public void setProperty(String name, String value) {
+        setAttribute(name, value);
+    }
+
+    /**
+     * Get a property
+     */
+    public String getProperty(String name) {
+        return (String)getAttribute(name);
+    }
+
+    /** The adapter, used to call the connector 
+     */
+    public void setAdapter(Adapter adapter) {
+        this.adapter=adapter;
+    }
+
+    public Adapter getAdapter() {
+        return adapter;
+    }
+
+    
+    /** Start the protocol
+     */
+    public void init() throws Exception {
+        ep.setConnectionHandler( cHandler );
+    try {
+            checkSocketFactory();
+        } catch( Exception ex ) {
+            log.error(sm.getString("http11protocol.socketfactory.initerror"),
+                      ex);
+            throw ex;
+        }
+
+        if( socketFactory!=null ) {
+            Enumeration attE=attributes.keys();
+            while( attE.hasMoreElements() ) {
+                String key=(String)attE.nextElement();
+                Object v=attributes.get( key );
+                socketFactory.setAttribute( key, v );
+            }
+        }
+
+        // XXX get domain from registration
+        try {
+            ep.initEndpoint();
+        } catch (Exception ex) {
+            log.error(sm.getString("http11protocol.endpoint.initerror"), ex);
+            throw ex;
+        }
+        log.info(sm.getString("http11protocol.init", getName()));
+
+    }
+    
+    ObjectName tpOname;
+    ObjectName rgOname;
+    
+    public void start() throws Exception {
+        if( this.domain != null ) {
+            try {
+                // XXX We should be able to configure it separately
+                // XXX It should be possible to use a single TP
+                tpOname=new ObjectName
+                    (domain + ":" + "type=ThreadPool,name=" + getName());
+                Registry.getRegistry(null, null)
+                    .registerComponent(tp, tpOname, null );
+                tp.setName(getName());
+                tp.setDaemon(false);
+                tp.addThreadPoolListener(new MXPoolListener(this, tp));
+            } catch (Exception e) {
+                log.error("Can't register threadpool" );
+            }
+            rgOname=new ObjectName
+                (domain + ":type=GlobalRequestProcessor,name=" + getName());
+            Registry.getRegistry(null, null).registerComponent
+                ( cHandler.global, rgOname, null );
+        }
+
+        try {
+            ep.startEndpoint();
+        } catch (Exception ex) {
+            log.error(sm.getString("http11protocol.endpoint.starterror"), ex);
+            throw ex;
+        }
+        log.info(sm.getString("http11protocol.start", getName()));
+    }
+
+    public void pause() throws Exception {
+        try {
+            ep.pauseEndpoint();
+        } catch (Exception ex) {
+            log.error(sm.getString("http11protocol.endpoint.pauseerror"), ex);
+            throw ex;
+        }
+        log.info(sm.getString("http11protocol.pause", getName()));
+    }
+
+    public void resume() throws Exception {
+        try {
+            ep.resumeEndpoint();
+        } catch (Exception ex) {
+            log.error(sm.getString("http11protocol.endpoint.resumeerror"), ex);
+            throw ex;
+        }
+        log.info(sm.getString("http11protocol.resume", getName()));
+    }
+
+    public void destroy() throws Exception {
+        log.info(sm.getString("http11protocol.stop", getName()));
+        ep.stopEndpoint();
+        if( tpOname!=null ) 
+            Registry.getRegistry(null, null).unregisterComponent(tpOname);
+        if( rgOname != null ) 
+            Registry.getRegistry(null, null).unregisterComponent(rgOname);
+    }
+    
+    // -------------------- Properties--------------------
+    protected ThreadPool tp=ThreadPool.createThreadPool(true);
+    protected PoolTcpEndpoint ep=new PoolTcpEndpoint(tp);
+    protected boolean secure;
+    
+    protected ServerSocketFactory socketFactory;
+    protected SSLImplementation sslImplementation;
+    // socket factory attriubtes ( XXX replace with normal setters ) 
+    protected Hashtable attributes = new Hashtable();
+    protected String socketFactoryName=null;
+    protected String sslImplementationName=null;
+
+    private int maxKeepAliveRequests=100; // as in Apache HTTPD server
+    private int timeout = 300000;   // 5 minutes as in Apache HTTPD server
+    private int maxPostSize = 2 * 1024 * 1024;
+    private int maxHttpHeaderSize = 4 * 1024;
+    private String reportedname;
+    private int socketCloseDelay=-1;
+    private boolean disableUploadTimeout = true;
+    private int socketBuffer = 9000;
+    private Adapter adapter;
+    private Http11ConnectionHandler cHandler;
+
+    /**
+     * Compression value.
+     */
+    private String compression = "off";
+    private String noCompressionUserAgents = null;
+    private String restrictedUserAgents = null;
+    private String compressableMimeTypes = "text/html,text/xml,text/plain";
+    private int compressionMinSize    = 2048;
+    
+    // -------------------- Pool setup --------------------
+
+    public boolean getPools(){
+        return ep.isPoolOn();
+    }
+    
+    public void setPools( boolean t ) {
+        ep.setPoolOn(t);
+        setAttribute("pools", "" + t);
+    }
+
+    public int getMaxThreads() {
+        return ep.getMaxThreads();
+    }
+    
+    public void setMaxThreads( int maxThreads ) {
+        ep.setMaxThreads(maxThreads);
+        setAttribute("maxThreads", "" + maxThreads);
+    }
+
+    public int getMaxSpareThreads() {
+        return ep.getMaxSpareThreads();
+    }
+    
+    public void setMaxSpareThreads( int maxThreads ) {
+        ep.setMaxSpareThreads(maxThreads);
+        setAttribute("maxSpareThreads", "" + maxThreads);
+    }
+    
+    public int getMinSpareThreads() {
+        return ep.getMinSpareThreads();
+    }
+
+    public void setMinSpareThreads( int minSpareThreads ) {
+        ep.setMinSpareThreads(minSpareThreads);
+        setAttribute("minSpareThreads", "" + minSpareThreads);
+    }
+
+    public void setThreadPriority(int threadPriority) {
+      ep.setThreadPriority(threadPriority);
+      setAttribute("threadPriority", "" + threadPriority);
+    }
+
+    public int getThreadPriority() {
+      return ep.getThreadPriority();
+    }
+
+    // -------------------- Tcp setup --------------------
+
+    public int getBacklog() {
+        return ep.getBacklog();
+    }
+    
+    public void setBacklog( int i ) {
+        ep.setBacklog(i);
+        setAttribute("backlog", "" + i);
+    }
+    
+    public int getPort() {
+        return ep.getPort();
+    }
+    
+    public void setPort( int port ) {
+        ep.setPort(port);
+        setAttribute("port", "" + port);
+        //this.port=port;
+    }
+
+    public InetAddress getAddress() {
+        return ep.getAddress();
+    }
+    
+    public void setAddress(InetAddress ia) {
+        ep.setAddress( ia );
+        setAttribute("address", "" + ia);
+    }
+    
+    public String getName() {
+        String encodedAddr = "";
+        if (getAddress() != null) {
+            encodedAddr = "" + getAddress();
+            if (encodedAddr.startsWith("/"))
+                encodedAddr = encodedAddr.substring(1);
+            encodedAddr = URLEncoder.encode(encodedAddr) + "-";
+        }
+        return ("http-" + encodedAddr + ep.getPort());
+    }
+    
+    // commenting out for now since it's not doing anything
+    //public void setHostName( String name ) {
+    // ??? Doesn't seem to be used in existing or prev code
+    // vhost=name;
+    //}
+
+    public String getSocketFactory() {
+        return socketFactoryName;
+    }
+    
+    public void setSocketFactory( String valueS ) {
+        socketFactoryName = valueS;
+        setAttribute("socketFactory", valueS);
+    }
+    
+    public String getSSLImplementation() {
+        return sslImplementationName;
+    }
+    
+    public void setSSLImplementation( String valueS) {
+        sslImplementationName = valueS;
+        setAttribute("sslImplementation", valueS);
+    }
+    
+    public boolean getTcpNoDelay() {
+        return ep.getTcpNoDelay();
+    }
+    
+    public void setTcpNoDelay( boolean b ) {
+        ep.setTcpNoDelay( b );
+        setAttribute("tcpNoDelay", "" + b);
+    }
+
+    public boolean getDisableUploadTimeout() {
+        return disableUploadTimeout;
+    }
+    
+    public void setDisableUploadTimeout(boolean isDisabled) {
+        disableUploadTimeout = isDisabled;
+    }
+
+    public int getSocketBuffer() {
+        return socketBuffer;
+    }
+    
+    public void setSocketBuffer(int valueI) {
+        socketBuffer = valueI;
+    }
+
+    public String getCompression() {
+        return compression;
+    }
+    
+    public void setCompression(String valueS) {
+        compression = valueS;
+        setAttribute("compression", valueS);
+    }
+
+    public int getMaxPostSize() {
+        return maxPostSize;
+    }
+    
+    public void setMaxPostSize(int valueI) {
+        maxPostSize = valueI;
+        setAttribute("maxPostSize", "" + valueI);
+    }
+
+    public int getMaxHttpHeaderSize() {
+        return maxHttpHeaderSize;
+    }
+    
+    public void setMaxHttpHeaderSize(int valueI) {
+        maxHttpHeaderSize = valueI;
+        setAttribute("maxHttpHeaderSize", "" + valueI);
+    }
+
+    public String getRestrictedUserAgents() {
+        return restrictedUserAgents;
+    }
+    
+    public void setRestrictedUserAgents(String valueS) {
+        restrictedUserAgents = valueS;
+        setAttribute("restrictedUserAgents", valueS);
+    }
+
+    public String getNoCompressionUserAgents() {
+        return noCompressionUserAgents;
+    }
+    
+    public void setNoCompressionUserAgents(String valueS) {
+        noCompressionUserAgents = valueS;
+        setAttribute("noCompressionUserAgents", valueS);
+    }
+
+    public String getCompressableMimeType() {
+        return compressableMimeTypes;
+    }
+    
+    public void setCompressableMimeType(String valueS) {
+        compressableMimeTypes = valueS;
+        setAttribute("compressableMimeTypes", valueS);
+    }
+
+    public int getCompressionMinSize() {
+        return compressionMinSize;
+    }
+    
+    public void setCompressionMinSize(int valueI) {
+        compressionMinSize = valueI;
+        setAttribute("compressionMinSize", "" + valueI);
+    }
+
+    public int getSoLinger() {
+        return ep.getSoLinger();
+    }
+    
+    public void setSoLinger( int i ) {
+        ep.setSoLinger( i );
+        setAttribute("soLinger", "" + i);
+    }
+
+    public int getSoTimeout() {
+        return ep.getSoTimeout();
+    }
+    
+    public void setSoTimeout( int i ) {
+        ep.setSoTimeout(i);
+        setAttribute("soTimeout", "" + i);
+    }
+    
+    public int getServerSoTimeout() {
+        return ep.getServerSoTimeout();
+    }
+    
+    public void setServerSoTimeout( int i ) {
+        ep.setServerSoTimeout(i);
+        setAttribute("serverSoTimeout", "" + i);
+    }
+    
+    public String getKeystore() {
+        return getProperty("keystore");
+    }
+    
+    public void setKeystore( String k ) {
+        setAttribute("keystore", k);
+    }
+
+    public String getKeypass() {
+        return getProperty("keypass");
+    }
+    
+    public void setKeypass( String k ) {
+        attributes.put("keypass", k);
+        //setAttribute("keypass", k);
+    }
+    
+    public String getKeytype() {
+        return getProperty("keystoreType");
+    }
+    
+    public void setKeytype( String k ) {
+        setAttribute("keystoreType", k);
+    }
+
+    public String getClientauth() {
+        return getProperty("clientauth");
+    }
+    
+    public void setClientauth( String k ) {
+        setAttribute("clientauth", k);
+    }
+
+    public String getProtocol() {
+        return getProperty("protocol");
+    }
+    
+    public void setProtocol( String k ) {
+        setAttribute("protocol", k);
+    }
+
+    public String getProtocols() {
+        return getProperty("protocols");
+    }
+    
+    public void setProtocols(String k) {
+        setAttribute("protocols", k);
+    }
+
+    public String getAlgorithm() {
+        return getProperty("algorithm");
+    }
+    
+    public void setAlgorithm( String k ) {
+        setAttribute("algorithm", k);
+    }
+
+    public boolean getSecure() {
+        return secure;
+    }
+    
+    public void setSecure( boolean b ) {
+        secure=b;
+        setAttribute("secure", "" + b);
+    }
+
+    public String getCiphers() {
+        return getProperty("ciphers");
+    }
+    
+    public void setCiphers(String ciphers) {
+        setAttribute("ciphers", ciphers);
+    }
+
+    public String getKeyAlias() {
+        return getProperty("keyAlias");
+    }
+    
+    public void setKeyAlias(String keyAlias) {
+        setAttribute("keyAlias", keyAlias);
+    }
+
+    public int getMaxKeepAliveRequests() {
+        return maxKeepAliveRequests;
+    }
+    
+    /** Set the maximum number of Keep-Alive requests that we will honor.
+     */
+    public void setMaxKeepAliveRequests(int mkar) {
+        maxKeepAliveRequests = mkar;
+        setAttribute("maxKeepAliveRequests", "" + mkar);
+    }
+
+    public int getSocketCloseDelay() {
+        return socketCloseDelay;
+    }
+    
+    public void setSocketCloseDelay( int d ) {
+        socketCloseDelay=d;
+        setAttribute("socketCloseDelay", "" + d);
+    }
+
+    private static ServerSocketFactory string2SocketFactory( String val)
+    throws ClassNotFoundException, IllegalAccessException,
+    InstantiationException
+    {
+        Class chC=Class.forName( val );
+        return (ServerSocketFactory)chC.newInstance();
+    }
+
+    public int getTimeout() {
+        return timeout;
+    }
+    
+    public void setTimeout( int timeouts ) {
+        timeout = timeouts * 1000;
+        setAttribute("timeout", "" + timeouts);
+    }
+
+    public String getReportedname() {
+        return reportedname;
+    }
+    
+    public void setReportedname( String reportedName) {
+        reportedname = reportedName;
+    }
+    
+    // --------------------  Connection handler --------------------
+    public static final int THREAD_DATA_PROCESSOR=1;
+    public static final int THREAD_DATA_OBJECT_NAME=2;
+    
+    
+    static class MXPoolListener implements ThreadPool.ThreadPoolListener {
+        MXPoolListener( Http11Protocol proto, ThreadPool control ) {
+            
+        }
+
+        public void threadStart(ThreadPool tp, Thread t) {
+        }
+
+        public void threadEnd(ThreadPool tp, Thread t) {
+            // Register our associated processor
+            // TP uses only TWA
+            ThreadWithAttributes ta=(ThreadWithAttributes)t;
+            Object tpData[]=ta.getThreadData(tp);
+            if( tpData==null ) return;
+            // Weird artifact - it should be cleaned up, but that may break something
+            // and it won't gain us too much
+            if( tpData[1] instanceof Object[] ) {
+                tpData=(Object [])tpData[1];
+            }
+            ObjectName oname=(ObjectName)tpData[Http11Protocol.THREAD_DATA_OBJECT_NAME];
+            if( oname==null ) return;
+            Registry.getRegistry(null, null).unregisterComponent(oname);
+            Http11Processor processor = 
+                (Http11Processor) tpData[Http11Protocol.THREAD_DATA_PROCESSOR];
+            RequestInfo rp=processor.getRequest().getRequestProcessor();
+            rp.setGlobalProcessor(null);
+        }
+    }
+
+    static class Http11ConnectionHandler implements TcpConnectionHandler {
+        Http11Protocol proto;
+        static int count=0;
+        RequestGroupInfo global=new RequestGroupInfo();
+
+        Http11ConnectionHandler( Http11Protocol proto ) {
+            this.proto=proto;
+        }
+        
+        public void setAttribute( String name, Object value ) {
+        }
+        
+        public void setServer( Object o ) {
+        }
+    
+        public Object[] init() {
+            Object thData[]=new Object[3];
+            
+            Http11Processor  processor = 
+                new Http11Processor(proto.maxHttpHeaderSize);
+            processor.setAdapter( proto.adapter );
+            processor.setThreadPool( proto.tp );
+            processor.setMaxKeepAliveRequests( proto.maxKeepAliveRequests );
+            processor.setTimeout( proto.timeout );
+            processor.setDisableUploadTimeout( proto.disableUploadTimeout );
+            processor.setCompression( proto.compression );
+            processor.setCompressionMinSize( proto.compressionMinSize);
+            processor.setNoCompressionUserAgents( proto.noCompressionUserAgents);
+            processor.setCompressableMimeTypes( proto.compressableMimeTypes);
+            processor.setRestrictedUserAgents( proto.restrictedUserAgents);
+            processor.setSocketBuffer( proto.socketBuffer );
+            processor.setMaxPostSize( proto.maxPostSize );
+
+            thData[Http11Protocol.THREAD_DATA_PROCESSOR]=processor;
+            
+            if( proto.getDomain() != null ) {
+                try {
+                    RequestInfo rp=processor.getRequest().getRequestProcessor();
+                    rp.setGlobalProcessor(global);
+                    ObjectName rpName=new ObjectName
+                        (proto.getDomain() + ":type=RequestProcessor,worker=" 
+                         + proto.getName() +",name=HttpRequest" + count++ );
+                    Registry.getRegistry(null, null).registerComponent( rp, rpName, null);
+                    thData[Http11Protocol.THREAD_DATA_OBJECT_NAME]=rpName;
+                } catch( Exception ex ) {
+                    log.warn("Error registering request");
+                }
+            }
+
+            return  thData;
+        }
+
+        public void processConnection(TcpConnection connection,
+                      Object thData[]) {
+            Socket socket=null;
+            Http11Processor  processor=null;
+            try {
+                processor=(Http11Processor)thData[Http11Protocol.THREAD_DATA_PROCESSOR];
+                
+                if (processor instanceof ActionHook) {
+                    ((ActionHook) processor).action(ActionCode.ACTION_START, null);
+                }
+                socket=connection.getSocket();
+                
+                InputStream in = socket.getInputStream();
+                OutputStream out = socket.getOutputStream();
+
+                if( proto.secure ) {
+                    SSLSupport sslSupport=null;
+                    if(proto.sslImplementation != null)
+                        sslSupport = proto.sslImplementation.getSSLSupport(socket);
+                    processor.setSSLSupport(sslSupport);
+                } else {
+                    processor.setSSLSupport( null );
+                }
+                processor.setSocket( socket );
+                
+                processor.process(in, out);
+                
+                // If unread input arrives after the shutdownInput() call
+                // below and before or during the socket.close(), an error
+                // may be reported to the client.  To help troubleshoot this
+                // type of error, provide a configurable delay to give the
+                // unread input time to arrive so it can be successfully read
+                // and discarded by shutdownInput().
+                if( proto.socketCloseDelay >= 0 ) {
+                    try {
+                        Thread.sleep(proto.socketCloseDelay);
+                    } catch (InterruptedException ie) { /* ignore */ }
+                }
+                
+                TcpConnection.shutdownInput( socket );
+            } catch(java.net.SocketException e) {
+                // SocketExceptions are normal
+                Http11Protocol.log.debug
+                    (sm.getString
+                     ("http11protocol.proto.socketexception.debug"), e);
+            } catch (java.io.IOException e) {
+                // IOExceptions are normal 
+                Http11Protocol.log.debug
+                    (sm.getString
+                     ("http11protocol.proto.ioexception.debug"), e);
+            }
+            // Future developers: if you discover any other
+            // rare-but-nonfatal exceptions, catch them here, and log as
+            // above.
+            catch (Throwable e) {
+                // any other exception or error is odd. Here we log it
+                // with "ERROR" level, so it will show up even on
+                // less-than-verbose logs.
+                Http11Protocol.log.error
+                    (sm.getString("http11protocol.proto.error"), e);
+            } finally {
+                //       if(proto.adapter != null) proto.adapter.recycle();
+                //                processor.recycle();
+                
+                if (processor instanceof ActionHook) {
+                    ((ActionHook) processor).action(ActionCode.ACTION_STOP, null);
+                }
+                // recycle kernel sockets ASAP
+                try { if (socket != null) socket.close (); }
+                catch (IOException e) { /* ignore */ }
+            }
+        }
+    }
+
+    protected static org.apache.commons.logging.Log log 
+        = org.apache.commons.logging.LogFactory.getLog(Http11Protocol.class);
+
+    // -------------------- Various implementation classes --------------------
+
+    /** Sanity check and socketFactory setup.
+     *  IMHO it is better to stop the show on a broken connector,
+     *  then leave Tomcat running and broken.
+     *  @exception TomcatException Unable to resolve classes
+     */
+    private void checkSocketFactory() throws Exception {
+        if (secure) {
+            try {
+                // The SSL setup code has been moved into
+                // SSLImplementation since SocketFactory doesn't
+                // provide a wide enough interface
+                sslImplementation =
+                    SSLImplementation.getInstance(sslImplementationName);
+                socketFactory = sslImplementation.getServerSocketFactory();
+                ep.setServerSocketFactory(socketFactory);
+            } catch (ClassNotFoundException e){
+                throw e;
+            }
+        } else if (socketFactoryName != null) {
+            try {
+                socketFactory = string2SocketFactory(socketFactoryName);
+                ep.setServerSocketFactory(socketFactory);
+            } catch(Exception sfex) {
+                throw sfex;
+            }
+        }
+    }
+
+    /*
+    public boolean isKeystoreSet() {
+        return (attributes.get("keystore") != null);
+    }
+
+    public boolean isKeypassSet() {
+        return (attributes.get("keypass") != null);
+    }
+
+    public boolean isClientauthSet() {
+        return (attributes.get("clientauth") != null);
+    }
+
+    public boolean isAttributeSet( String attr ) {
+        return (attributes.get(attr) != null);
+    }
+
+    public boolean isSecure() {
+        return secure;
+    }
+   
+    public PoolTcpEndpoint getEndpoint() {
+        return ep;
+    }
+    */
+    
+    protected String domain;
+    protected ObjectName oname;
+    protected MBeanServer mserver;
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public ObjectName preRegister(MBeanServer server,
+                                  ObjectName name) throws Exception {
+        oname=name;
+        mserver=server;
+        domain=name.getDomain();
+        return name;
+    }
+
+    public void postRegister(Boolean registrationDone) {
+    }
+
+    public void preDeregister() throws Exception {
+    }
+
+    public void postDeregister() {
+    }
+}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/InputFilter.java b/connectors/http11/src/java/org/apache/coyote/http11/InputFilter.java
new file mode 100644
index 0000000..a734c60
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/InputFilter.java
@@ -0,0 +1,81 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.http11;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.Request;
+
+/**
+ * Input filter interface.
+ * 
+ * @author Remy Maucherat
+ */
+public interface InputFilter extends InputBuffer {
+
+
+    /**
+     * Read bytes.
+     * 
+     * @return Number of bytes read.
+     */
+    public int doRead(ByteChunk chunk, Request unused)
+        throws IOException;
+
+
+    /**
+     * Some filters need additional parameters from the request. All the 
+     * necessary reading can occur in that method, as this method is called
+     * after the request header processing is complete.
+     */
+    public void setRequest(Request request);
+
+
+    /**
+     * Make the filter ready to process the next request.
+     */
+    public void recycle();
+
+
+    /**
+     * Get the name of the encoding handled by this filter.
+     */
+    public ByteChunk getEncodingName();
+
+
+    /**
+     * Set the next buffer in the filter pipeline.
+     */
+    public void setBuffer(InputBuffer buffer);
+
+
+    /**
+     * End the current request.
+     * 
+     * @return 0 is the expected return value. A positive value indicates that
+     * too many bytes were read. This method is allowed to use buffer.doRead
+     * to consume extra bytes. The result of this method can't be negative (if
+     * an error happens, an IOException should be thrown instead).
+     */
+    public long end()
+        throws IOException;
+
+
+}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/InternalInputBuffer.java b/connectors/http11/src/java/org/apache/coyote/http11/InternalInputBuffer.java
new file mode 100644
index 0000000..a4a41d3
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/InternalInputBuffer.java
@@ -0,0 +1,793 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+
+package org.apache.coyote.http11;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.EOFException;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.res.StringManager;
+
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.Request;
+
+/**
+ * Implementation of InputBuffer which provides HTTP request header parsing as
+ * well as transfer decoding.
+ *
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ */
+public class InternalInputBuffer implements InputBuffer {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor.
+     */
+    public InternalInputBuffer(Request request) {
+        this(request, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
+    }
+
+
+    /**
+     * Alternate constructor.
+     */
+    public InternalInputBuffer(Request request, int headerBufferSize) {
+
+        this.request = request;
+        headers = request.getMimeHeaders();
+
+        headerBuffer1 = new byte[headerBufferSize];
+        headerBuffer2 = new byte[headerBufferSize];
+        bodyBuffer = new byte[headerBufferSize];
+        buf = headerBuffer1;
+
+        headerBuffer = new char[headerBufferSize];
+        ascbuf = headerBuffer;
+
+        inputStreamInputBuffer = new InputStreamInputBuffer();
+
+        filterLibrary = new InputFilter[0];
+        activeFilters = new InputFilter[0];
+        lastActiveFilter = -1;
+
+        parsingHeader = true;
+        swallowInput = true;
+
+    }
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated Coyote request.
+     */
+    protected Request request;
+
+
+    /**
+     * Headers of the associated request.
+     */
+    protected MimeHeaders headers;
+
+
+    /**
+     * State.
+     */
+    protected boolean parsingHeader;
+
+
+    /**
+     * Swallow input ? (in the case of an expectation)
+     */
+    protected boolean swallowInput;
+
+
+    /**
+     * Pointer to the current read buffer.
+     */
+    protected byte[] buf;
+
+
+    /**
+     * Pointer to the US-ASCII header buffer.
+     */
+    protected char[] ascbuf;
+
+
+    /**
+     * Last valid byte.
+     */
+    protected int lastValid;
+
+
+    /**
+     * Position in the buffer.
+     */
+    protected int pos;
+
+
+    /**
+     * HTTP header buffer no 1.
+     */
+    protected byte[] headerBuffer1;
+
+
+    /**
+     * HTTP header buffer no 2.
+     */
+    protected byte[] headerBuffer2;
+
+
+    /**
+     * HTTP body buffer.
+     */
+    protected byte[] bodyBuffer;
+
+
+    /**
+     * US-ASCII header buffer.
+     */
+    protected char[] headerBuffer;
+
+
+    /**
+     * Underlying input stream.
+     */
+    protected InputStream inputStream;
+
+
+    /**
+     * Underlying input buffer.
+     */
+    protected InputBuffer inputStreamInputBuffer;
+
+
+    /**
+     * Filter library.
+     * Note: Filter[0] is always the "chunked" filter.
+     */
+    protected InputFilter[] filterLibrary;
+
+
+    /**
+     * Active filters (in order).
+     */
+    protected InputFilter[] activeFilters;
+
+
+    /**
+     * Index of the last active filter.
+     */
+    protected int lastActiveFilter;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Set the underlying socket input stream.
+     */
+    public void setInputStream(InputStream inputStream) {
+
+        // FIXME: Check for null ?
+
+        this.inputStream = inputStream;
+
+    }
+
+
+    /**
+     * Get the underlying socket input stream.
+     */
+    public InputStream getInputStream() {
+
+        return inputStream;
+
+    }
+
+
+    /**
+     * Add an input filter to the filter library.
+     */
+    public void addFilter(InputFilter filter) {
+
+        // FIXME: Check for null ?
+
+        InputFilter[] newFilterLibrary = 
+            new InputFilter[filterLibrary.length + 1];
+        for (int i = 0; i < filterLibrary.length; i++) {
+            newFilterLibrary[i] = filterLibrary[i];
+        }
+        newFilterLibrary[filterLibrary.length] = filter;
+        filterLibrary = newFilterLibrary;
+
+        activeFilters = new InputFilter[filterLibrary.length];
+
+    }
+
+
+    /**
+     * Get filters.
+     */
+    public InputFilter[] getFilters() {
+
+        return filterLibrary;
+
+    }
+
+
+    /**
+     * Clear filters.
+     */
+    public void clearFilters() {
+
+        filterLibrary = new InputFilter[0];
+        lastActiveFilter = -1;
+
+    }
+
+
+    /**
+     * Add an input filter to the filter library.
+     */
+    public void addActiveFilter(InputFilter filter) {
+
+        if (lastActiveFilter == -1) {
+            filter.setBuffer(inputStreamInputBuffer);
+        } else {
+            for (int i = 0; i <= lastActiveFilter; i++) {
+                if (activeFilters[i] == filter)
+                    return;
+            }
+            filter.setBuffer(activeFilters[lastActiveFilter]);
+        }
+
+        activeFilters[++lastActiveFilter] = filter;
+
+        filter.setRequest(request);
+
+    }
+
+
+    /**
+     * Set the swallow input flag.
+     */
+    public void setSwallowInput(boolean swallowInput) {
+        this.swallowInput = swallowInput;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Recycle the input buffer. This should be called when closing the 
+     * connection.
+     */
+    public void recycle() {
+
+        // Recycle Request object
+        request.recycle();
+
+        inputStream = null;
+        buf = headerBuffer1;
+        lastValid = 0;
+        pos = 0;
+        lastActiveFilter = -1;
+        parsingHeader = true;
+        swallowInput = true;
+
+    }
+
+
+    /**
+     * End processing of current HTTP request.
+     * Note: All bytes of the current request should have been already 
+     * consumed. This method only resets all the pointers so that we are ready
+     * to parse the next HTTP request.
+     */
+    public void nextRequest()
+        throws IOException {
+
+        // Recycle Request object
+        request.recycle();
+
+        // Determine the header buffer used for next request
+        byte[] newHeaderBuf = null;
+        if (buf == headerBuffer1) {
+            newHeaderBuf = headerBuffer2;
+        } else {
+            newHeaderBuf = headerBuffer1;
+        }
+
+        // Copy leftover bytes from buf to newHeaderBuf
+        System.arraycopy(buf, pos, newHeaderBuf, 0, lastValid - pos);
+
+        // Swap buffers
+        buf = newHeaderBuf;
+
+        // Recycle filters
+        for (int i = 0; i <= lastActiveFilter; i++) {
+            activeFilters[i].recycle();
+        }
+
+        // Reset pointers
+        lastValid = lastValid - pos;
+        pos = 0;
+        lastActiveFilter = -1;
+        parsingHeader = true;
+        swallowInput = true;
+
+    }
+
+
+    /**
+     * End request (consumes leftover bytes).
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    public void endRequest()
+        throws IOException {
+
+        if (swallowInput && (lastActiveFilter != -1)) {
+            int extraBytes = (int) activeFilters[lastActiveFilter].end();
+            pos = pos - extraBytes;
+        }
+
+    }
+
+
+    /**
+     * Read the request line. This function is meant to be used during the 
+     * HTTP request header parsing. Do NOT attempt to read the request body 
+     * using it.
+     *
+     * @throws IOException If an exception occurs during the underlying socket
+     * read operations, or if the given buffer is not big enough to accomodate
+     * the whole line.
+     */
+    public void parseRequestLine()
+        throws IOException {
+
+        int start = 0;
+
+        //
+        // Skipping blank lines
+        //
+
+        byte chr = 0;
+        do {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            chr = buf[pos++];
+
+        } while ((chr == Constants.CR) || (chr == Constants.LF));
+
+        pos--;
+
+        // Mark the current buffer position
+        start = pos;
+
+        //
+        // Reading the method name
+        // Method name is always US-ASCII
+        //
+
+        boolean space = false;
+
+        while (!space) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            ascbuf[pos] = (char) buf[pos];
+
+            if (buf[pos] == Constants.SP) {
+                space = true;
+                request.method().setChars(ascbuf, start, pos - start);
+            }
+
+            pos++;
+
+        }
+
+        // Mark the current buffer position
+        start = pos;
+        int end = 0;
+        int questionPos = -1;
+
+        //
+        // Reading the URI
+        //
+
+        space = false;
+        boolean eol = false;
+
+        while (!space) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            if (buf[pos] == Constants.SP) {
+                space = true;
+                end = pos;
+            } else if ((buf[pos] == Constants.CR) 
+                       || (buf[pos] == Constants.LF)) {
+                // HTTP/0.9 style request
+                eol = true;
+                space = true;
+                end = pos;
+            } else if ((buf[pos] == Constants.QUESTION) 
+                       && (questionPos == -1)) {
+                questionPos = pos;
+            }
+
+            pos++;
+
+        }
+
+        request.unparsedURI().setBytes(buf, start, end - start);
+        if (questionPos >= 0) {
+            request.queryString().setBytes(buf, questionPos + 1, 
+                                           end - questionPos - 1);
+            request.requestURI().setBytes(buf, start, questionPos - start);
+        } else {
+            request.requestURI().setBytes(buf, start, end - start);
+        }
+
+        // Mark the current buffer position
+        start = pos;
+        end = 0;
+
+        //
+        // Reading the protocol
+        // Protocol is always US-ASCII
+        //
+
+        while (!eol) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            ascbuf[pos] = (char) buf[pos];
+
+            if (buf[pos] == Constants.CR) {
+                end = pos;
+            } else if (buf[pos] == Constants.LF) {
+                if (end == 0)
+                    end = pos;
+                eol = true;
+            }
+
+            pos++;
+
+        }
+
+        if ((end - start) > 0) {
+            request.protocol().setChars(ascbuf, start, end - start);
+        } else {
+            request.protocol().setString("");
+        }
+
+    }
+
+
+    /**
+     * Parse the HTTP headers.
+     */
+    public void parseHeaders()
+        throws IOException {
+
+        while (parseHeader()) {
+        }
+
+        parsingHeader = false;
+
+    }
+
+
+    /**
+     * Parse an HTTP header.
+     * 
+     * @return false after reading a blank line (which indicates that the
+     * HTTP header parsing is done
+     */
+    public boolean parseHeader()
+        throws IOException {
+
+        //
+        // Check for blank line
+        //
+
+        byte chr = 0;
+        while (true) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            chr = buf[pos];
+
+            if ((chr == Constants.CR) || (chr == Constants.LF)) {
+                if (chr == Constants.LF) {
+                    pos++;
+                    return false;
+                }
+            } else {
+                break;
+            }
+
+            pos++;
+
+        }
+
+        // Mark the current buffer position
+        int start = pos;
+
+        //
+        // Reading the header name
+        // Header name is always US-ASCII
+        //
+
+        boolean colon = false;
+        MessageBytes headerValue = null;
+
+        while (!colon) {
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            if (buf[pos] == Constants.COLON) {
+                colon = true;
+                headerValue = headers.addValue(ascbuf, start, pos - start);
+            }
+            chr = buf[pos];
+            if ((chr >= Constants.A) && (chr <= Constants.Z)) {
+                buf[pos] = (byte) (chr - Constants.LC_OFFSET);
+            }
+
+            ascbuf[pos] = (char) buf[pos];
+
+            pos++;
+
+        }
+
+        // Mark the current buffer position
+        start = pos;
+        int realPos = pos;
+
+        //
+        // Reading the header value (which can be spanned over multiple lines)
+        //
+
+        boolean eol = false;
+        boolean validLine = true;
+
+        while (validLine) {
+
+            boolean space = true;
+
+            // Skipping spaces
+            while (space) {
+
+                // Read new bytes if needed
+                if (pos >= lastValid) {
+                    if (!fill())
+                        throw new EOFException(sm.getString("iib.eof.error"));
+                }
+
+                if ((buf[pos] == Constants.SP) || (buf[pos] == Constants.HT)) {
+                    pos++;
+                } else {
+                    space = false;
+                }
+
+            }
+
+            int lastSignificantChar = realPos;
+
+            // Reading bytes until the end of the line
+            while (!eol) {
+
+                // Read new bytes if needed
+                if (pos >= lastValid) {
+                    if (!fill())
+                        throw new EOFException(sm.getString("iib.eof.error"));
+                }
+
+                if (buf[pos] == Constants.CR) {
+                } else if (buf[pos] == Constants.LF) {
+                    eol = true;
+                } else if (buf[pos] == Constants.SP) {
+                    buf[realPos] = buf[pos];
+                    realPos++;
+                } else {
+                    buf[realPos] = buf[pos];
+                    realPos++;
+                    lastSignificantChar = realPos;
+                }
+
+                pos++;
+
+            }
+
+            realPos = lastSignificantChar;
+
+            // Checking the first character of the new line. If the character
+            // is a LWS, then it's a multiline header
+
+            // Read new bytes if needed
+            if (pos >= lastValid) {
+                if (!fill())
+                    throw new EOFException(sm.getString("iib.eof.error"));
+            }
+
+            chr = buf[pos];
+            if ((chr != Constants.SP) && (chr != Constants.HT)) {
+                validLine = false;
+            } else {
+                eol = false;
+                // Copying one extra space in the buffer (since there must
+                // be at least one space inserted between the lines)
+                buf[realPos] = chr;
+                realPos++;
+            }
+
+        }
+
+        // Set the header value
+        headerValue.setBytes(buf, start, realPos - start);
+
+        return true;
+
+    }
+
+
+    // ---------------------------------------------------- InputBuffer Methods
+
+
+    /**
+     * Read some bytes.
+     */
+    public int doRead(ByteChunk chunk, Request req) 
+        throws IOException {
+
+        if (lastActiveFilter == -1)
+            return inputStreamInputBuffer.doRead(chunk, req);
+        else
+            return activeFilters[lastActiveFilter].doRead(chunk,req);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Fill the internal buffer using data from the undelying input stream.
+     * 
+     * @return false if at end of stream
+     */
+    protected boolean fill()
+        throws IOException {
+
+        int nRead = 0;
+
+        if (parsingHeader) {
+
+            if (lastValid == buf.length) {
+                throw new IOException
+                    (sm.getString("iib.requestheadertoolarge.error"));
+            }
+
+            nRead = inputStream.read(buf, pos, buf.length - lastValid);
+            if (nRead > 0) {
+                lastValid = pos + nRead;
+            }
+
+        } else {
+
+            buf = bodyBuffer;
+            pos = 0;
+            lastValid = 0;
+            nRead = inputStream.read(buf, 0, buf.length);
+            if (nRead > 0) {
+                lastValid = nRead;
+            }
+
+        }
+
+        return (nRead > 0);
+
+    }
+
+
+    // ------------------------------------- InputStreamInputBuffer Inner Class
+
+
+    /**
+     * This class is an input buffer which will read its data from an input
+     * stream.
+     */
+    protected class InputStreamInputBuffer 
+        implements InputBuffer {
+
+
+        /**
+         * Read bytes into the specified chunk.
+         */
+        public int doRead(ByteChunk chunk, Request req ) 
+            throws IOException {
+
+            if (pos >= lastValid) {
+                if (!fill())
+                    return -1;
+            }
+
+            int length = lastValid - pos;
+            chunk.setBytes(buf, pos, length);
+            pos = lastValid;
+
+            return (length);
+
+        }
+
+
+    }
+
+
+}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/InternalOutputBuffer.java b/connectors/http11/src/java/org/apache/coyote/http11/InternalOutputBuffer.java
new file mode 100644
index 0000000..7be02b7
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/InternalOutputBuffer.java
@@ -0,0 +1,790 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.http11;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.HttpMessages;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.res.StringManager;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Response;
+
+/**
+ * Output buffer.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ */
+public class InternalOutputBuffer 
+    implements OutputBuffer, ByteChunk.ByteOutputChannel {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor.
+     */
+    public InternalOutputBuffer(Response response) {
+        this(response, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
+    }
+
+
+    /**
+     * Alternate constructor.
+     */
+    public InternalOutputBuffer(Response response, int headerBufferSize) {
+
+        this.response = response;
+        headers = response.getMimeHeaders();
+
+        headerBuffer = new byte[headerBufferSize];
+        buf = headerBuffer;
+
+        outputStreamOutputBuffer = new OutputStreamOutputBuffer();
+
+        filterLibrary = new OutputFilter[0];
+        activeFilters = new OutputFilter[0];
+        lastActiveFilter = -1;
+
+        socketBuffer = new ByteChunk();
+        socketBuffer.setByteOutputChannel(this);
+
+        committed = false;
+        finished = false;
+
+    }
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager(Constants.Package);
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Associated Coyote response.
+     */
+    protected Response response;
+
+
+    /**
+     * Headers of the associated request.
+     */
+    protected MimeHeaders headers;
+
+
+    /**
+     * Committed flag.
+     */
+    protected boolean committed;
+
+
+    /**
+     * Finished flag.
+     */
+    protected boolean finished;
+
+
+    /**
+     * Pointer to the current read buffer.
+     */
+    protected byte[] buf;
+
+
+    /**
+     * Position in the buffer.
+     */
+    protected int pos;
+
+
+    /**
+     * HTTP header buffer.
+     */
+    protected byte[] headerBuffer;
+
+
+    /**
+     * Underlying output stream.
+     */
+    protected OutputStream outputStream;
+
+
+    /**
+     * Underlying output buffer.
+     */
+    protected OutputBuffer outputStreamOutputBuffer;
+
+
+    /**
+     * Filter library.
+     * Note: Filter[0] is always the "chunked" filter.
+     */
+    protected OutputFilter[] filterLibrary;
+
+
+    /**
+     * Active filter (which is actually the top of the pipeline).
+     */
+    protected OutputFilter[] activeFilters;
+
+
+    /**
+     * Index of the last active filter.
+     */
+    protected int lastActiveFilter;
+
+
+    /**
+     * Socket buffer.
+     */
+    protected ByteChunk socketBuffer;
+
+
+    /**
+     * Socket buffer (extra buffering to reduce number of packets sent).
+     */
+    protected boolean useSocketBuffer = false;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Set the underlying socket output stream.
+     */
+    public void setOutputStream(OutputStream outputStream) {
+
+        // FIXME: Check for null ?
+
+        this.outputStream = outputStream;
+
+    }
+
+
+    /**
+     * Get the underlying socket output stream.
+     */
+    public OutputStream getOutputStream() {
+
+        return outputStream;
+
+    }
+
+
+    /**
+     * Set the socket buffer size.
+     */
+    public void setSocketBuffer(int socketBufferSize) {
+
+        if (socketBufferSize > 500) {
+            useSocketBuffer = true;
+            socketBuffer.allocate(socketBufferSize, socketBufferSize);
+        } else {
+            useSocketBuffer = false;
+        }
+
+    }
+
+
+    /**
+     * Add an output filter to the filter library.
+     */
+    public void addFilter(OutputFilter filter) {
+
+        OutputFilter[] newFilterLibrary = 
+            new OutputFilter[filterLibrary.length + 1];
+        for (int i = 0; i < filterLibrary.length; i++) {
+            newFilterLibrary[i] = filterLibrary[i];
+        }
+        newFilterLibrary[filterLibrary.length] = filter;
+        filterLibrary = newFilterLibrary;
+
+        activeFilters = new OutputFilter[filterLibrary.length];
+
+    }
+
+
+    /**
+     * Get filters.
+     */
+    public OutputFilter[] getFilters() {
+
+        return filterLibrary;
+
+    }
+
+
+    /**
+     * Clear filters.
+     */
+    public void clearFilters() {
+
+        filterLibrary = new OutputFilter[0];
+        lastActiveFilter = -1;
+
+    }
+
+
+    /**
+     * Add an output filter to the filter library.
+     */
+    public void addActiveFilter(OutputFilter filter) {
+
+        if (lastActiveFilter == -1) {
+            filter.setBuffer(outputStreamOutputBuffer);
+        } else {
+            for (int i = 0; i <= lastActiveFilter; i++) {
+                if (activeFilters[i] == filter)
+                    return;
+            }
+            filter.setBuffer(activeFilters[lastActiveFilter]);
+        }
+
+        activeFilters[++lastActiveFilter] = filter;
+
+        filter.setResponse(response);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Flush the response.
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    public void flush()
+        throws IOException {
+
+        if (!committed) {
+
+            // Send the connector a request for commit. The connector should
+            // then validate the headers, send them (using sendHeader) and 
+            // set the filters accordingly.
+            response.action(ActionCode.ACTION_COMMIT, null);
+
+        }
+
+        // Flush the current buffer
+        if (useSocketBuffer) {
+            socketBuffer.flushBuffer();
+        }
+
+    }
+
+
+    /**
+     * Reset current response.
+     * 
+     * @throws IllegalStateException if the response has already been committed
+     */
+    public void reset() {
+
+        if (committed)
+            throw new IllegalStateException(/*FIXME:Put an error message*/);
+
+        // Recycle Request object
+        response.recycle();
+
+    }
+
+
+    /**
+     * Recycle the output buffer. This should be called when closing the 
+     * connection.
+     */
+    public void recycle() {
+
+        // Recycle Request object
+        response.recycle();
+        socketBuffer.recycle();
+
+        outputStream = null;
+        buf = headerBuffer;
+        pos = 0;
+        lastActiveFilter = -1;
+        committed = false;
+        finished = false;
+
+    }
+
+
+    /**
+     * End processing of current HTTP request.
+     * Note: All bytes of the current request should have been already 
+     * consumed. This method only resets all the pointers so that we are ready
+     * to parse the next HTTP request.
+     */
+    public void nextRequest() {
+
+        // Recycle Request object
+        response.recycle();
+        socketBuffer.recycle();
+
+        // Determine the header buffer used for next request
+        buf = headerBuffer;
+
+        // Recycle filters
+        for (int i = 0; i <= lastActiveFilter; i++) {
+            activeFilters[i].recycle();
+        }
+
+        // Reset pointers
+        pos = 0;
+        lastActiveFilter = -1;
+        committed = false;
+        finished = false;
+
+    }
+
+
+    /**
+     * End request.
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    public void endRequest()
+        throws IOException {
+
+        if (!committed) {
+
+            // Send the connector a request for commit. The connector should
+            // then validate the headers, send them (using sendHeader) and 
+            // set the filters accordingly.
+            response.action(ActionCode.ACTION_COMMIT, null);
+
+        }
+
+        if (finished)
+            return;
+
+        if (lastActiveFilter != -1)
+            activeFilters[lastActiveFilter].end();
+
+        if (useSocketBuffer) {
+            socketBuffer.flushBuffer();
+        }
+
+        finished = true;
+
+    }
+
+
+    // ------------------------------------------------ HTTP/1.1 Output Methods
+
+
+    /**
+     * Send an acknoledgement.
+     */
+    public void sendAck()
+        throws IOException {
+
+        if (!committed)
+            outputStream.write(Constants.ACK_BYTES);
+
+    }
+
+
+    /**
+     * Send the response status line.
+     */
+    public void sendStatus() {
+
+        // Write protocol name
+        write("HTTP/1.1 ");
+
+        // Write status code
+        int status = response.getStatus();
+	switch (status) {
+	case 200:
+            write("200");
+	    break;
+	case 400:
+            write("400");
+	    break;
+	case 404:
+            write("404");
+	    break;
+        default:
+	    write(status);
+	}
+
+        write(" ");
+
+        // Write message
+        String message = response.getMessage();
+        if (message == null) {
+            write(getMessage(status));
+        } else {
+            write(message);
+        }
+
+        // End the response status line
+        if (System.getSecurityManager() != null){
+           AccessController.doPrivileged(
+                new PrivilegedAction(){
+                    public Object run(){
+                        write(Constants.CRLF_BYTES);
+                        return null;
+                    }
+                }
+           );
+        } else {
+            write(Constants.CRLF_BYTES);
+        }
+
+    }
+
+    private String getMessage(final int message){
+        if (System.getSecurityManager() != null){
+           return (String)AccessController.doPrivileged(
+                new PrivilegedAction(){
+                    public Object run(){
+                        return HttpMessages.getMessage(message); 
+                    }
+                }
+           );
+        } else {
+            return HttpMessages.getMessage(message);
+        }
+    }
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(MessageBytes name, MessageBytes value) {
+
+        write(name);
+        write(": ");
+        write(value);
+        write(Constants.CRLF_BYTES);
+
+    }
+
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(ByteChunk name, ByteChunk value) {
+
+        write(name);
+        write(": ");
+        write(value);
+        write(Constants.CRLF_BYTES);
+
+    }
+
+
+    /**
+     * Send a header.
+     * 
+     * @param name Header name
+     * @param value Header value
+     */
+    public void sendHeader(String name, String value) {
+
+        write(name);
+        write(": ");
+        write(value);
+        write(Constants.CRLF_BYTES);
+
+    }
+
+
+    /**
+     * End the header block.
+     */
+    public void endHeaders() {
+
+        write(Constants.CRLF_BYTES);
+
+    }
+
+
+    // --------------------------------------------------- OutputBuffer Methods
+
+
+    /**
+     * Write the contents of a byte chunk.
+     * 
+     * @param chunk byte chunk
+     * @return number of bytes written
+     * @throws IOException an undelying I/O error occured
+     */
+    public int doWrite(ByteChunk chunk, Response res) 
+        throws IOException {
+
+        if (!committed) {
+
+            // Send the connector a request for commit. The connector should
+            // then validate the headers, send them (using sendHeaders) and 
+            // set the filters accordingly.
+            response.action(ActionCode.ACTION_COMMIT, null);
+
+        }
+
+        if (lastActiveFilter == -1)
+            return outputStreamOutputBuffer.doWrite(chunk, res);
+        else
+            return activeFilters[lastActiveFilter].doWrite(chunk, res);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Commit the response.
+     * 
+     * @throws IOException an undelying I/O error occured
+     */
+    protected void commit()
+        throws IOException {
+
+        // The response is now committed
+        committed = true;
+        response.setCommitted(true);
+
+        if (pos > 0) {
+            // Sending the response header buffer
+            if (useSocketBuffer) {
+                socketBuffer.append(buf, 0, pos);
+            } else {
+                outputStream.write(buf, 0, pos);
+            }
+        }
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied message bytes 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param mb data to be written
+     */
+    protected void write(MessageBytes mb) {
+
+        if (mb.getType() == MessageBytes.T_BYTES) {
+            ByteChunk bc = mb.getByteChunk();
+            write(bc);
+        } else if (mb.getType() == MessageBytes.T_CHARS) {
+            CharChunk cc = mb.getCharChunk();
+            write(cc);
+        } else {
+            write(mb.toString());
+        }
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied message bytes 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param bc data to be written
+     */
+    protected void write(ByteChunk bc) {
+
+        // Writing the byte chunk to the output buffer
+        System.arraycopy(bc.getBytes(), bc.getStart(), buf, pos,
+                         bc.getLength());
+        pos = pos + bc.getLength();
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied char 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param cc data to be written
+     */
+    protected void write(CharChunk cc) {
+
+        int start = cc.getStart();
+        int end = cc.getEnd();
+        char[] cbuf = cc.getBuffer();
+        for (int i = start; i < end; i++) {
+            char c = cbuf[i];
+            // Note:  This is clearly incorrect for many strings,
+            // but is the only consistent approach within the current
+            // servlet framework.  It must suffice until servlet output
+            // streams properly encode their output.
+            if ((c & 0xff00) != 0) {
+                // High order byte must be zero
+                //log("Header character is not iso8859_1, " +
+                //"not supported yet: " + c, Log.ERROR ) ;
+            }
+            if (c != 9) {
+                if ((c >= 0) && (c <= 31)) {
+                    c = ' ';
+                }
+                if (c == 127) {
+                    c = ' ';
+                }
+            }
+            buf[pos++] = (byte) c;
+        }
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied byte 
+     * buffer to the output stream, without filtering. This method is meant to
+     * be used to write the response header.
+     * 
+     * @param b data to be written
+     */
+    protected void write(byte[] b) {
+
+        // Writing the byte chunk to the output buffer
+        System.arraycopy(b, 0, buf, pos, b.length);
+        pos = pos + b.length;
+
+    }
+
+
+    /**
+     * This method will write the contents of the specyfied String to the 
+     * output stream, without filtering. This method is meant to be used to 
+     * write the response header.
+     * 
+     * @param s data to be written
+     */
+    protected void write(String s) {
+
+        if (s == null)
+            return;
+
+        // From the Tomcat 3.3 HTTP/1.0 connector
+        int len = s.length();
+        for (int i = 0; i < len; i++) {
+            char c = s.charAt (i);
+            // Note:  This is clearly incorrect for many strings,
+            // but is the only consistent approach within the current
+            // servlet framework.  It must suffice until servlet output
+            // streams properly encode their output.
+            if ((c & 0xff00) != 0) {
+                // High order byte must be zero
+                //log("Header character is not iso8859_1, " +
+                //"not supported yet: " + c, Log.ERROR ) ;
+            }
+            if (c != 9) {
+                if ((c >= 0) && (c <= 31)) {
+                    c = ' ';
+                }
+                if (c == 127) {
+                    c = ' ';
+                }
+            }
+            buf[pos++] = (byte) c;
+        }
+
+    }
+
+
+    /**
+     * This method will print the specified integer to the output stream, 
+     * without filtering. This method is meant to be used to write the 
+     * response header.
+     * 
+     * @param i data to be written
+     */
+    protected void write(int i) {
+
+        write(String.valueOf(i));
+
+    }
+
+
+    /**
+     * Callback to write data from the buffer.
+     */
+    public void realWriteBytes(byte cbuf[], int off, int len)
+        throws IOException {
+        if (len > 0) {
+            outputStream.write(cbuf, off, len);
+        }
+    }
+
+
+    // ----------------------------------- OutputStreamOutputBuffer Inner Class
+
+
+    /**
+     * This class is an output buffer which will write data to an output
+     * stream.
+     */
+    protected class OutputStreamOutputBuffer 
+        implements OutputBuffer {
+
+
+        /**
+         * Write chunk.
+         */
+        public int doWrite(ByteChunk chunk, Response res) 
+            throws IOException {
+
+            if (useSocketBuffer) {
+                socketBuffer.append(chunk.getBuffer(), chunk.getStart(), 
+                                   chunk.getLength());
+            } else {
+                outputStream.write(chunk.getBuffer(), chunk.getStart(), 
+                                   chunk.getLength());
+            }
+            return chunk.getLength();
+
+        }
+
+
+    }
+
+
+}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/LocalStrings.properties b/connectors/http11/src/java/org/apache/coyote/http11/LocalStrings.properties
new file mode 100644
index 0000000..cf613ae
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/LocalStrings.properties
@@ -0,0 +1,39 @@
+# $Id$
+
+# language 
+
+# package org.apache.coyote.http11
+
+#
+# Http11Protocol
+#
+
+http11protocol.endpoint.initerror=Error initializing endpoint
+http11protocol.endpoint.starterror=Error starting endpoint
+http11protocol.init=Initializing Coyote HTTP/1.1 on {0}
+http11protocol.proto.error=Error reading request, ignored
+http11protocol.proto.ioexception.debug=IOException reading request
+http11protocol.proto.ioexception.info=IOException reading request, ignored
+http11protocol.proto.socketexception.debug=SocketException reading request
+http11protocol.proto.socketexception.info=SocketException reading request, ignored
+http11protocol.getattribute=Attribute {0}
+http11protocol.setattribute=Attribute {0}: {1}
+http11protocol.socketfactory.initerror=Error initializing socket factory
+http11protocol.start=Starting Coyote HTTP/1.1 on {0}
+http11protocol.stop=Stopping Coyote HTTP/1.1 on {0}
+http11protocol.pause=Pausing Coyote HTTP/1.1 on {0}
+http11protocol.endpoint.pauseerror=Error pausing endpoint
+http11protocol.resume=Resuming Coyote HTTP/1.1 on {0}
+http11protocol.endpoint.resumeerror=Error resuming endpoint
+
+#
+# Http11Processor
+#
+
+#
+# InternalInputBuffer
+#
+
+iib.eof.error=Unexpected EOF read on the socket
+iib.requestheadertoolarge.error=Request header is too large
+
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/LocalStrings_es.properties b/connectors/http11/src/java/org/apache/coyote/http11/LocalStrings_es.properties
new file mode 100644
index 0000000..1d6002c
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/LocalStrings_es.properties
@@ -0,0 +1,34 @@
+# $Id$
+
+# language es
+
+# package org.apache.coyote.http11
+
+#
+# Http11Protocol
+#
+
+http11protocol.endpoint.initerror=Error inicializando punto final (endpoint)
+http11protocol.endpoint.starterror=Error arrancando punto final (endpoint)
+http11protocol.init=Inicializando Coyote HTTP/1.1 en puerto {0}
+http11protocol.proto.error=Error leyendo requerimiento, ignorado
+http11protocol.proto.ioexception.debug=IOException leyendo requerimiento
+http11protocol.proto.ioexception.info=IOException leyendo requerimiento, ignorada
+http11protocol.proto.socketexception.debug=SocketException leyendo requerimiento
+http11protocol.proto.socketexception.info=SocketException leyendo requerimiento, ignorada
+http11protocol.getattribute=Atributo {0}
+http11protocol.setattribute=Atributo {0}: {1}
+http11protocol.socketfactory.initerror=Error inicializando fábrica de enchufes (sockets)
+http11protocol.start=Arrancando Coyote HTTP/1.1 en puerto {0}
+
+#
+# Http11Processor
+#
+
+#
+# InternalInputBuffer
+#
+
+iib.eof.error=Inesperado Fin De Archivo (EOF) leído en el enchufe (socket)
+iib.requestheadertoolarge.error=La cabecera del requerimiento es demasido grande
+
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/LocalStrings_fr.properties b/connectors/http11/src/java/org/apache/coyote/http11/LocalStrings_fr.properties
new file mode 100644
index 0000000..c4d53a8
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/LocalStrings_fr.properties
@@ -0,0 +1,36 @@
+# $Id$
+
+# language fr
+
+# package org.apache.coyote.http11
+
+#
+# Http11Protocol
+#
+
+http11protocol.endpoint.initerror=Erreur à l'initialisation du point de contact
+http11protocol.endpoint.starterror=Erreur au démarrage du point de contact
+http11protocol.init=Initialisation de Coyote HTTP/1.1 sur {0}
+http11protocol.proto.error=Erreur à la lecture de la requête, ignoré
+http11protocol.proto.ioexception.debug=Exception d'entrée/sortie (IOException) à la lecture de la requête
+http11protocol.proto.ioexception.info=Exception d'entrée/sortie à la lecture de la requête, ignoré
+http11protocol.proto.socketexception.debug=Exception "Socket" (SocketException) à la lecture de la requête
+http11protocol.proto.socketexception.info=Exception "Socket" (SocketException) à la lecture de la requête, ignoré
+http11protocol.setattribute=Attribut {0}: {1}
+http11protocol.socketfactory.initerror=Erreur à l'initialisation du créateur de socket (socket factory)
+http11protocol.start=Démarrage de Coyote HTTP/1.1 sur {0}
+http11protocol.stop=Arrêt de Coyote HTTP/1.1 sur {0}
+http11protocol.pause=Suspension de Coyote HTTP/1.1 sur {0}
+http11protocol.resume=Redémarrage de Coyote HTTP/1.1 sur {0}
+
+#
+# Http11Processor
+#
+
+#
+# InternalInputBuffer
+#
+
+iib.eof.error=Fin de flux (EOF) inattendue à la lecture sur la socket
+iib.requestheadertoolarge.error=L'entête de requête est trop important
+
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/LocalStrings_ja.properties b/connectors/http11/src/java/org/apache/coyote/http11/LocalStrings_ja.properties
new file mode 100644
index 0000000..653de69
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/LocalStrings_ja.properties
@@ -0,0 +1,39 @@
+# $Id$
+
+# language ja
+
+# package org.apache.coyote.http11
+
+#
+# Http11Protocol
+#
+
+http11protocol.endpoint.initerror=\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u3092\u521d\u671f\u5316\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+http11protocol.endpoint.starterror=\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u3092\u8d77\u52d5\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+http11protocol.init=Coyote HTTP/1.1\u3092 {0} \u3067\u521d\u671f\u5316\u3057\u307e\u3059
+http11protocol.proto.error=\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059\u304c\u3001\u7121\u8996\u3055\u308c\u307e\u3057\u305f
+http11protocol.proto.ioexception.debug=\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u306eIOException\u3067\u3059
+http11protocol.proto.ioexception.info=\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u306eIOException\u3067\u3059\u304c\u3001\u7121\u8996\u3055\u308c\u307e\u3057\u305f
+http11protocol.proto.socketexception.debug=\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u306eSocketException\u3067\u3059
+http11protocol.proto.socketexception.info=\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u8aad\u307f\u8fbc\u307f\u4e2d\u306eSocketException\u3067\u3059\u304c\u3001\u7121\u8996\u3055\u308c\u307e\u3057\u305f
+http11protocol.setattribute=\u5c5e\u6027 {0}: {1}
+http11protocol.socketfactory.initerror=\u30bd\u30b1\u30c3\u30c8\u30d5\u30a1\u30af\u30c8\u30ea\u3092\u521d\u671f\u5316\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+http11protocol.start=Coyote HTTP/1.1\u3092 {0} \u3067\u8d77\u52d5\u3057\u307e\u3059
+http11protocol.stop=Coyote HTTP/1.1\u3092 {0} \u3067\u505c\u6b62\u3057\u307e\u3059
+http11protocol.pause=Coyote HTTP/1.1\u3092 {0} \u3067\u4e00\u6642\u505c\u6b62\u3057\u307e\u3059
+http11protocol.endpoint.pauseerror=\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u306e\u4e00\u6642\u505c\u6b62\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+http11protocol.resume=Coyote HTTP/1.1\u3092 {0} \u3067\u518d\u958b\u3057\u307e\u3059
+http11protocol.endpoint.resumeerror=\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u306e\u518d\u958b\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
+ 
+
+#
+# Http11Processor
+#
+
+#
+# InternalInputBuffer
+#
+
+iib.eof.error=\u30bd\u30b1\u30c3\u30c8\u304b\u3089\u4e88\u671f\u3057\u306a\u3044EOF\u3092\u8aad\u307f\u8fbc\u307f\u307e\u3057\u305f
+iib.requestheadertoolarge.error=\u30ea\u30af\u30a8\u30b9\u30c8\u30d8\u30c3\u30c0\u304c\u9577\u3059\u304e\u307e\u3059
+
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/OutputFilter.java b/connectors/http11/src/java/org/apache/coyote/http11/OutputFilter.java
new file mode 100644
index 0000000..bd073c5
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/OutputFilter.java
@@ -0,0 +1,82 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.http11;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Response;
+
+/**
+ * Output filter.
+ * 
+ * @author Remy Maucherat
+ */
+public interface OutputFilter extends OutputBuffer {
+
+
+    /**
+     * Write some bytes.
+     * 
+     * @return number of bytes written by the filter
+     */
+    public int doWrite(ByteChunk chunk, Response unused)
+        throws IOException;
+
+
+    /**
+     * Some filters need additional parameters from the response. All the 
+     * necessary reading can occur in that method, as this method is called
+     * after the response header processing is complete.
+     */
+    public void setResponse(Response response);
+
+
+    /**
+     * Make the filter ready to process the next request.
+     */
+    public void recycle();
+
+
+    /**
+     * Get the name of the encoding handled by this filter.
+     */
+    public ByteChunk getEncodingName();
+
+
+    /**
+     * Set the next buffer in the filter pipeline.
+     */
+    public void setBuffer(OutputBuffer buffer);
+
+
+    /**
+     * End the current request. It is acceptable to write extra bytes using
+     * buffer.doWrite during the execution of this method.
+     * 
+     * @return Should return 0 unless the filter does some content length 
+     * delimitation, in which case the number is the amount of extra bytes or
+     * missing bytes, which would indicate an error. 
+     * Note: It is recommended that extra bytes be swallowed by the filter.
+     */
+    public long end()
+        throws IOException;
+
+
+}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/filters/BufferedInputFilter.java b/connectors/http11/src/java/org/apache/coyote/http11/filters/BufferedInputFilter.java
new file mode 100644
index 0000000..411cd53
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/BufferedInputFilter.java
@@ -0,0 +1,122 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.http11.filters;
+
+import java.io.IOException;
+import org.apache.coyote.Request;
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.http11.InputFilter;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+/**
+ * Input filter responsible for reading and buffering the request body, so that
+ * it does not interfere with client SSL handshake messages.
+ */
+public class BufferedInputFilter implements InputFilter {
+
+    // -------------------------------------------------------------- Constants
+
+    private static final String ENCODING_NAME = "buffered";
+    private static final ByteChunk ENCODING = new ByteChunk();
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    private ByteChunk buffered = null;
+    private ByteChunk tempRead = new ByteChunk(1024);
+    private InputBuffer buffer;
+    private boolean hasRead = false;
+
+
+    // ----------------------------------------------------- Static Initializer
+
+    static {
+        ENCODING.setBytes(ENCODING_NAME.getBytes(), 0, ENCODING_NAME.length());
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Set the buffering limit. This should be reset every time the buffer is
+     * used.
+     */
+    public void setLimit(int limit) {
+        if (buffered == null) {
+            buffered = new ByteChunk(4048);
+            buffered.setLimit(limit);
+        }
+    }
+
+
+    // ---------------------------------------------------- InputBuffer Methods
+
+
+    /**
+     * Reads the request body and buffers it.
+     */
+    public void setRequest(Request request) {
+        // save off the Request body
+        try {
+            while (buffer.doRead(tempRead, request) >= 0) {
+                buffered.append(tempRead);
+                tempRead.recycle();
+            }
+        } catch(IOException iex) {
+            // Ignore
+        }
+    }
+
+    /**
+     * Fills the given ByteChunk with the buffered request body.
+     */
+    public int doRead(ByteChunk chunk, Request request) throws IOException {
+        if (hasRead || buffered.getLength() <= 0) {
+            return -1;
+        } else {
+            chunk.setBytes(buffered.getBytes(), buffered.getStart(),
+                           buffered.getLength());
+            hasRead = true;
+        }
+        return chunk.getLength();
+    }
+
+    public void setBuffer(InputBuffer buffer) {
+        this.buffer = buffer;
+    }
+
+    public void recycle() {
+        if (buffered.getBuffer().length > 65536) {
+            buffered = null;
+        } else {
+            buffered.recycle();
+        }
+        tempRead.recycle();
+        hasRead = false;
+        buffer = null;
+    }
+
+    public ByteChunk getEncodingName() {
+        return ENCODING;
+    }
+
+    public long end() throws IOException {
+        return 0;
+    }
+
+}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java b/connectors/http11/src/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
new file mode 100644
index 0000000..d12fc6a
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
@@ -0,0 +1,325 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.http11.filters;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.HexUtils;
+
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.Request;
+import org.apache.coyote.http11.Constants;
+import org.apache.coyote.http11.InputFilter;
+
+/**
+ * Chunked input filter.
+ * 
+ * @author Remy Maucherat
+ */
+public class ChunkedInputFilter implements InputFilter {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    protected static final String ENCODING_NAME = "chunked";
+    protected static final ByteChunk ENCODING = new ByteChunk();
+
+
+    // ----------------------------------------------------- Static Initializer
+
+
+    static {
+        ENCODING.setBytes(ENCODING_NAME.getBytes(), 0, ENCODING_NAME.length());
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Next buffer in the pipeline.
+     */
+    protected InputBuffer buffer;
+
+
+    /**
+     * Number of bytes remaining in the current chunk.
+     */
+    protected int remaining = 0;
+
+
+    /**
+     * Position in the buffer.
+     */
+    protected int pos = 0;
+
+
+    /**
+     * Last valid byte in the buffer.
+     */
+    protected int lastValid = 0;
+
+
+    /**
+     * Read bytes buffer.
+     */
+    protected byte[] buf = null;
+
+
+    /**
+     * Byte chunk used to read bytes.
+     */
+    protected ByteChunk readChunk = new ByteChunk();
+
+
+    /**
+     * Flag set to true when the end chunk has been read.
+     */
+    protected boolean endChunk = false;
+
+    /**
+     * Flag set to true if the next call to doRead() must parse a CRLF pair
+     * before doing anything else.
+     */
+    protected boolean needCRLFParse = false;
+
+    // ------------------------------------------------------------- Properties
+
+
+    // ---------------------------------------------------- InputBuffer Methods
+
+
+    /**
+     * Read bytes.
+     * 
+     * @return If the filter does request length control, this value is
+     * significant; it should be the number of bytes consumed from the buffer,
+     * up until the end of the current request body, or the buffer length, 
+     * whichever is greater. If the filter does not do request body length
+     * control, the returned value should be -1.
+     */
+    public int doRead(ByteChunk chunk, Request req)
+        throws IOException {
+
+        if (endChunk)
+            return -1;
+
+        if(needCRLFParse) {
+            needCRLFParse = false;
+            parseCRLF();
+        }
+
+        if (remaining <= 0) {
+            if (!parseChunkHeader()) {
+                throw new IOException("Invalid chunk");
+            }
+            if (endChunk) {
+                parseEndChunk();
+                return -1;
+            }
+        }
+
+        int result = 0;
+
+        if (pos >= lastValid) {
+            readBytes();
+        }
+
+        if (remaining > (lastValid - pos)) {
+            result = lastValid - pos;
+            remaining = remaining - result;
+            chunk.setBytes(buf, pos, result);
+            pos = lastValid;
+        } else {
+            result = remaining;
+            chunk.setBytes(buf, pos, remaining);
+            pos = pos + remaining;
+            remaining = 0;
+            needCRLFParse = true;
+        }
+
+        return result;
+
+    }
+
+
+    // ---------------------------------------------------- InputFilter Methods
+
+
+    /**
+     * Read the content length from the request.
+     */
+    public void setRequest(Request request) {
+    }
+
+
+    /**
+     * End the current request.
+     */
+    public long end()
+        throws IOException {
+
+        // Consume extra bytes : parse the stream until the end chunk is found
+        while (doRead(readChunk, null) >= 0) {
+        }
+
+        // Return the number of extra bytes which were consumed
+        return (lastValid - pos);
+
+    }
+
+
+    /**
+     * Set the next buffer in the filter pipeline.
+     */
+    public void setBuffer(InputBuffer buffer) {
+        this.buffer = buffer;
+    }
+
+
+    /**
+     * Make the filter ready to process the next request.
+     */
+    public void recycle() {
+        remaining = 0;
+        pos = 0;
+        lastValid = 0;
+        endChunk = false;
+    }
+
+
+    /**
+     * Return the name of the associated encoding; Here, the value is 
+     * "identity".
+     */
+    public ByteChunk getEncodingName() {
+        return ENCODING;
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Read bytes from the previous buffer.
+     */
+    protected int readBytes()
+        throws IOException {
+
+        int nRead = buffer.doRead(readChunk, null);
+        pos = readChunk.getStart();
+        lastValid = pos + nRead;
+        buf = readChunk.getBytes();
+
+        return nRead;
+
+    }
+
+
+    /**
+     * Parse the header of a chunk.
+     */
+    protected boolean parseChunkHeader()
+        throws IOException {
+
+        int result = 0;
+        boolean eol = false;
+        boolean readDigit = false;
+
+        while (!eol) {
+
+            if (pos >= lastValid) {
+                if (readBytes() <= 0)
+                    return false;
+            }
+
+            if (buf[pos] == Constants.CR) {
+            } else if (buf[pos] == Constants.LF) {
+                eol = true;
+            } else {
+                if (HexUtils.DEC[buf[pos]] != -1) {
+                    readDigit = true;
+                    result *= 16;
+                    result += HexUtils.DEC[buf[pos]];
+                }
+            }
+
+            pos++;
+
+        }
+
+        if (!readDigit)
+            return false;
+
+        if (result == 0)
+            endChunk = true;
+
+        remaining = result;
+        if (remaining < 0)
+            return false;
+
+        return true;
+
+    }
+
+
+    /**
+     * Parse CRLF at end of chunk.
+     */
+    protected boolean parseCRLF()
+        throws IOException {
+
+        boolean eol = false;
+
+        while (!eol) {
+
+            if (pos >= lastValid) {
+                if (readBytes() <= 0)
+                    throw new IOException("Invalid CRLF");
+            }
+
+            if (buf[pos] == Constants.CR) {
+            } else if (buf[pos] == Constants.LF) {
+                eol = true;
+            } else {
+                throw new IOException("Invalid CRLF");
+            }
+
+            pos++;
+
+        }
+
+        return true;
+
+    }
+
+
+    /**
+     * Parse end chunk data.
+     * FIXME: Handle trailers
+     */
+    protected boolean parseEndChunk()
+        throws IOException {
+
+        return parseCRLF(); // FIXME
+
+    }
+
+
+}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java b/connectors/http11/src/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java
new file mode 100644
index 0000000..9b01e3b
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java
@@ -0,0 +1,186 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.http11.filters;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.HexUtils;
+
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Response;
+import org.apache.coyote.http11.OutputFilter;
+
+/**
+ * Chunked output filter.
+ * 
+ * @author Remy Maucherat
+ */
+public class ChunkedOutputFilter implements OutputFilter {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    protected static final String ENCODING_NAME = "chunked";
+    protected static final ByteChunk ENCODING = new ByteChunk();
+
+
+    /**
+     * End chunk.
+     */
+    protected static final ByteChunk END_CHUNK = new ByteChunk();
+
+
+    // ----------------------------------------------------- Static Initializer
+
+
+    static {
+        ENCODING.setBytes(ENCODING_NAME.getBytes(), 0, ENCODING_NAME.length());
+        byte[] END_CHUNK_BYTES = {(byte) '0', (byte) '\r', (byte) '\n', 
+                                  (byte) '\r', (byte) '\n'};
+        END_CHUNK.setBytes(END_CHUNK_BYTES, 0, END_CHUNK_BYTES.length);
+    }
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Default constructor.
+     */
+    public ChunkedOutputFilter() {
+        chunkLength = new byte[10];
+        chunkLength[8] = (byte) '\r';
+        chunkLength[9] = (byte) '\n';
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Next buffer in the pipeline.
+     */
+    protected OutputBuffer buffer;
+
+
+    /**
+     * Buffer used for chunk length conversion.
+     */
+    protected byte[] chunkLength = new byte[10];
+
+
+    /**
+     * Chunk header.
+     */
+    protected ByteChunk chunkHeader = new ByteChunk();
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    // --------------------------------------------------- OutputBuffer Methods
+
+
+    /**
+     * Write some bytes.
+     * 
+     * @return number of bytes written by the filter
+     */
+    public int doWrite(ByteChunk chunk, Response res)
+        throws IOException {
+
+        int result = chunk.getLength();
+
+        if (result <= 0) {
+            return 0;
+        }
+
+        // Calculate chunk header
+        int pos = 7;
+        int current = result;
+        while (current > 0) {
+            int digit = current % 16;
+            current = current / 16;
+            chunkLength[pos--] = HexUtils.HEX[digit];
+        }
+        chunkHeader.setBytes(chunkLength, pos + 1, 9 - pos);
+        buffer.doWrite(chunkHeader, res);
+
+        buffer.doWrite(chunk, res);
+
+        chunkHeader.setBytes(chunkLength, 8, 2);
+        buffer.doWrite(chunkHeader, res);
+
+        return result;
+
+    }
+
+
+    // --------------------------------------------------- OutputFilter Methods
+
+
+    /**
+     * Some filters need additional parameters from the response. All the 
+     * necessary reading can occur in that method, as this method is called
+     * after the response header processing is complete.
+     */
+    public void setResponse(Response response) {
+    }
+
+
+    /**
+     * Set the next buffer in the filter pipeline.
+     */
+    public void setBuffer(OutputBuffer buffer) {
+        this.buffer = buffer;
+    }
+
+
+    /**
+     * End the current request. It is acceptable to write extra bytes using
+     * buffer.doWrite during the execution of this method.
+     */
+    public long end()
+        throws IOException {
+
+        // Write end chunk
+        buffer.doWrite(END_CHUNK, null);
+        
+        return 0;
+
+    }
+
+
+    /**
+     * Make the filter ready to process the next request.
+     */
+    public void recycle() {
+    }
+
+
+    /**
+     * Return the name of the associated encoding; Here, the value is 
+     * "identity".
+     */
+    public ByteChunk getEncodingName() {
+        return ENCODING;
+    }
+
+
+}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/filters/GzipOutputFilter.java b/connectors/http11/src/java/org/apache/coyote/http11/filters/GzipOutputFilter.java
new file mode 100644
index 0000000..dbd19d7
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/GzipOutputFilter.java
@@ -0,0 +1,170 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.http11.filters;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.GZIPOutputStream;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Response;
+import org.apache.coyote.http11.OutputFilter;
+
+/**
+ * Gzip output filter.
+ * 
+ * @author Remy Maucherat
+ */
+public class GzipOutputFilter implements OutputFilter {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    protected static final String ENCODING_NAME = "gzip";
+    protected static final ByteChunk ENCODING = new ByteChunk();
+
+
+    // ----------------------------------------------------- Static Initializer
+
+
+    static {
+        ENCODING.setBytes(ENCODING_NAME.getBytes(), 0, ENCODING_NAME.length());
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Next buffer in the pipeline.
+     */
+    protected OutputBuffer buffer;
+
+
+    /**
+     * Compression output stream.
+     */
+    protected GZIPOutputStream compressionStream = null;
+
+
+    /**
+     * Fake internal output stream.
+     */
+    protected OutputStream fakeOutputStream = new FakeOutputStream();
+
+
+    // --------------------------------------------------- OutputBuffer Methods
+
+
+    /**
+     * Write some bytes.
+     * 
+     * @return number of bytes written by the filter
+     */
+    public int doWrite(ByteChunk chunk, Response res)
+        throws IOException {
+        if (compressionStream == null) {
+            compressionStream = new GZIPOutputStream(fakeOutputStream);
+        }
+        compressionStream.write(chunk.getBytes(), chunk.getStart(), 
+                                chunk.getLength());
+        return chunk.getLength();
+    }
+
+
+    // --------------------------------------------------- OutputFilter Methods
+
+
+    /**
+     * Some filters need additional parameters from the response. All the 
+     * necessary reading can occur in that method, as this method is called
+     * after the response header processing is complete.
+     */
+    public void setResponse(Response response) {
+    }
+
+
+    /**
+     * Set the next buffer in the filter pipeline.
+     */
+    public void setBuffer(OutputBuffer buffer) {
+        this.buffer = buffer;
+    }
+
+
+    /**
+     * End the current request. It is acceptable to write extra bytes using
+     * buffer.doWrite during the execution of this method.
+     */
+    public long end()
+        throws IOException {
+        if (compressionStream == null) {
+            compressionStream = new GZIPOutputStream(fakeOutputStream);
+        }
+        compressionStream.finish();
+        compressionStream.close();
+        return ((OutputFilter) buffer).end();
+    }
+
+
+    /**
+     * Make the filter ready to process the next request.
+     */
+    public void recycle() {
+        // Set compression stream to null
+        compressionStream = null;
+    }
+
+
+    /**
+     * Return the name of the associated encoding; Here, the value is 
+     * "identity".
+     */
+    public ByteChunk getEncodingName() {
+        return ENCODING;
+    }
+
+
+    // ------------------------------------------- FakeOutputStream Inner Class
+
+
+    protected class FakeOutputStream
+        extends OutputStream {
+        protected ByteChunk outputChunk = new ByteChunk();
+        protected byte[] singleByteBuffer = new byte[1];
+        public void write(int b)
+            throws IOException {
+            // Shouldn't get used for good performance, but is needed for 
+            // compatibility with Sun JDK 1.4.0
+            singleByteBuffer[0] = (byte) (b & 0xff);
+            outputChunk.setBytes(singleByteBuffer, 0, 1);
+            buffer.doWrite(outputChunk, null);
+        }
+        public void write(byte[] b, int off, int len)
+            throws IOException {
+            outputChunk.setBytes(b, off, len);
+            buffer.doWrite(outputChunk, null);
+        }
+        public void flush() throws IOException {}
+        public void close() throws IOException {}
+    }
+
+
+}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/filters/IdentityInputFilter.java b/connectors/http11/src/java/org/apache/coyote/http11/filters/IdentityInputFilter.java
new file mode 100644
index 0000000..2545fbc
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/IdentityInputFilter.java
@@ -0,0 +1,201 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.http11.filters;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.Request;
+import org.apache.coyote.http11.InputFilter;
+
+/**
+ * Identity input filter.
+ * 
+ * @author Remy Maucherat
+ */
+public class IdentityInputFilter implements InputFilter {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    protected static final String ENCODING_NAME = "identity";
+    protected static final ByteChunk ENCODING = new ByteChunk();
+
+
+    // ----------------------------------------------------- Static Initializer
+
+
+    static {
+        ENCODING.setBytes(ENCODING_NAME.getBytes(), 0, ENCODING_NAME.length());
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Content length.
+     */
+    protected long contentLength = -1;
+
+
+    /**
+     * Remaining bytes.
+     */
+    protected long remaining = 0;
+
+
+    /**
+     * Next buffer in the pipeline.
+     */
+    protected InputBuffer buffer;
+
+
+    /**
+     * Chunk used to read leftover bytes.
+     */
+    protected ByteChunk endChunk = new ByteChunk();
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Get content length.
+     */
+    public long getContentLength() {
+        return contentLength;
+    }
+
+
+    /**
+     * Get remaining bytes.
+     */
+    public long getRemaining() {
+        return remaining;
+    }
+
+
+    // ---------------------------------------------------- InputBuffer Methods
+
+
+    /**
+     * Read bytes.
+     * 
+     * @return If the filter does request length control, this value is
+     * significant; it should be the number of bytes consumed from the buffer,
+     * up until the end of the current request body, or the buffer length, 
+     * whichever is greater. If the filter does not do request body length
+     * control, the returned value should be -1.
+     */
+    public int doRead(ByteChunk chunk, Request req)
+        throws IOException {
+
+        int result = -1;
+
+        if (contentLength >= 0) {
+            if (remaining > 0) {
+                int nRead = buffer.doRead(chunk, req);
+                if (nRead > remaining) {
+                    // The chunk is longer than the number of bytes remaining
+                    // in the body; changing the chunk length to the number
+                    // of bytes remaining
+                    chunk.setBytes(chunk.getBytes(), chunk.getStart(), 
+                                   (int) remaining);
+                    result = (int) remaining;
+                } else {
+                    result = nRead;
+                }
+                remaining = remaining - nRead;
+            } else {
+                // No more bytes left to be read : return -1 and clear the 
+                // buffer
+                chunk.recycle();
+                result = -1;
+            }
+        }
+
+        return result;
+
+    }
+
+
+    // ---------------------------------------------------- InputFilter Methods
+
+
+    /**
+     * Read the content length from the request.
+     */
+    public void setRequest(Request request) {
+        contentLength = request.getContentLengthLong();
+        remaining = contentLength;
+    }
+
+
+    /**
+     * End the current request.
+     */
+    public long end()
+        throws IOException {
+
+        // Consume extra bytes.
+        while (remaining > 0) {
+            int nread = buffer.doRead(endChunk, null);
+            if (nread > 0 ) {
+                remaining = remaining - nread;
+            } else { // errors are handled higher up.
+                remaining = 0;
+            }
+        }
+
+        // If too many bytes were read, return the amount.
+        return -remaining;
+
+    }
+
+
+    /**
+     * Set the next buffer in the filter pipeline.
+     */
+    public void setBuffer(InputBuffer buffer) {
+        this.buffer = buffer;
+    }
+
+
+    /**
+     * Make the filter ready to process the next request.
+     */
+    public void recycle() {
+        contentLength = -1;
+        remaining = 0;
+        endChunk.recycle();
+    }
+
+
+    /**
+     * Return the name of the associated encoding; Here, the value is 
+     * "identity".
+     */
+    public ByteChunk getEncodingName() {
+        return ENCODING;
+    }
+
+
+}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java b/connectors/http11/src/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java
new file mode 100644
index 0000000..f241729
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java
@@ -0,0 +1,189 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.http11.filters;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Response;
+import org.apache.coyote.http11.OutputFilter;
+
+/**
+ * Identity output filter.
+ * 
+ * @author Remy Maucherat
+ */
+public class IdentityOutputFilter implements OutputFilter {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    protected static final String ENCODING_NAME = "identity";
+    protected static final ByteChunk ENCODING = new ByteChunk();
+
+
+    // ----------------------------------------------------- Static Initializer
+
+
+    static {
+        ENCODING.setBytes(ENCODING_NAME.getBytes(), 0, ENCODING_NAME.length());
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Content length.
+     */
+    protected long contentLength = -1;
+
+
+    /**
+     * Remaining bytes.
+     */
+    protected long remaining = 0;
+
+
+    /**
+     * Next buffer in the pipeline.
+     */
+    protected OutputBuffer buffer;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Get content length.
+     */
+    public long getContentLength() {
+        return contentLength;
+    }
+
+
+    /**
+     * Get remaining bytes.
+     */
+    public long getRemaining() {
+        return remaining;
+    }
+
+
+    // --------------------------------------------------- OutputBuffer Methods
+
+
+    /**
+     * Write some bytes.
+     * 
+     * @return number of bytes written by the filter
+     */
+    public int doWrite(ByteChunk chunk, Response res)
+        throws IOException {
+
+        int result = -1;
+
+        if (contentLength >= 0) {
+            if (remaining > 0) {
+                result = chunk.getLength();
+                if (result > remaining) {
+                    // The chunk is longer than the number of bytes remaining
+                    // in the body; changing the chunk length to the number
+                    // of bytes remaining
+                    chunk.setBytes(chunk.getBytes(), chunk.getStart(), 
+                                   (int) remaining);
+                    result = (int) remaining;
+                    remaining = 0;
+                } else {
+                    remaining = remaining - result;
+                }
+                buffer.doWrite(chunk, res);
+            } else {
+                // No more bytes left to be written : return -1 and clear the 
+                // buffer
+                chunk.recycle();
+                result = -1;
+            }
+        } else {
+            // If no content length was set, just write the bytes
+            buffer.doWrite(chunk, res);
+            result = chunk.getLength();
+        }
+
+        return result;
+
+    }
+
+
+    // --------------------------------------------------- OutputFilter Methods
+
+
+    /**
+     * Some filters need additional parameters from the response. All the 
+     * necessary reading can occur in that method, as this method is called
+     * after the response header processing is complete.
+     */
+    public void setResponse(Response response) {
+        contentLength = response.getContentLength();
+        remaining = contentLength;
+    }
+
+
+    /**
+     * Set the next buffer in the filter pipeline.
+     */
+    public void setBuffer(OutputBuffer buffer) {
+        this.buffer = buffer;
+    }
+
+
+    /**
+     * End the current request. It is acceptable to write extra bytes using
+     * buffer.doWrite during the execution of this method.
+     */
+    public long end()
+        throws IOException {
+
+        if (remaining > 0)
+            return remaining;
+        return 0;
+
+    }
+
+
+    /**
+     * Make the filter ready to process the next request.
+     */
+    public void recycle() {
+        contentLength = -1;
+        remaining = 0;
+    }
+
+
+    /**
+     * Return the name of the associated encoding; Here, the value is 
+     * "identity".
+     */
+    public ByteChunk getEncodingName() {
+        return ENCODING;
+    }
+
+
+}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/filters/VoidInputFilter.java b/connectors/http11/src/java/org/apache/coyote/http11/filters/VoidInputFilter.java
new file mode 100644
index 0000000..e8551b0
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/VoidInputFilter.java
@@ -0,0 +1,118 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.http11.filters;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+
+import org.apache.coyote.InputBuffer;
+import org.apache.coyote.Request;
+import org.apache.coyote.http11.InputFilter;
+
+/**
+ * Void input filter, which returns -1 when attempting a read. Used with a GET,
+ * HEAD, or a similar request.
+ * 
+ * @author Remy Maucherat
+ */
+public class VoidInputFilter implements InputFilter {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    protected static final String ENCODING_NAME = "void";
+    protected static final ByteChunk ENCODING = new ByteChunk();
+
+
+    // ----------------------------------------------------- Static Initializer
+
+
+    static {
+        ENCODING.setBytes(ENCODING_NAME.getBytes(), 0, ENCODING_NAME.length());
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // --------------------------------------------------- OutputBuffer Methods
+
+
+    /**
+     * Write some bytes.
+     * 
+     * @return number of bytes written by the filter
+     */
+    public int doRead(ByteChunk chunk, Request req)
+        throws IOException {
+
+        return -1;
+
+    }
+
+
+    // --------------------------------------------------- OutputFilter Methods
+
+
+    /**
+     * Set the associated reauest.
+     */
+    public void setRequest(Request request) {
+    }
+
+
+    /**
+     * Set the next buffer in the filter pipeline.
+     */
+    public void setBuffer(InputBuffer buffer) {
+    }
+
+
+    /**
+     * Make the filter ready to process the next request.
+     */
+    public void recycle() {
+    }
+
+
+    /**
+     * Return the name of the associated encoding; Here, the value is 
+     * "void".
+     */
+    public ByteChunk getEncodingName() {
+        return ENCODING;
+    }
+
+
+    /**
+     * End the current request. It is acceptable to write extra bytes using
+     * buffer.doWrite during the execution of this method.
+     * 
+     * @return Should return 0 unless the filter does some content length 
+     * delimitation, in which case the number is the amount of extra bytes or
+     * missing bytes, which would indicate an error. 
+     * Note: It is recommended that extra bytes be swallowed by the filter.
+     */
+    public long end()
+        throws IOException {
+        return 0;
+    }
+
+
+}
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/filters/VoidOutputFilter.java b/connectors/http11/src/java/org/apache/coyote/http11/filters/VoidOutputFilter.java
new file mode 100644
index 0000000..efe6faf
--- /dev/null
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/VoidOutputFilter.java
@@ -0,0 +1,127 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.coyote.http11.filters;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Response;
+import org.apache.coyote.http11.OutputFilter;
+
+/**
+ * Void output filter, which silently swallows bytes written. Used with a 204
+ * status (no content) or a HEAD request.
+ * 
+ * @author Remy Maucherat
+ */
+public class VoidOutputFilter implements OutputFilter {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    protected static final String ENCODING_NAME = "void";
+    protected static final ByteChunk ENCODING = new ByteChunk();
+
+
+    // ----------------------------------------------------- Static Initializer
+
+
+    static {
+        ENCODING.setBytes(ENCODING_NAME.getBytes(), 0, ENCODING_NAME.length());
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Next buffer in the pipeline.
+     */
+    protected OutputBuffer buffer;
+
+
+    // --------------------------------------------------- OutputBuffer Methods
+
+
+    /**
+     * Write some bytes.
+     * 
+     * @return number of bytes written by the filter
+     */
+    public int doWrite(ByteChunk chunk, Response res)
+        throws IOException {
+
+        return chunk.getLength();
+
+    }
+
+
+    // --------------------------------------------------- OutputFilter Methods
+
+
+    /**
+     * Some filters need additional parameters from the response. All the 
+     * necessary reading can occur in that method, as this method is called
+     * after the response header processing is complete.
+     */
+    public void setResponse(Response response) {
+    }
+
+
+    /**
+     * Set the next buffer in the filter pipeline.
+     */
+    public void setBuffer(OutputBuffer buffer) {
+        this.buffer = buffer;
+    }
+
+
+    /**
+     * Make the filter ready to process the next request.
+     */
+    public void recycle() {
+    }
+
+
+    /**
+     * Return the name of the associated encoding; Here, the value is 
+     * "identity".
+     */
+    public ByteChunk getEncodingName() {
+        return ENCODING;
+    }
+
+
+    /**
+     * End the current request. It is acceptable to write extra bytes using
+     * buffer.doWrite during the execution of this method.
+     * 
+     * @return Should return 0 unless the filter does some content length 
+     * delimitation, in which case the number is the amount of extra bytes or
+     * missing bytes, which would indicate an error. 
+     * Note: It is recommended that extra bytes be swallowed by the filter.
+     */
+    public long end()
+        throws IOException {
+        return 0;
+    }
+
+
+}
diff --git a/connectors/http11/src/test/java/org/apache/coyote/http11/FileTester.java b/connectors/http11/src/test/java/org/apache/coyote/http11/FileTester.java
new file mode 100644
index 0000000..a02409c
--- /dev/null
+++ b/connectors/http11/src/test/java/org/apache/coyote/http11/FileTester.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+package org.apache.coyote.http11;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.Locale;
+
+import org.apache.coyote.Adapter;
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.Processor;
+
+/**
+ * File tester.
+ * <p>
+ * This tester is initialized with an adapter (it will use the HTTP/1.1 
+ * processor), and will then accept an input file (which will contain the 
+ * input data), and an output file, to which the result of the request(s)
+ * will be written.
+ *
+ * @author Remy Maucherat
+ */
+public class FileTester {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new file tester using the two files specified. The contents
+     * of the output file will be overwritten when the test is run.
+     * 
+     * @param adapter Coyote adapter to use for this test
+     * @param processor Coyote processor to use for this test
+     * @param inputFile File containing the HTTP requests in plain text
+     * @param outputFile File containing the HTTP responses
+     */
+    public FileTester(Adapter adapter, Processor processor,
+                      File inputFile, File outputFile) {
+
+        processor.setAdapter(adapter);
+
+        this.adapter = adapter;
+        this.processor = processor;
+        this.inputFile = inputFile;
+        this.outputFile = outputFile;
+
+    }
+
+
+    // --------------------------------------------------------- Static Methods
+
+
+    /**
+     * Utility main method, which will use the HTTP/1.1 processor with the 
+     * test adapter.
+     *
+     * @param args Command line arguments to be processed
+     */
+    public static void main(String args[])
+        throws Exception {
+
+        if (args.length < 2) {
+            System.out.println("Incorrect number of arguments");
+            return;
+        }
+
+        // The first argument is the input file
+        File inputFile = new File(args[0]);
+
+        // The second argument is the output file
+        File outputFile = new File(args[1]);
+
+        Adapter testAdapter = new RandomAdapter();
+        Http11Processor http11Processor = new Http11Processor();
+        http11Processor.setSocket(new Socket("127.0.0.1", 8080));
+        http11Processor.action(ActionCode.ACTION_START, null);
+
+        FileTester tester = new FileTester(testAdapter, http11Processor,
+                                           inputFile, outputFile);
+        tester.test();
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * File containing the input data.
+     */
+    protected File inputFile;
+
+
+    /**
+     * File containing the output data.
+     */
+    protected File outputFile;
+
+
+    /**
+     * Coyote adapter to use.
+     */
+    protected Adapter adapter;
+
+
+    /**
+     * Coyote processor to use.
+     */
+    protected Processor processor;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the test.
+     */
+    public void test()
+        throws Exception {
+
+        InputStream is = new FileInputStream(inputFile);
+        OutputStream os = new FileOutputStream(outputFile);
+
+        processor.process(is, os);
+
+    }
+
+
+}
diff --git a/connectors/http11/src/test/java/org/apache/coyote/http11/RandomAdapter.java b/connectors/http11/src/test/java/org/apache/coyote/http11/RandomAdapter.java
new file mode 100644
index 0000000..d35f7be
--- /dev/null
+++ b/connectors/http11/src/test/java/org/apache/coyote/http11/RandomAdapter.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+package org.apache.coyote.http11;
+
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.http.MimeHeaders;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.Adapter;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+
+/**
+ * Adapter which will return random responses using pipelining, which means it
+ * will never try to generate bogus responses.
+ *
+ * @author Remy Maucherat
+ */
+public class RandomAdapter
+    implements Adapter {
+
+
+    public static final String CRLF = "\r\n";
+    public static final byte[] b = "0123456789\r\n".getBytes();
+    public static final ByteChunk bc = new ByteChunk();
+
+
+    /** 
+     * Service method, which dumps the request to the console.
+     */
+    public void service(Request req, Response res)
+	throws Exception {
+
+        double rand = Math.random();
+        int n = (int) Math.round(10 * rand);
+
+        // Temp variables
+        byte[] buf = null;
+        int nRead = 0;
+        StringBuffer sbuf = new StringBuffer();
+        MimeHeaders headers = req.getMimeHeaders();
+        int size = headers.size();
+
+        switch (n) {
+
+        case 0:
+
+            // 0) Do nothing
+            System.out.println("Response 0");
+            break;
+
+        case 1:
+
+            // 1) Set content length, and write the appropriate content
+            System.out.println("Response 1");
+            res.setContentLength(b.length);
+            bc.setBytes(b, 0, b.length);
+            res.doWrite(bc);
+            break;
+
+        case 2:
+
+            // 2) Read the request data, and print out the number of bytes
+            // read
+            System.out.println("Response 2");
+            while (nRead >= 0) {
+                nRead = req.doRead(bc);
+                buf = ("Read " + nRead + " bytes\r\n").getBytes();
+                bc.setBytes(buf, 0, buf.length);
+                res.doWrite(bc);
+            }
+            break;
+
+        case 3:
+
+            // 3) Return 204 (no content), while reading once on input
+            System.out.println("Response 3");
+            res.setStatus(204);
+            nRead = req.doRead(bc);
+            res.setHeader("Info", "Read " + nRead + " bytes");
+            break;
+
+        case 4:
+
+            // 4) Do a request dump
+            System.out.println("Response 4");
+            sbuf.append("Request dump:");
+            sbuf.append(CRLF);
+            sbuf.append(req.method());
+            sbuf.append(" ");
+            sbuf.append(req.unparsedURI());
+            sbuf.append(" ");
+            sbuf.append(req.protocol());
+            sbuf.append(CRLF);
+            for (int i = 0; i < size; i++) {
+                sbuf.append(headers.getName(i) + ": ");
+                sbuf.append(headers.getValue(i).toString());
+                sbuf.append(CRLF);
+            }
+            sbuf.append("Request body:");
+            sbuf.append(CRLF);
+            res.action(ActionCode.ACTION_ACK, null);
+            ByteChunk bc2 = new ByteChunk();
+            byte[] b2 = sbuf.toString().getBytes();
+            bc2.setBytes(b2, 0, b2.length);
+            res.doWrite(bc2);
+            while (nRead >= 0) {
+                nRead = req.doRead(bc2);
+                if (nRead > 0)
+                    res.doWrite(bc2);
+            }
+            break;
+
+        default:
+
+            // Response not implemented yet
+            System.out.println("Response " + n + " is not implemented yet");
+
+        }
+
+/*
+        StringBuffer buf = new StringBuffer();
+        buf.append("Request dump:");
+        buf.append(CRLF);
+        buf.append(req.method());
+        buf.append(" ");
+        buf.append(req.unparsedURI());
+        buf.append(" ");
+        buf.append(req.protocol());
+        buf.append(CRLF);
+        
+        MimeHeaders headers = req.getMimeHeaders();
+        int size = headers.size();
+        for (int i = 0; i < size; i++) {
+            buf.append(headers.getName(i) + ": ");
+            buf.append(headers.getValue(i).toString());
+            buf.append(CRLF);
+        }
+
+        buf.append("Request body:");
+        buf.append(CRLF);
+
+        res.action(ActionCode.ACTION_ACK, null);
+
+        ByteChunk bc = new ByteChunk();
+        byte[] b = buf.toString().getBytes();
+        bc.setBytes(b, 0, b.length);
+        res.doWrite(bc);
+
+        int nRead = 0;
+
+        while (nRead >= 0) {
+            nRead = req.doRead(bc);
+            if (nRead > 0)
+                res.doWrite(bc);
+        }
+*/
+
+    }
+
+
+}
diff --git a/connectors/http11/src/test/java/org/apache/coyote/http11/TestAdapter.java b/connectors/http11/src/test/java/org/apache/coyote/http11/TestAdapter.java
new file mode 100644
index 0000000..5b6ecdd
--- /dev/null
+++ b/connectors/http11/src/test/java/org/apache/coyote/http11/TestAdapter.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+package org.apache.coyote.http11;
+
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.http.MimeHeaders;
+
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.Adapter;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+
+/**
+ * Adapter which will generate content.
+ *
+ * @author Remy Maucherat
+ */
+public class TestAdapter
+    implements Adapter {
+
+
+    public static final String CRLF = "\r\n";
+
+
+    /** 
+     * Service method, which dumps the request to the console.
+     */
+    public void service(Request req, Response res)
+	throws Exception {
+        
+        StringBuffer buf = new StringBuffer();
+        buf.append("Request dump:");
+        buf.append(CRLF);
+        buf.append(req.method());
+        buf.append(" ");
+        buf.append(req.unparsedURI());
+        buf.append(" ");
+        buf.append(req.protocol());
+        buf.append(CRLF);
+        
+        MimeHeaders headers = req.getMimeHeaders();
+        int size = headers.size();
+        for (int i = 0; i < size; i++) {
+            buf.append(headers.getName(i) + ": ");
+            buf.append(headers.getValue(i).toString());
+            buf.append(CRLF);
+        }
+
+        buf.append("Request body:");
+        buf.append(CRLF);
+
+        res.action(ActionCode.ACTION_ACK, null);
+
+        ByteChunk bc = new ByteChunk();
+        byte[] b = buf.toString().getBytes();
+        bc.setBytes(b, 0, b.length);
+        res.doWrite(bc);
+
+        int nRead = 0;
+
+        while (nRead >= 0) {
+            nRead = req.doRead(bc);
+            if (nRead > 0)
+                res.doWrite(bc);
+        }
+
+    }
+
+
+}
diff --git a/connectors/http11/src/test/test.bat b/connectors/http11/src/test/test.bat
new file mode 100644
index 0000000..63a99ff
--- /dev/null
+++ b/connectors/http11/src/test/test.bat
@@ -0,0 +1,4 @@
+
+
+java -cp .;../../../lib/commons-logging.jar;../lib/tomcat-util.jar;../lib/tomcat-coyote.jar;../lib/tomcat-http11.jar org.apache.coyote.http11.FileTester tests/test1.txt tests/test1.out
+
diff --git a/connectors/http11/src/test/tests/test1.txt b/connectors/http11/src/test/tests/test1.txt
new file mode 100644
index 0000000..25e26a2
--- /dev/null
+++ b/connectors/http11/src/test/tests/test1.txt
@@ -0,0 +1,198 @@
+GET /foo HTTP/1.1
+Header1: simple header
+Host:     random_host     
+Header2:     this is a sample multiline header formatted 
+     using lots of extra spaces   
+Header3:    another     
+         multiline
+   header
+      which   
+    spans
+        six lines  
+Content-Length: 204
+
+01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+
+GET /bar HTTP/1.1
+Host:foobar
+Transfer-Encoding: chunked
+
+cc
+00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+
+
+cc
+11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
+
+
+cc
+22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
+
+
+cc
+33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
+
+
+cc
+44444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444
+
+
+cc
+55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555
+
+
+cc
+66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666
+
+
+cc
+77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777
+
+
+cc
+88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
+
+
+cc
+99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
+
+
+cc
+00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+
+
+cc
+11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
+
+
+cc
+22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
+
+
+cc
+33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
+
+
+cc
+44444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444
+
+
+cc
+55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555
+
+
+cc
+66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666
+
+
+cc
+77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777
+
+
+cc
+88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
+
+
+cc
+99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
+
+
+cc
+00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+
+
+cc
+11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
+
+
+cc
+22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
+
+
+cc
+33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
+
+
+cc
+44444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444
+
+
+cc
+55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555
+
+
+cc
+66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666
+
+
+cc
+77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777
+
+
+cc
+88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
+
+
+cc
+99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
+
+
+0
+
+GET /bar HTTP/1.1
+Host:foobar
+Transfer-Encoding: chunked
+
+1774
+000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999990000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
+
+
+cc
+00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+
+
+1774
+000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999990000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
+
+
+1774
+000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999990000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333344444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666677777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444445555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555566666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777778888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888899999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
+
+
+cc
+11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
+
+
+0
+
+GET /foo2 HTTP/1.1
+Host: localhost
+Header4:     this is a sample multiline header formatted 
+     using lots of extra spaces   
+Header5:    another     
+         multiline
+   header
+      which   
+    spans
+        six lines  
+Content-Length: 12
+
+0123456789
+HEAD /bar2 HTTP/1.1
+Header6:    another     
+         multiline
+   header
+      which   
+    spans
+        six lines  
+Host:foobar
+
+GET /bar2 HTTP/1.1
+Header6:    another     
+         multiline
+   header
+      which   
+    spans
+        six lines  
+Host:foobar
+
diff --git a/connectors/jk/.cvsignore b/connectors/jk/.cvsignore
new file mode 100644
index 0000000..601b0d6
--- /dev/null
+++ b/connectors/jk/.cvsignore
@@ -0,0 +1,3 @@
+build
+build.properties
+
diff --git a/connectors/jk/BUILD.txt b/connectors/jk/BUILD.txt
new file mode 100644
index 0000000..39eada9
--- /dev/null
+++ b/connectors/jk/BUILD.txt
@@ -0,0 +1,11 @@
+This is a source release of the mod_jk 1.2 web server connector for
+Tomcat. Only the web server connector source is included.  The Tomcat
+side of the connector is available with the normal Tomcat distribution.
+
+Documentation for how to build mod_jk 1.2 from source and configure
+it for your webserver is located in the jk/docs directory in this
+source distribution.
+
+The source is located in the jk/native directory and there are
+additional text files their on how to build mod_jk for different
+platforms.
diff --git a/connectors/jk/HOWTO-RELEASE b/connectors/jk/HOWTO-RELEASE
new file mode 100644
index 0000000..c3fc88d
--- /dev/null
+++ b/connectors/jk/HOWTO-RELEASE
@@ -0,0 +1,232 @@
+		How to do a mod_jk 1.2 release
+
+If you haven't already, add your public PGP key to
+jakarta-tomcat-connectors/KEYS.
+
+Check out a clean copy of jakarta-tomcat-connectors from CVS to
+make sure you don't have any lingering configure or build files.
+This will make sure that the source distribution created is clean.
+
+Update version numbers as needed
+--------------------------------
+
+Do a find for all the docs which include the previous version string
+and replace it with the new version.  These are the docs I found which
+had to be updated:
+
+xdocs/jk/aphowto.xml
+xdocs/jk/quickhowto.xml
+xdocs/jk/workershowto.xml
+
+Update the version in jk/native/configure.in.
+
+Update the version in jk/native/common/jk_version.h, here is
+a cvs diff that shows what I changed:
+
+Index: native/common/jk_version.h
+===================================================================
+RCS file: /home/cvs/jakarta-tomcat-connectors/jk/native/common/jk_version.h,v
+retrieving revision 1.9
+diff -c -w -r1.9 jk_version.h
+*** native/common/jk_version.h  17 Dec 2002 10:36:16 -0000      1.9
+--- native/common/jk_version.h  26 Apr 2003 18:05:27 -0000
+***************
+*** 67,80 ****
+  /************** START OF AREA TO MODIFY BEFORE RELEASING *************/
+  #define JK_VERMAJOR     1
+  #define JK_VERMINOR     2
+! #define JK_VERFIX       2
+  #define JK_VERSTRING    "1.2.5"
+  
+  /* Beta number */
+  #define JK_VERBETA      0
+  #define JK_BETASTRING   "1"
+  /* set JK_VERISRELEASE to 1 when release (do not forget to commit!) */
+! #define JK_VERISRELEASE 0
+  /************** END OF AREA TO MODIFY BEFORE RELEASING *************/
+  
+  #define PACKAGE "mod_jk/"
+--- 67,80 ----
+  /************** START OF AREA TO MODIFY BEFORE RELEASING *************/
+  #define JK_VERMAJOR     1
+  #define JK_VERMINOR     2
+! #define JK_VERFIX       3
+  #define JK_VERSTRING    "1.2.5"
+  
+  /* Beta number */
+  #define JK_VERBETA      0
+  #define JK_BETASTRING   "1"
+  /* set JK_VERISRELEASE to 1 when release (do not forget to commit!) */
+! #define JK_VERISRELEASE 1
+  /************** END OF AREA TO MODIFY BEFORE RELEASING *************/
+  
+  #define PACKAGE "mod_jk/"
+
+After updating revision numbers, commit your changes to CVS.
+
+Tag and branch jk in CVS
+------------------------
+
+Change directory (cd) to jakarta-tomcat-connectors/jk
+Use the pattern below for branching and tagging the jk directory.
+
+cvs tag -b JK_{MAJOR_REVISION}_{MINOR_REVISION}_{RELEASE}
+
+Here is an example for mod_jk 1.2.5
+
+cvs tag -b JK_1_2_5
+
+Build the mod_jk 1.2 documentation
+----------------------------------
+
+cd jakarta-tomcat-connectors/jk
+ant docs
+
+Create the new source distribution
+----------------------------------
+
+Create the directory
+jakarta-tomcat-connectors-jk-{MAJOR_REVISION}-{MINOR_REVISION}-{RELEASE}-src
+
+For this example mkdir jakarta-tomcat-connectors-jk-1.2.5-src
+
+Copy the files KEYS and LICENSE from jakarta-tomcat-conectors to
+the source distribution directory.
+
+cp jakarta-tomcat-connectors/KEYS jakarta-tomcat-connectors-jk-1.2.5-src
+cp jakarta-tomcat-connectors/LICENSE jakarta-tomcat-connectors-jk-1.2.5-src
+
+Copy the directory jakarta-tomcat-connectors/scandoc to the source
+distribution directory.
+
+cp -pr jakarta-tomcat-connectors/scandoc jakarta-tomcat-connectors-jk-1.2.5-src
+
+Copy the directory jakarta-tomcat-connectors/common to the source
+distribution directory.
+
+cp -pr jakarta-tomcat-connectors/common jakarta-tomcat-connectors-jk-1.2.5-src
+
+Make the jk directory in the source distribution.
+
+mkdir jakarta-tomcat-connectors-jk-1.2.5-src/jk
+
+Copy the file BUILD.txt from jakarta-tomcat-conectors/jk to 
+the source distribution jk directory.
+
+cp -p jakarta-tomcat-connectors/jk/BUILD.txt jakarta-tomcat-connectors-jk-1.2.5-src/README.txt
+
+Copy the directory jakarta-tomcat-connectors/build/docs to the source
+distribution directory.
+
+cp -pr jakarta-tomcat-connectors/jk/build/docs jakarta-tomcat-connectors-jk-1.2.5-src/jk
+
+Copy the directory jakarta-tomcat-connectors/tools to the source
+distribution directory.
+
+cp -pr jakarta-tomcat-connectors/jk/tools jakarta-tomcat-connectors-jk-1.2.5-src/jk
+
+Make the jk conf directory in the source distribution.
+
+mkdir jakarta-tomcat-connectors-jk-1.2.5-src/jk/conf
+
+Copy the worker.properties file to the jk/conf directory.
+
+cp -p jakarta-tomcat-connectors/jk/conf/worker.properties jakarta-tomcat-connectors-jk-1.2.5-src/jk/conf
+
+Copy the directory jakarta-tomcat-connectors/support to the source
+distribution directory.
+
+cp -pr jakarta-tomcat-connectors/jk/support jakarta-tomcat-connectors-jk-1.2.5-src/jk
+
+Copy the directory jakarta-tomcat-connectors/native to the source
+distribution directory.
+
+cp -pr jakarta-tomcat-connectors/jk/native jakarta-tomcat-connectors-jk-1.2.5-src/jk
+
+Remove all the CVS directories from the new source distribution.
+
+find jakarta-tomcat-connectors-jk-1.2.5-src -type d -name CVS | xargs rm -rf
+
+cd to jakarta-tomcat-connectors-jk-1.2.5-src/jk/native and run buildconf.sh
+to create the configure script.
+
+Create a tar gzip'd archive
+
+tar zcf jakarta-tomcat-connectors-jk-1.2.5-src.tar.gz jakarta-tomcat-connectors-jk-1.2.5-src
+
+Create a zip archive
+
+zip -r jakarta-tomcat-connectors-jk-1.2.5-src.zip jakarta-tomcat-connectors-jk-1.2.5-src
+
+Sign the release using PGP. Here is an example using gpg:
+
+gpg -abs -o jakarta-tomcat-connectors-jk-1.2.5-src.tar.gz.asc jakarta-tomcat-connectors-jk-1.2.5-src.tar.gz
+
+Upload source distribution and documentation to www.apache.org
+-------------------------------------------------------------------
+First update the KEYS on the server if you have added a new pgp key.
+
+scp jakarta-tomcat-connectors/KEYS to the
+/www/www.apache.org/dist/jakarta/tomcat-connectors
+directory on the www.apache.org server.
+
+scp jakarta-tomcat-connectors-jk-1.2.5-src.tar.gz* to 
+/www/www.apache.org/dist/jakarta/tomcat-connectors/jk/source
+scp jakarta-tomcat-connectors-jk-1.2.5-src.zip* to   
+/www/www.apache.org/dist/jakarta/tomcat-connectors/jk/source
+
+ssh to www.apache.org and cd to the
+/www/www.apache.org/dist/jakarta/tomcat-connectors/jk directory.
+
+Remove the symlinks for current and replace them with a soft link
+to the new source distribution files.
+
+ln -s source/jakarta-tomcat-connectors-jk-1.2.5-src.tar.gz jakarta-tomcat-connectors-jk-src-current.tar.gz
+ln -s source/jakarta-tomcat-connectors-jk-1.2.5-src.tar.gz.asc jakarta-tomcat-connectors-jk-src-current.tar.gz.asc
+ln -s source/jakarta-tomcat-connectors-jk-1.2.5-src.tar.zip jakarta-tomcat-connectors-jk-src-current.zip
+ln -s source/jakarta-tomcat-connectors-jk-1.2.5-src.zip.asc jakarta-tomcat-connectors-jk-src-current.zip.asc
+
+Make sure the group write bit is set on all files and directories
+in the jk directory.
+
+chmod -R g+w /www/www.apache.org/dist/jakarta/tomcat-connectors/jk/
+
+Build binaries and upload distributions to www.apache.org
+--------------------------------------------------------------
+
+Build mod_jk for a specific web server and OS.  Package it as appropriate for
+the OS and sign the archive using PGP. Please include the ASF License, the
+generated docs, and the tools.  Please name the distribuiton as follows:
+
+jakarta-tomcat-connectors-jk-{version}-{os-version-cpu}-{web server-version}.(tar.gz|zip)
+
+scp the binary distribution and pgp signature file to the appropriate binaries/{os} directory.
+
+Make sure the group write bit is on for all files you upload.
+
+Update source for next version
+------------------------------
+
+Reset JK_VERISRELEASE to 0 and update JK_VERSTRING, JK_VERMAJOR,
+JK_VERMINOR, and JK_VERFIX as needed.  Commit your changes to CVS.
+
+Remove old release distributions from www.apache.org
+----------------------------------------------------
+
+Verify that the old versions of the source and binary distributions are
+available at /www/archive.apache.org/dist/jakarta/tomcat-connector/jk .
+Copy old source distributions and binaries as needed, then remove the
+old source and binary distributions.
+
+Announcements
+-------------
+
+The release distribution directories are mirrored so that the
+releases can be downloaded from multiple sites.  Please wait
+24 hours before sending out the announcement so that the mirrors
+get a chance to get the new release distributions.
+
+Send an email announcement to announcements@jakarta.apache.org,
+tomcat-user@jakarta.apache.org, tomcat-dev@jakarta.apache.org,
+and announce@apache.org.
+
diff --git a/connectors/jk/README.txt b/connectors/jk/README.txt
new file mode 100644
index 0000000..cf6724f
--- /dev/null
+++ b/connectors/jk/README.txt
@@ -0,0 +1,68 @@
+This directory contains both the native and java-side code for
+ajp connectors.
+
+Building
+========
+
+* Install tomcat(3.3, 4.0, 4.1). Install Apache(1.3, 2.0). Any
+combination is fine, but j-t-c developers should have them all.
+
+* Copy build.properties.sample to build.properties
+
+* Edit build.properties to taste. In particular it's important to set
+the paths to tomcat and apache.
+
+* Run "ant". It'll build modules for all the detected containers.
+
+* If your system have a current working version of libtool, run "ant native". 
+  This will build the native connectors for the detected servers ( both jk
+  and jk2).
+
+  Alternatively, you can build using configure/make/dsp/apxs.
+
+* Run "ant install". This will copy the files in the right location
+  in your server/container. Optional: on unix systems you can create
+  symlinks ( XXX script will be provided ) and avoid copying. 
+
+Setting tomcat 3.3
+==================
+
+Restart tomcat. 
+
+Setting tomcat 4.0
+==================
+
+To use the ajp13 connector for tomcat 4:
+
+XXX Can we automate this ?
+
+ 1) add the following to server.xml:
+
+     <Connector className="org.apache.ajp.tomcat4.Ajp13Connector"
+               port="8009" acceptCount="10" debug="0"/>
+
+Setting tomcat 4.1
+==================
+
+Restart tomcat. 
+
+( XXX this is not completely implemented. For now we'll use the same 
+mechanism as in 4.0 - i.e. add <Connector> in server.xml.  )
+
+Configuring JK1
+===============
+
+
+
+Configuring JK2
+===============
+
+
+Building the tests
+==================
+( probably not working - the best test is to run watchdog/tester/3.3 test webapp )
+( XXX is it ok to remove this ? )
+
+* Download junit from http://www.junit.org (version 3.7 or greater is required).
+
+* "ant test"
\ No newline at end of file
diff --git a/connectors/jk/build.properties.autoconf b/connectors/jk/build.properties.autoconf
new file mode 100644
index 0000000..6790eaf
--- /dev/null
+++ b/connectors/jk/build.properties.autoconf
@@ -0,0 +1,66 @@
+#
+# build.properties for jk2/ajp connector.
+# to be processed by configure
+#
+
+# Directory where tomcat5 is installed
+tomcat5.home=@TOMCAT5_HOME@ 
+
+# Directory where catalina is installed. It can 
+# be either 4.0 or 4.1
+tomcat40.home=@TOMCAT40_HOME@
+
+# If you want to build/install on  both 4.0 
+# and 4.1, set this to point to 4.0 and 'catalina.home'
+# to point to 4.0
+# ( most people need only the first, but developers should
+# have both )
+tomcat41.home=@TOMCAT41_HOME@
+
+# Directory where tomcat3.3 is installed
+tomcat33.home=@TOMCAT33_HOME@ 
+
+# Location of Apache2, Apache1.3, IIS and Netscape (iPlanet)
+apache2.home=@APACHE2_HOME@
+apache13.home=@APACHE_HOME@
+iis.home=@IIS_HOME@
+iplanet.home=@IPLANET_HOME@
+
+
+# APR location - by default the version included in Apache2 is used.
+# Don't edit unless you install 'standalone' apr.
+apr.home=@APR_HOME@
+
+apr.include=@APR_INCDIR@
+apr-util.include=@APR_UTIL_INCL@
+
+apr.lib=@APR_LIBDIR@
+apr-util.lib=@APR_UTIL_LIB@
+
+
+# Location of Apache2, Apache1.3 apxs build tool
+apxs=@APXS@
+apxs2=@APXS2@
+
+
+# Location of Apache2, Apache1.3, IIS and Netscape (iPlanet) 
+# includes directory
+apache2.include=@APACHE2_INCDIR@
+apache13.include=@APACHE_INCDIR@
+iis.include=@IIS_INCDIR@
+iplanet.include=@IPLANET_INCDIR@
+
+
+# Location of Apache2, Apache1.3, IIS and Netscape (iPlanet) 
+# lib directory
+apache2.lib=@APACHE2_LIBDIR@
+apache13.lib=@APACHE_LIBDIR@
+iis.lib=@IIS_LIBDIR@
+iplanet.lib=@IPLANET_LIBDIR@
+
+
+# Compile-time options for native code
+so.debug=true
+so.optimize=false
+so.profile=false
+
diff --git a/connectors/jk/build.properties.sample b/connectors/jk/build.properties.sample
new file mode 100644
index 0000000..b215b45
--- /dev/null
+++ b/connectors/jk/build.properties.sample
@@ -0,0 +1,62 @@
+#
+# sample build.properties for ajp connector.
+# edit to taste...
+#
+
+# Directory where tomcat5 is installed
+tomcat5.home= ../../jakarta-tomcat-5/build
+
+# Directory where catalina is installed. It can 
+# be either 4.0 or 4.1
+tomcat40.home=../../jakarta-tomcat-4.0/build
+
+# If you want to build/install on  both 4.0 
+# and 4.1, set this to point to 4.0 and 'catalina.home'
+# to point to 4.0
+# ( most people need only the first, but developers should
+# have both )
+tomcat41.home=../../jakarta-tomcat-4.1/build
+
+# Directory where tomcat3.3 is installed
+tomcat33.home= ../../jakarta-tomcat/build/tomcat
+
+# Location of Apache2, Apache1.3, Netscape, IIS
+apache2.home=/opt/apache2
+apache13.home=/opt/apache13
+iplanet.home=/opt/iplanet6
+aolserver.home=/opt/aolserver-4.0
+# iplanet.home=d:/tools/sdk/netscape
+# iis.home=e:/
+
+# APR location - by default the version included in Apache2 is used.
+# Don't edit unless you install 'standalone' apr.
+apr.home=${apache2.home}
+
+apr.include=${apr.home}/include
+apr-util.include=${apr.home}/include
+
+apr.lib=${apr.home}/lib
+apr-util.lib=${apr.home}/lib
+apache2.lib=${apache2.home}/lib
+
+
+# Compile-time options for native code
+so.debug=true
+so.optimize=false
+so.profile=false
+
+# tools for other directories
+# Metrowerks and Novell ndk
+#mw.home=d:/tools/mw/6.0
+#novellndk.home=d:/tools/novell/ndk/nwsdk
+#novelllibc.home=d:/tools/novell/ndk/libc
+#build.compiler.base=${mw.home}
+#build.compiler.cc=${mw.home}/bin/mwccnlm
+#build.compiler.ld=${mw.home}/bin/mwldnlm
+#netscape.home=${iplanet.home}
+#option_no_reuse_worker=true
+
+# MSVC
+#mssdk.home=c:/Program Files/Microsoft Visual Studio/VC98
+
+
diff --git a/connectors/jk/build.xml b/connectors/jk/build.xml
new file mode 100644
index 0000000..d36212f
--- /dev/null
+++ b/connectors/jk/build.xml
@@ -0,0 +1,554 @@
+<project name="jk" default="build-main" basedir=".">
+  
+    <!-- We'll build jk for 3.3 or 4.0 ( depending on what you have installed ).
+    You need to set tomcat5.home, tomcat40.home, and/or tomcat33.home in
+    build.properties ( either the path to 'official' distribution or the
+    development dirs )
+    -->
+  
+    <!-- ===================== Initialize Property Values ================ -->
+    <property file="build.properties"/>
+    <property file="../build.properties"/>
+    <property file="../build.properties.default"/>
+    <property file="${user.home}/build.properties"/>
+    <property file="${user.home}/.build.properties"/>
+
+    <property name="jk.build" location="${basedir}/build"/>
+    <property name="build.docs" location="${basedir}/build/docs"/>
+    <property name="source.docs" location="./xdocs"/>
+    <property name="gen.dev.doc" value="false"/>
+
+    <!-- Compile options -->
+    <property name="optimize" value="off" />
+    <property name="compile.debug" value="true" />
+    <property name="compile.deprecation" value="false" />
+
+    <!-- default locations, overrident by properties -->
+    <property name="base.path" location="/usr/share/java"/>
+
+    <property name="tomcat33.home" 
+              location="../../jakarta-tomcat/build/tomcat/lib/common/tomcat_core.jar" />
+    <property name="tomcat40.home" 
+	      location="../../jakarta-tomcat-4.0/build" />
+    <property name="tomcat41.home" 
+	      location="../../jakarta-tomcat-4.1/build" />
+    <property name="tomcat5.home" 
+	      location="../../jakarta-tomcat-catalina/build" />
+    <property name="coyote.home" 
+	      location="../coyote/build" />
+    <property name="tomcat-coyote.jar" location="${coyote.home}/lib/tomcat-coyote.jar" />
+    <property name="servlet-api.jar" location="${tomcat5.home}/common/lib/servlet-api.jar" />
+    <property name="tomcat-util.jar" location="../util/build/lib/tomcat-util.jar" />
+
+    <property name="commons-modeler.jar" location="../../jakarta-commons/modeler/dist/commons-modeler.jar" />
+
+    <!-- Fix build via ECLIPSE which didn't export ant's jars -->
+    <path id="xml-apis.classpath">
+        <pathelement path="${jaxp.home}/jaxp.jar"/>
+        <pathelement path="${jaxp.home}/crimson.jar"/>
+        <pathelement path="${xerces2.home}/xml-apis.jar"/>
+        <pathelement path="${xml-parser-apis.jar}"/>
+    </path>
+    
+    <path id="build-main.classpath">
+        <pathelement location="../util/build/classes"/>
+        <pathelement location="${tomcat5.home}/server/lib/catalina.jar"/>
+        <pathelement location="${tomcat41.home}/server/lib/catalina.jar"/>
+        <pathelement location="${tomcat40.home}/server/lib/catalina.jar"/>
+        <pathelement location="${tomcat33.home}/lib/common/tomcat_core.jar"/>
+        <pathelement location="${tomcat33.home}/lib/common/core_util.jar"/>
+        <pathelement location="${servlet-api.jar}"/>
+        <pathelement location="${tomcat-util.jar}" />
+        <pathelement location="${commons-logging.jar}"/>
+        <pathelement location="${commons-modeler.jar}"/>
+        <pathelement location="${jmx.jar}"/>
+        <pathelement location="${tomcat33.home}/lib/container/tomcat_modules.jar"/>
+        <!-- this is needed - otherwise tomcat33 connector will not compile.
+        Just change tomcat33.home in build.properties to point
+        to nowhere, and tomcat_util will no longer be visible, nor
+        3.3 classes. -->
+        <pathelement 
+                     location="${tomcat33.home}/lib/container/tomcat_util.jar"/>
+        <pathelement location="${tomcat-coyote.jar}"/>
+    </path>
+    
+  <!-- ==================== Detection and reports ==================== -->
+
+    <target name="report"  >
+        <echo message="Tomcat33: ${tomcat33.detect} ${tomcat33.home}" />
+        <echo message="Tomcat40:  ${tomcat40.detect} ${tomcat40.home}" />
+        <echo message="Tomcat41: ${tomcat41.detect} ${tomcat41.home}" />
+        <echo message="Tomcat5:  ${tomcat5.detect} ${tomcat5.home}" />
+        <echo message="Apache13: ${apache13.detect} ${apache13.home}" />
+        <echo message="Apache2: ${apache2.detect} ${apache2.home}" />
+        <echo message="iPlanet:  ${iplanet.detect} ${iplanet.home}" />
+        <echo message="IIS:      ${iis.detect} ${iis.home}" />
+        <echo message="AOLserver: ${aolserver.detect} ${aolserver.home}" />
+        <echo message="jmx:      ${jmx.jar} ${jmx.detect} ${commons-modeler.jar} ${modeler.detect}" />
+    </target>
+
+    <target name="detect" >
+        <echo message="-------- jakarta-tomcat-connectors --------" /> 
+        <available property="tomcat33.detect" 
+                   file="${tomcat33.home}/lib/common/tomcat_core.jar" />
+        <available property="tomcat40.detect" 
+                   file="${tomcat40.home}/server/lib/catalina.jar" />
+        <available property="tomcat41.detect" 
+                   file="${tomcat41.home}/server/webapps" />
+        <condition property="tomcat5.detect">
+          <and>
+            <available
+              classname="javax.servlet.ServletRequestEvent"
+              classpath="${tomcat5.home}/common/lib/servlet-api.jar"
+            />
+          </and>
+        </condition>
+        <available property="apache13.detect" 
+                   file="${apache13.home}" />
+        <available property="apache2.detect" 
+                   file="${apache2.home}" />
+        <available property="iis.detect" 
+                   file="${iis.home}" />
+        <available property="iplanet.detect" 
+                   file="${iplanet.home}" />
+        <available property="aolserver.detect" 
+                   file="${aolserver.home}" />
+        <available property="jmx.detect" 
+                   file="${jmx.jar}" />
+        <available property="jdk14.detect" 
+                   classname="java.nio.MappedByteBuffer" />
+        <available property="modeler.detect" 
+                   file="${commons-modeler.jar}" />
+        <!-- Check if we can find the XSLTProcessor class in the classpath -->
+        <available
+                   property="avail.xalan"
+                   classname="org.apache.xalan.xslt.Process">
+                   <!--
+                   <classpath refid="classpath"/>
+                    -->
+        </available>
+    </target>
+
+    <target name="prepare" depends="detect" >
+        <mkdir dir="${jk.build}"/>
+        <mkdir dir="${jk.build}"/>
+        <mkdir dir="${jk.build}/conf"/>
+	<mkdir dir="${jk.build}/classes"/>
+        <mkdir dir="${jk.build}/classes/META-INF" />
+	<mkdir dir="${jk.build}/lib"/>
+	<copy todir="${jk.build}/conf" >
+	    <fileset dir="conf" includes="*" />
+	</copy>
+
+        <!-- util and coyote must be build first -->
+        <copy  tofile="${jk.build}/lib/tomcat-coyote.jar"
+              file="${tomcat-coyote.jar}" />
+
+        <!-- Fix build via ECLIPSE which didn't export ant's jars -->
+        <path id="xml-apis.classpath">
+          <pathelement path="${jaxp.home}/jaxp.jar"/>
+          <pathelement path="${jaxp.home}/crimson.jar"/>
+          <pathelement path="${xerces2.home}/xml-apis.jar"/>
+          <pathelement path="${xml-parser-apis.jar}"/>
+        </path>
+
+    </target>
+     
+    <target name="build-main" 
+            depends="prepare,report,jkjava" />
+
+    <!-- build all the stuff -->
+    <target name="all" 
+            depends="prepare,report,coyote,jkjava,jkant" />
+
+    <!-- Build only jk, assume coyote and utils are built -->
+    <target name="build-jk" 
+            depends="prepare,report,jkjava" />
+
+    <!-- ==================== Building ==================== -->
+    
+    <target name="jkjava" 
+            description="Build java side of the connector" >
+        <javac srcdir="java"
+               destdir="${jk.build}/classes"
+               deprecation="${compile.deprecation}"
+               debug="${compile.debug}"
+               optimize="${optimize}"
+               verbose="off" >
+            <exclude name="org/apache/ajp/**" if="tomcat5.detect"/>
+            <exclude name="org/apache/ajp/tomcat4/**" unless="tomcat40.detect"/>
+            <exclude name="org/apache/ajp/tomcat33/**" unless="tomcat33.detect"/>
+	    <exclude name="org/apache/jk/common/JkMX.java" unless="jmx.detect"/>
+	    <exclude name="org/apache/jk/common/ModJkMX.java" unless="jmx.detect"/>
+	    <exclude name="org/apache/jk/common/Shm14.java" unless="jdk14.detect"/>
+            <exclude name="org/apache/jk/config/*Config.java" unless="tomcat5.detect" />
+	    <classpath>
+	       <path refid="xml-apis.classpath"/>
+	       <path refid="build-main.classpath"/>
+	    </classpath>
+
+	</javac>
+
+	<!-- Copy static resource files -->
+	<copy todir="${jk.build}/classes">
+	    <fileset dir="java">
+	    	<include name="**/*.properties"/>
+	    </fileset>
+        </copy>
+
+        <property name="tomcat-jk.jar" value="${jk.build}/lib/tomcat-jk.jar" />
+        <property name="tomcat-jkconfig.jar" value="${jk.build}/lib/jkconfig.jar" />
+        <property name="tomcat-jkshm.jar" value="${jk.build}/lib/jkshm.jar" />
+        <property name="tomcat-jk2.jar" value="${jk.build}/lib/tomcat-jk2.jar" />
+        <property name="tomcat-jni.jar" value="${jk.build}/lib/tomcat-jni.jar" />
+
+	<jar jarfile="${tomcat-jk.jar}"
+             index="true"
+	     basedir="${jk.build}/classes">
+            <include name="org/apache/ajp/**" />
+        </jar>
+	
+	<jar jarfile="${tomcat-jkconfig.jar}"
+             index="true"
+	     basedir="${jk.build}/classes" 
+             manifest="conf/jkconfig.manifest">
+            <include name="org/apache/jk/config/**" />
+        </jar>
+	
+	<jar jarfile="${tomcat-jkshm.jar}"
+             index="true"
+	     basedir="${jk.build}/classes" 
+             manifest="conf/shm.manifest">
+            <include name="org/apache/ajp/common/Shm.class" />
+        </jar>
+
+        <copy todir="${jk.build}/classes" >
+          <fileset dir="java" includes="**/*.xml" />
+        </copy>
+	<jar jarfile="${tomcat-jk2.jar}"
+             index="true"
+             manifest="conf/tomcat-jk2.manifest"
+	     basedir="${jk.build}/classes" >
+            <include name="org/apache/jk/**" />
+            <exclude name="org/apache/jk/ant/**" />
+        </jar>
+	
+	<jar jarfile="${tomcat-jni.jar}"
+             index="true"
+	     basedir="${jk.build}/classes" 
+             manifest="conf/jk2.manifest" >
+            <include name="org/apache/jk/apr/**" />
+            <include name="org/apache/jk/core/**" />
+        </jar>
+	
+    </target>
+    
+    <target name="jkant" >
+        <mkdir dir="${jk.build}/classes"/>
+        <mkdir dir="${jk.build}/classes/META-INF" />
+        <mkdir dir="${jk.build}/lib"/>
+	<javac srcdir="jkant/java" 
+	       destdir="${jk.build}/classes" 
+	       debug="${compile.debug}"
+               deprecation="${compile.deprecation}"
+	       optimize="${optimize}"
+	       verbose="off" >
+	</javac>
+	<copy todir="${jk.build}/classes/META-INF" 
+              file="jkant/ant.tasks"/>
+	<jar jarfile="${jk.build}/lib/jkant.jar"
+             index="true"
+	     basedir="${jk.build}/classes" >
+            <include name="org/apache/jk/ant/**" />
+            <include name="META-INF/ant.tasks" />
+        </jar>
+    </target>
+    
+    <target name="coyote" 
+            description="Build utils" >
+        <ant dir="../util"  />
+        <ant dir="../coyote" />
+    </target>
+
+
+    <!-- ================ Experimental: Xdoclet =================== -->
+
+    <!-- Use Javadoc tags to generate auxiliary files.
+    -->
+    <target name="xdoclet" depends="prepare">
+        <path id="xdoclet.classpath">
+            <pathelement location="../lib/xdoclet.jar"/>
+            <pathelement location="../lib/log4j-core.jar"/>
+            <pathelement location="${ant.home}/lib/ant.jar"/>
+            <path refid="build-main.classpath" />
+        </path>
+        
+        
+        <taskdef name="webdoclet"
+                 classname="xdoclet.web.WebDocletTask" 
+                 classpathref="xdoclet.classpath" />
+        <taskdef name="document"
+                 classname="xdoclet.doc.DocumentDocletTask"
+                 classpathref="xdoclet.classpath" />
+        <taskdef name="jmxdoclet"
+                 classname="xdoclet.jmx.JMXDocletTask"
+                 classpathref="xdoclet.classpath" />
+            
+        <jmxdoclet sourcepath="java"
+                   destdir="${jk.build}/jmx-java"
+                   classpathref="xdoclet.classpath"
+                   force="${xdoclet.force}">
+            <fileset dir="java">
+               <include name="**/*.java" />
+            </fileset>
+
+            <!-- Create the {0}MBean interface for the MBean -->
+            <mbeaninterface mergedir="java"/>
+
+            <!-- Create the OpenJMX specific description adaptor class for the MBean -->
+            <openjmxDescription />
+
+            <!--create the jbossmx xml descriptor for the mbean-->
+            <jbossxmbean/>
+
+            <!--create the jboss xml service template for the mbean-->
+           <jbossXmlServiceTemplate/>
+        </jmxdoclet>
+
+            
+        <document sourcepath="java"
+                  destdir="${jk.build}/todo" 
+                  classpathref="xdoclet.classpath" >
+            <fileset dir="java">
+                <include name="**/*.java" />
+            </fileset>
+            <info header="Todo list"
+                  projectname="JK2"
+                  tag="todo" />
+         </document>
+
+    </target>
+
+    <!-- ================ javadocs =================== -->
+    <target name="javadoc" unless="docs-uptodate">
+        <delete dir="${jk.build}/javadoc"/>
+	<mkdir dir="${jk.build}/javadoc"/>
+	<javadoc packagenames="org.apache.ajp,org.apache.ajp.tomcat4,org.apache.ajp,org.apache.ajp.tomcat5"
+                 sourcepath="java"
+                 classpath="${tomcat5.home}/server/lib/catalina.jar:${tomcat5.home}/common/lib/servlet-api.jar:${tomcat41.home}/server/lib/catalina.jar:${tomcat41.home}/common/lib/servlet.jar:${tomcat40.home}/server/lib/catalina.jar:${tomcat40.home}/common/lib/servlet.jar:${tomcat-util.jar}"
+                 destdir="${jk.build}/javadoc"
+                 author="true"
+                 version="true"
+                 windowtitle="Jk Connector Documentation"
+                 doctitle="Jk Connector"
+                 bottom="Copyright &#169; 2001-2003 Apache Software Foundation.  All Rights Reserved."
+	/>
+    </target>
+
+    <target name="clean">
+        <delete dir="${jk.build}/classes"/>
+        <delete dir="${jk.build}/lib"/>
+        <delete dir="${jk.build}/javadoc"/>
+        <delete dir="${build.docs}"/>
+        <delete file="${source.docs}/style.css"/>
+        <delete file="${source.docs}/style.xsl"/>
+        <delete file="${source.docs}/menu.idx"/>
+    </target>
+
+    <!-- It's better to call it directly with individual tags -->
+    <target name="native" depends="jkant,detect,report" >
+	<ant  dir="native" antfile="build.xml"  />
+	<ant  dir="native2" antfile="build.xml"  />
+    </target>
+
+    <target name="clean-native">
+        <ant  dir="native" antfile="build.xml" target="clean"  />
+        <ant  dir="native2" antfile="build.xml" target="clean"  />
+    </target>
+
+    <!-- Check if we found Xalan in our classpath. We require Xalan because it has
+    some nifty functions that we use throughout the XSLT (and also because
+    we want people to eat our own food, right?)
+  -->
+  <target
+      name="docs.check"
+      depends="detect"
+      description="Fail if we don't find Xalan"
+      unless="avail.xalan">
+ 
+    <!-- Just jump out -->
+    <fail message="Cannot find the Apache Xalan XSLT processor"/>
+  </target>
+
+  <!-- builds the css and xls file from the css.in and xls.in -->
+  <target name="docs.init">
+    <property file="${source.docs}/build.properties"/>
+
+    <property name="body-bg"       value="#ffffff"/>
+    <property name="body-fg"       value="#000000"/>
+    <property name="body-link"     value="#525D76"/>
+    <property name="banner-bg"     value="#525D76"/>
+    <property name="banner-fg"     value="#ffffff"/>
+    <property name="sub-banner-bg" value="#828DA6"/>
+    <property name="sub-banner-fg" value="#ffffff"/>
+    <property name="table-th-bg"   value="#039acc"/>
+    <property name="table-td-bg"   value="#a0ddf0"/>
+    <property name="source-color"  value="#023264"/>
+
+    <antcall target="docs.color">
+      <param name="sourcefile" value="${source.docs}/style.css.in"/>
+      <param name="destfile" value="${source.docs}/style.css"/>
+    </antcall>
+    <antcall target="docs.color">
+      <param name="sourcefile" value="${source.docs}/style.xsl.in"/>
+      <param name="destfile" value="${source.docs}/style.xsl"/>
+    </antcall>
+
+    <condition property="jkmenu" value="&amp;JK;">
+      <not>
+        <isset property="nojkdoc"/>
+      </not>
+    </condition>
+   
+    <condition property="jk2menu" value="&amp;JK2;">
+      <not>
+        <isset property="nojk2doc"/>
+      </not>
+    </condition>
+   
+    <copy file="${source.docs}/menu.idx.in" tofile="${source.docs}/menu.idx"/>
+    <replace file="${source.docs}/menu.idx" token="@JK@"  value="${jkmenu}"/>
+    <replace file="${source.docs}/menu.idx" token="@JK2@"  value="${jk2menu}"/>
+    
+  </target>
+
+  <target name="docs.color">
+    <copy file="${sourcefile}" tofile="${destfile}"/>
+    <replace file="${destfile}" token="@body-bg@"       value="${body-bg}"/>
+    <replace file="${destfile}" token="@body-fg@"       value="${body-fg}"/>
+    <replace file="${destfile}" token="@body-link@"     value="${body-link}"/>
+    <replace file="${destfile}" token="@banner-bg@"     value="${banner-bg}"/>
+    <replace file="${destfile}" token="@banner-fg@"     value="${banner-fg}"/>
+    <replace file="${destfile}" token="@sub-banner-bg@" value="${sub-banner-bg}"/>
+    <replace file="${destfile}" token="@sub-banner-fg@" value="${sub-banner-fg}"/>
+    <replace file="${destfile}" token="@table-th-bg@"   value="${table-th-bg}"/>
+    <replace file="${destfile}" token="@table-td-bg@"   value="${table-td-bg}"/>
+    <replace file="${destfile}" token="@source-color@"  value="${source-color}"/>
+    <replace file="${destfile}" token="@gen-dev-doc@"   value="${gen.dev.doc}"/>
+  </target>
+
+  <!--
+    Generate documentation from the XML sources.
+  -->
+  <target name="docs"
+          depends="jkdocs,jkprinter,jk2printer" />
+  <target
+      name="jkdocs" unless="docs-uptodate"
+      depends="docs.check,docs.init"
+      description="Create Documentation">
+ 
+    <!-- Create the directory where we're going to store the docs -->
+    <mkdir dir="${build.docs}"/>
+    <mkdir dir="${build.docs}/printer"/>
+    <mkdir dir="${build.docs}/common/printer"/>
+ 
+    <!-- Add some style to our otherwise  utterly ugly XML files -->
+    <style
+        basedir="${source.docs}"
+        destdir="${build.docs}"
+        style="${source.docs}/style.xsl" >
+        
+        <include name="*/**.xml" />
+        <exclude name="*.xml" />
+        <exclude name="jk/**"  if="nojkdoc"/>
+        <exclude name="jk2/**" if="nojk2doc"/>
+        <param name="styles" expression="../"/>
+    </style>
+    
+    <style
+        basedir="${source.docs}"
+        destdir="${build.docs}"
+        style="${source.docs}/style.xsl" >
+        <include name="**.xml" />
+        <param name="images" expression="images"/>
+        <param name="styles" expression="."/>
+        <param name="homedoc" expression=""/>
+    </style>
+
+    <!-- Create the printer friendly pages -->
+    <style
+        basedir="${source.docs}"
+        destdir="${build.docs}/printer"
+        style="${source.docs}/style.xsl" >
+        
+        <include name="**.xml" />
+        <param name="images" expression="images"/>
+        <param name="styles" expression="."/>
+        <param name="homedoc" expression=""/>
+        <param name="project-menu" expression="nomenu"/>
+    </style>
+    <style
+        basedir="${source.docs}/common"
+        destdir="${build.docs}/common/printer"
+        style="${source.docs}/style.xsl" >
+        
+        <include name="**.xml" />
+        <param name="styles" expression="../"/>
+        <param name="project-menu" expression="nomenu"/>
+    </style>
+ 
+    <!-- Copy all relevant (non processed) files from the sources -->
+    <copy
+        todir="${build.docs}" >
+      <fileset dir="${source.docs}">
+        <exclude name="jk/**"  if="nojkdoc"/>
+        <exclude name="jk2/**" if="nojk2doc"/>
+        <exclude name="**/*.xml"/>
+        <exclude name="**/*.css.in"/>
+        <exclude name="**/*.xsl.in"/>
+        <exclude name="**/*.samples"/>
+        <exclude name="**/*.xsl"/>
+        <exclude name="**/*.idx"/>
+        <exclude name="**/*.idx.in"/>
+        <exclude name="**/images/originals/**"/>
+      </fileset>
+    </copy>
+
+  </target>
+
+  <!-- build the printer friendly pages for jk -->
+  <target
+      name="jkprinter"
+      unless="docs-uptodate"
+      depends="docs.check,docs.init"
+      description="Create Printer Friendly Documentation for jk">
+        <mkdir dir="${build.docs}/jk/printer"/> 
+        <style
+            basedir="${source.docs}/jk"
+            destdir="${build.docs}/jk/printer"
+            style="${source.docs}/style.xsl" >
+        
+            <include name="**.xml" />
+            <param name="styles" expression="../"/>
+            <param name="project-menu" expression="nomenu"/>
+        </style>
+  </target>
+  <!-- build the printer friendly pages for jk2 -->
+  <target
+      name="jk2printer"
+      unless="docs-uptodate"
+      depends="docs.check,docs.init"
+      description="Create Printer Friendly Documentation for jk">
+        <mkdir dir="${build.docs}/jk2/printer"/> 
+        <style
+            basedir="${source.docs}/jk2"
+            destdir="${build.docs}/jk2/printer"
+            style="${source.docs}/style.xsl" >
+        
+            <include name="**.xml" />
+            <param name="styles" expression="../"/>
+            <param name="project-menu" expression="nomenu"/>
+        </style>
+  </target>
+
+</project>
diff --git a/connectors/jk/conf/jk2.manifest b/connectors/jk/conf/jk2.manifest
new file mode 100644
index 0000000..986d7b8
--- /dev/null
+++ b/connectors/jk/conf/jk2.manifest
@@ -0,0 +1,2 @@
+Main-Class: org.apache.jk.apr.TomcatStarter
+Class-Path: ../lib/tomcat.jar log4j.jar log4j-core.jar ../lib/common/log4j.jar ../lib/common/log4j-core.jar ../lib/common/classes ../lib/common/commons-logging.jar bootstrap.jar ../server/lib/commons-logging.jar ../server/lib/jmx.jar jmx.jar commons-logging-api.jar
diff --git a/connectors/jk/conf/jk2.properties b/connectors/jk/conf/jk2.properties
new file mode 100644
index 0000000..093bae8
--- /dev/null
+++ b/connectors/jk/conf/jk2.properties
@@ -0,0 +1,26 @@
+## THIS FILE MAY BE OVERRIDEN AT RUNTIME. MAKE SURE TOMCAT IS STOPED
+## WHEN YOU EDIT THE FILE.
+
+## COMMENTS WILL BE _LOST_
+
+## DOCUMENTATION OF THE FORMAT IN JkMain javadoc.
+
+# Set the desired handler list
+# handler.list=apr,request,channelJni
+#
+# Override the default port for the socketChannel
+# channelSocket.port=8019
+# Default: 
+# channelUnix.file=${jkHome}/work/jk2.socket
+# Just to check if the the config  is working
+# shm.file=${jkHome}/work/jk2.shm
+
+# In order to enable jni use any channelJni directive
+# channelJni.disabled = 0
+# And one of the following directives:
+
+# apr.jniModeSo=/opt/apache2/modules/mod_jk2.so
+
+# If set to inprocess the mod_jk2 will Register natives itself
+# This will enable the starting of the Tomcat from mod_jk2
+# apr.jniModeSo=inprocess
diff --git a/connectors/jk/conf/jkconf.ant.xml b/connectors/jk/conf/jkconf.ant.xml
new file mode 100644
index 0000000..245cf98
--- /dev/null
+++ b/connectors/jk/conf/jkconf.ant.xml
@@ -0,0 +1,51 @@
+<project name="jkconf" default="main" basedir=".">
+
+    <target name="init-3x" if="33.detect">
+        <taskdef name="jkconf" 
+                 classname="org.apache.jk.config.WebXml2Jk" >
+            <classpath>
+                <!-- 3.3 support -->
+                <pathelement location="/ws/jtc/jk/build/classes" />
+                <pathelement location="${tomcat.home}/lib/container/tomcat-jk2.jar" />
+                <pathelement location="${tomcat.home}/lib/container/crimson.jar"/>
+                <pathelement location="${tomcat.home}/lib/common/commons-logging.jar"/>
+            </classpath>
+        </taskdef>
+    </target>
+
+    <target name="init-4x" if="4x.detect" >
+        <path id="main.classpath">
+            <!-- 3.3 support -->
+            <fileset dir="${tomcat.home}/lib" includes="*.jar" />
+            <fileset dir="${tomcat.home}/server/lib" includes="*.jar" />
+            <fileset dir="${tomcat.home}/common/lib" includes="*.jar" />
+        </path>
+        
+        <taskdef name="jkconf" classpathref="main.classpath" 
+                 classname="org.apache.jk.config.WebXml2Jk" />
+    </target>
+
+    <target name="detect" >
+        <property file="build.properties"/>
+        <property file="${user.home}/build.properties"/>
+        <property file="${user.home}/.build.properties"/>
+            
+            <!-- default locations, overrident by properties.
+            This file must be installed in conf/  -->
+        <property name="tomcat.home" location=".." />
+            
+        <available property="33.detect" file="${tomcat.home}/lib/container" />
+        <available property="4x.detect" file="${tomcat.home}/server/lib" />
+    </target>
+
+    <target name="init" depends="detect,init-3x,init-4x" />
+
+    <!-- ==================== Detection and reports ==================== -->
+
+
+    <target name="main" depends="init">
+        <jkconf docBase="${tomcat.home}/webapps/examples" 
+                context="/examples" />
+    </target>
+
+</project>
diff --git a/connectors/jk/conf/jkconfig.manifest b/connectors/jk/conf/jkconfig.manifest
new file mode 100644
index 0000000..3ba1f2e
--- /dev/null
+++ b/connectors/jk/conf/jkconfig.manifest
@@ -0,0 +1,2 @@
+Main-Class: org.apache.jk.config.WebXml2Jk
+Class-Path: tomcat-jk2.jar commons-logging.jar crimson.jar xercesImpl.jar xmlApis.jar tomcat-util.jar log4j.jar log4j-core.jar
diff --git a/connectors/jk/conf/shm.manifest b/connectors/jk/conf/shm.manifest
new file mode 100644
index 0000000..0505c08
--- /dev/null
+++ b/connectors/jk/conf/shm.manifest
@@ -0,0 +1,2 @@
+Main-Class: org.apache.jk.common.Shm
+Class-Path: tomcat-jk2.jar commons-logging.jar tomcat-util.jar log4j.jar log4j-core.jar
diff --git a/connectors/jk/conf/tomcat-jk2.manifest b/connectors/jk/conf/tomcat-jk2.manifest
new file mode 100644
index 0000000..acfef4a
--- /dev/null
+++ b/connectors/jk/conf/tomcat-jk2.manifest
@@ -0,0 +1,7 @@
+Manifest-version: 1.0
+Extension-Name: org.apache.jk
+Specification-Vendor: Apache Software Foundation
+Specification-Version: 2.0
+Implementation-Vendor-Id: org.apache
+Implementation-Vendor: Apache Software Foundation
+Implementation-Version: 2.1
diff --git a/connectors/jk/conf/workers.properties b/connectors/jk/conf/workers.properties
new file mode 100644
index 0000000..50d8855
--- /dev/null
+++ b/connectors/jk/conf/workers.properties
@@ -0,0 +1,206 @@
+# workers.properties -
+#
+# This file provides jk derived plugins with the needed information to
+# connect to the different tomcat workers.  Note that the distributed
+# version of this file requires modification before it is usable by a
+# plugin.
+#
+# As a general note, the characters $( and ) are used internally to define
+# macros. Do not use them in your own configuration!!!
+#
+# Whenever you see a set of lines such as:
+# x=value
+# y=$(x)\something
+#
+# the final value for y will be value\something
+#
+# Normaly all you will need to do is un-comment and modify the first three
+# properties, i.e. workers.tomcat_home, workers.java_home and ps.
+# Most of the configuration is derived from these.
+#
+# When you are done updating workers.tomcat_home, workers.java_home and ps
+# you should have 3 workers configured:
+#
+# - An ajp12 worker that connects to localhost:8007
+# - An ajp13 worker that connects to localhost:8009
+# - A jni inprocess worker.
+# - A load balancer worker
+#
+# However by default the plugins will only use the ajp12 worker. To have
+# the plugins use other workers you should modify the worker.list property.
+#
+#
+
+# OPTIONS ( very important for jni mode ) 
+
+#
+# workers.tomcat_home should point to the location where you
+# installed tomcat. This is where you have your conf, webapps and lib
+# directories.
+#
+workers.tomcat_home=/var/tomcat3
+
+#
+# workers.java_home should point to your Java installation. Normally
+# you should have a bin and lib directories beneath it.
+#
+workers.java_home=/opt/IBMJava2-13
+
+#
+# You should configure your environment slash... ps=\ on NT and / on UNIX
+# and maybe something different elsewhere.
+#
+ps=/
+
+#
+#------ ADVANCED MODE ------------------------------------------------
+#---------------------------------------------------------------------
+#
+
+#
+#------ DEFAULT worket list ------------------------------------------
+#---------------------------------------------------------------------
+#
+#
+# The workers that your plugins should create and work with
+#
+# Add 'inprocess' if you want JNI connector 
+worker.list=ajp12, ajp13
+# , inprocess
+
+
+#
+#------ DEFAULT ajp12 WORKER DEFINITION ------------------------------
+#---------------------------------------------------------------------
+#
+
+#
+# Defining a worker named ajp12 and of type ajp12
+# Note that the name and the type do not have to match.
+#
+worker.ajp12.port=8007
+worker.ajp12.host=localhost
+worker.ajp12.type=ajp12
+#
+# Specifies the load balance factor when used with
+# a load balancing worker.
+# Note:
+#  ----> lbfactor must be > 0
+#  ----> Low lbfactor means less work done by the worker.
+worker.ajp12.lbfactor=1
+
+#
+#------ DEFAULT ajp13 WORKER DEFINITION ------------------------------
+#---------------------------------------------------------------------
+#
+
+#
+# Defining a worker named ajp13 and of type ajp13
+# Note that the name and the type do not have to match.
+#
+worker.ajp13.port=8009
+worker.ajp13.host=localhost
+worker.ajp13.type=ajp13
+#
+# Specifies the load balance factor when used with
+# a load balancing worker.
+# Note:
+#  ----> lbfactor must be > 0
+#  ----> Low lbfactor means less work done by the worker.
+worker.ajp13.lbfactor=1
+
+#
+# Specify the size of the open connection cache.
+#worker.ajp13.cachesize
+
+#
+#------ DEFAULT LOAD BALANCER WORKER DEFINITION ----------------------
+#---------------------------------------------------------------------
+#
+
+#
+# The loadbalancer (type lb) workers perform wighted round-robin
+# load balancing with sticky sessions.
+# Note:
+#  ----> If a worker dies, the load balancer will check its state
+#        once in a while. Until then all work is redirected to peer
+#        workers.
+worker.loadbalancer.type=lb
+worker.loadbalancer.balanced_workers=ajp12, ajp13
+
+
+#
+#------ DEFAULT JNI WORKER DEFINITION---------------------------------
+#---------------------------------------------------------------------
+#
+
+#
+# Defining a worker named inprocess and of type jni
+# Note that the name and the type do not have to match.
+#
+worker.inprocess.type=jni
+
+#
+#------ CLASSPATH DEFINITION -----------------------------------------
+#---------------------------------------------------------------------
+#
+
+#
+# Additional class path components.
+#
+worker.inprocess.class_path=$(workers.tomcat_home)$(ps)lib$(ps)tomcat.jar
+
+#
+# Setting the command line for tomcat. 
+# Note: The cmd_line string may not contain spaces.
+#
+worker.inprocess.cmd_line=start
+
+# Not needed, but can be customized.
+#worker.inprocess.cmd_line=-config
+#worker.inprocess.cmd_line=$(workers.tomcat_home)$(ps)conf$(ps)server.xml
+#worker.inprocess.cmd_line=-home
+#worker.inprocess.cmd_line=$(workers.tomcat_home)
+
+#
+# The JVM that we are about to use
+#
+# This is for Java2
+#
+# Windows
+worker.inprocess.jvm_lib=$(workers.java_home)$(ps)jre$(ps)bin$(ps)classic$(ps)jvm.dll
+# IBM JDK1.3 
+#worker.inprocess.jvm_lib=$(workers.java_home)$(ps)jre$(ps)bin$(ps)classic$(ps)libjvm.so
+# Unix - Sun VM or blackdown
+#worker.inprocess.jvm_lib=$(workers.java_home)$(ps)jre$(ps)lib$(ps)i386$(ps)classic$(ps)libjvm.so
+
+#
+# And this is for jdk1.1.X
+#
+#worker.inprocess.jvm_lib=$(workers.java_home)$(ps)bin$(ps)javai.dll
+
+
+#
+# Setting the place for the stdout and stderr of tomcat
+#
+worker.inprocess.stdout=$(workers.tomcat_home)$(ps)logs$(ps)inprocess.stdout
+worker.inprocess.stderr=$(workers.tomcat_home)$(ps)logs$(ps)inprocess.stderr
+
+#
+# Setting the tomcat.home Java property
+#
+#worker.inprocess.sysprops=tomcat.home=$(workers.tomcat_home)
+
+#
+# Java system properties
+#
+# worker.inprocess.sysprops=java.compiler=NONE
+# worker.inprocess.sysprops=myprop=mypropvalue
+
+#
+# Additional path components.
+#
+# worker.inprocess.ld_path=d:$(ps)SQLLIB$(ps)bin
+#
+
+
diff --git a/connectors/jk/conf/workers2.properties b/connectors/jk/conf/workers2.properties
new file mode 100644
index 0000000..778118f
--- /dev/null
+++ b/connectors/jk/conf/workers2.properties
@@ -0,0 +1,132 @@
+[logger]
+level=DEBUG
+
+[config:]
+file=${serverRoot}/conf/workers2.properties
+debug=0
+debugEnv=0
+
+[uriMap:]
+info=Maps the requests. Options: debug
+debug=0
+
+# Alternate file logger
+#[logger.file:0]
+#level=DEBUG
+#file=${serverRoot}/logs/jk2.log
+
+[shm:]
+info=Scoreboard. Required for reconfiguration and status with multiprocess servers
+file=${serverRoot}/logs/jk2.shm
+size=1000000
+debug=0
+disabled=0
+
+[workerEnv:]
+info=Global server options
+timing=1
+debug=0
+# Default Native Logger (apache2 or win32 ) 
+# can be overriden to a file logger, useful 
+# when tracing win32 related issues
+#logger=logger.file:0
+
+[lb:lb]
+info=Default load balancer.
+debug=0
+
+[lb:lb_1]
+info=A second load balancer.
+debug=0
+
+[channel.socket:localhost:8009]
+info=Ajp13 forwarding over socket
+debug=0
+tomcatId=localhost:8009
+
+[channel.socket:localhost:8019]
+info=A second tomcat instance. 
+debug=0
+tomcatId=localhost:8019
+lb_factor=1
+#group=lb
+group:lb:lb
+#group=lb_1
+group:lb:lb_1
+disabled=0
+
+[channel.un:/opt/33/work/jk2.socket]
+info=A second channel connecting to localhost:8019 via unix socket
+tomcatId=localhost:8019
+lb_factor=1
+debug=0
+
+[channel.jni:jni]
+info=The jni channel, used if tomcat is started inprocess
+
+[status:]
+info=Status worker, displays runtime informations
+
+[vm:]
+info=Parameters used to load a JVM in the server process
+#JVM=C:\jdk\jre\bin\hotspot\jvm.dll
+classpath=${TOMCAT_HOME}/bin/tomcat-jni.jar
+classpath=${TOMCAT_HOME}/server/lib/commons-logging.jar
+OPT=-Dtomcat.home=${TOMCAT_HOME}
+OPT=-Dcatalina.home=${TOMCAT_HOME}
+OPT=-Xmx128M
+#OPT=-Djava.compiler=NONE
+disabled=1
+
+[worker.jni:onStartup]
+info=Command to be executed by the VM on startup. This one will start tomcat.
+class=org/apache/jk/apr/TomcatStarter
+ARG=start
+# For Tomcat 5 use the 'stard' for startup argument
+# ARG=stard
+disabled=1
+stdout=${serverRoot}/logs/stdout.log
+stderr=${serverRoot}/logs/stderr.log
+
+[worker.jni:onShutdown]
+info=Command to be executed by the VM on shutdown. This one will stop tomcat.
+class=org/apache/jk/apr/TomcatStarter
+ARG=stop
+disabled=1
+
+[uri:/jkstatus/*]
+info=Display status information and checks the config file for changes.
+group=status:
+
+[uri:127.0.0.1:8003]
+info=Example virtual host. Make sure myVirtualHost is in /etc/hosts to test it
+alias=myVirtualHost:8003
+
+[uri:127.0.0.1:8003/ex]
+info=Example webapp in the virtual host. It'll go to lb_1 ( i.e. localhost:8019 )
+context=/ex
+group=lb_1
+
+[uri:/examples]
+info=Example webapp in the default context. 
+context=/examples
+debug=0
+
+[uri:/examples1/*]
+info=A second webapp, this time going to the second tomcat only.
+group=lb_1
+debug=0
+
+[uri:/examples/servlet/*]
+info=Prefix mapping
+
+[uri:/examples/*.jsp]
+info=Extension mapping
+
+[uri:/examples/*]
+info=Map the whole webapp
+
+[uri:/examples/servlet/HelloW]
+info=Example with debug enabled.
+debug=10
+
diff --git a/connectors/jk/conf/workers2.properties.minimal b/connectors/jk/conf/workers2.properties.minimal
new file mode 100644
index 0000000..41a0ba6
--- /dev/null
+++ b/connectors/jk/conf/workers2.properties.minimal
@@ -0,0 +1,55 @@
+#
+# This is the minimal JK2 connector configuration file.
+# 
+
+[logger]
+info=Native logger
+level=ERROR
+
+[config:]
+file=${serverRoot}/conf/workers2.properties
+debug=0
+debugEnv=0
+
+[uriMap:]
+info=Maps the requests.
+debug=0
+
+[shm:]
+info=Scoreboard. Required for reconfiguration and status with multiprocess servers
+file=anonymous
+debug=0
+
+[workerEnv:]
+info=Global server options
+timing=0
+debug=0
+
+[lb:lb]
+info=Default load balancer.
+debug=0
+
+[channel.socket:localhost:8009]
+info=Ajp13 forwarding over socket
+debug=0
+tomcatId=localhost:8009
+
+[uri:/admin]
+info=Tomcat HTML based administration web application.
+debug=0
+
+[uri:/manager]
+info=A scriptable management web application for the Tomcat Web Server.
+debug=0
+
+[uri:/jsp-examples]
+info=JSP 2.0 Examples.
+debug=0
+
+[uri:/servlets-examples]
+info=Servlet 2.4 Examples.
+debug=0
+
+[uri:/*.jsp]
+info=JSP Extension mapping.
+debug=0
diff --git a/connectors/jk/java/org/apache/ajp/Ajp13.java b/connectors/jk/java/org/apache/ajp/Ajp13.java
new file mode 100644
index 0000000..12a6d5c
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/Ajp13.java
@@ -0,0 +1,512 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+import org.apache.tomcat.util.http.BaseRequest;
+import org.apache.tomcat.util.http.HttpMessages;
+import org.apache.tomcat.util.http.MimeHeaders;
+
+/**
+ * Represents a single, persistent connection between the web server and
+ * the servlet container.  Uses the Apache JServ Protocol version 1.3 for
+ * communication.  Because this protocal does not multiplex requests, this
+ * connection can only be associated with a single request-handling cycle
+ * at a time.<P>
+ *
+ * This class contains knowledge about how an individual packet is laid out
+ * (via the <CODE>Ajp13Packet</CODE> class), and also about the
+ * stages of communicaton between the server and the servlet container.  It
+ * translates from Tomcat's internal servlet support methods
+ * (e.g. doWrite) to the correct packets to send to the web server.
+ *
+ * @author Dan Milstein [danmil@shore.net]
+ * @author Keith Wannamaker [Keith@Wannamaker.org]
+ * @author Kevin Seguin [seguin@apache.org]
+ * @author Henri Gomez [hgomez@apache.org]
+ * @author Costin Manolache
+ */
+public class Ajp13 {
+
+    public static final int MAX_PACKET_SIZE=8192;
+    public static final int H_SIZE=4;  // Size of basic packet header
+
+    public static final int  MAX_READ_SIZE = MAX_PACKET_SIZE - H_SIZE - 2;
+    public static final int  MAX_SEND_SIZE = MAX_PACKET_SIZE - H_SIZE - 4;
+
+    // Error code for Ajp13
+    public static final int  JK_AJP13_BAD_HEADER        = -100;
+    public static final int  JK_AJP13_NO_HEADER         = -101;
+    public static final int  JK_AJP13_COMM_CLOSED       = -102;
+    public static final int  JK_AJP13_COMM_BROKEN       = -103;
+    public static final int  JK_AJP13_BAD_BODY          = -104;
+    public static final int  JK_AJP13_INCOMPLETE_BODY   = -105;
+
+    // ============ Instance Properties ====================
+
+    OutputStream out;
+    InputStream in;
+
+    // Buffer used of output body and headers
+    public Ajp13Packet outBuf = new Ajp13Packet( MAX_PACKET_SIZE );
+    // Buffer used for input body
+    Ajp13Packet inBuf  = new Ajp13Packet( MAX_PACKET_SIZE );
+    // Buffer used for request head ( and headers )
+    Ajp13Packet hBuf=new Ajp13Packet( MAX_PACKET_SIZE );
+
+    // Holds incoming reads of request body data (*not* header data)
+    byte []bodyBuff = new byte[MAX_READ_SIZE];
+    
+    int blen;  // Length of current chunk of body data in buffer
+    int pos;   // Current read position within that buffer
+
+    boolean end_of_stream;  // true if we've received an empty packet
+    
+    // Required handler - essential request processing
+    public RequestHandler reqHandler;
+    // AJP14 - detect protocol,set communication parameters, login
+    // If no password is set, use only Ajp13 messaging
+    boolean backwardCompat=true;
+    boolean logged=false;
+    String secret=null;
+    
+    public Ajp13() {
+	super();
+	initBuf();
+        reqHandler=new RequestHandler();
+	reqHandler.init( this );
+    }
+
+    public Ajp13(RequestHandler reqHandler ) {
+	super();
+	initBuf();
+        this.reqHandler=reqHandler;
+	reqHandler.init( this );
+    }
+
+    /** Will be overriden
+     */
+    public void initBuf() {
+	outBuf = new Ajp13Packet( MAX_PACKET_SIZE );
+	inBuf  = new Ajp13Packet( MAX_PACKET_SIZE );
+	hBuf=new Ajp13Packet( MAX_PACKET_SIZE );
+    }
+    
+    public void recycle() {
+        if (debug > 0) {
+            logger.log("recycle()");
+        }
+
+        // This is a touch cargo-cultish, but I think wise.
+        blen = 0; 
+        pos = 0;
+        end_of_stream = false;
+        logged=false;
+    }
+    
+    /**
+     * Associate an open socket with this instance.
+     */
+    public void setSocket( Socket socket ) throws IOException {
+        if (debug > 0) {
+            logger.log("setSocket()");
+        }
+        
+	socket.setSoLinger( true, 100);
+	out = socket.getOutputStream();
+	in  = socket.getInputStream();
+	pos = 0;
+    }
+
+    /**
+     * Backward compat mode, no login  needed
+     */
+    public void setBackward(boolean b) 
+    {
+        backwardCompat=b;
+    }
+
+    public boolean isLogged() {
+	return logged;
+    }
+
+    void setLogged( boolean b ) {
+        logged=b;
+    }
+
+    public void setSecret( String s ) {
+        secret=s;
+    }
+
+    public String getSecret() {
+        return secret;
+    }
+    
+    // -------------------- Handlers registry --------------------
+
+    static final int MAX_HANDLERS=32;
+    static final int RESERVED=16;  // reserved names, backward compat
+
+    // Note that we don't make distinction between in and out
+    // messages ( i.e. one id is used only in one direction )
+    AjpHandler handlers[]=new AjpHandler[MAX_HANDLERS];
+    String handlerName[]=new String[MAX_HANDLERS];
+    int currentId=RESERVED;
+
+    public int registerMessageType( int id, String name, AjpHandler h,
+				    String sig[] )
+    {
+	if( id < 0 ) {
+	    // try to find it by name
+	    for( int i=0; i< handlerName.length; i++ )
+		if( name.equals( handlerName[i] ) ) return i;
+	    handlerName[currentId]=name;
+	    handlers[currentId]=h;
+	    currentId++;
+	    return currentId;
+	}
+	// fixed id
+	handlerName[id]=name;
+	handlers[id]=h;
+	return id;
+    }
+    
+    // -------------------- Main dispatch --------------------
+    
+    /**
+     * Read a new packet from the web server and decode it.  If it's a
+     * forwarded request, store its properties in the passed-in AjpRequest
+     * object.
+     *
+     * @param req An empty (newly-recycled) request object.
+     * 
+     * @return 200 in case of a successful read of a forwarded request, 500
+     * if there were errors in the reading of the request, and -2 if the
+     * server is asking the container to shut itself down.  
+     */
+    public int receiveNextRequest(BaseRequest req) throws IOException {
+        if (debug > 0) {
+            logger.log("receiveNextRequest()");
+        }
+        
+        // XXX The return values are awful.
+
+        int err = 0;
+
+        // if we receive an IOException here, it must be because
+        // the remote just closed the ajp13 connection, and it's not
+        // an error, we just need to close the AJP13 connection
+        try {
+            err = receive(hBuf);
+        } catch (IOException ioe) {
+            if(debug >0 ) logger.log( "IOException receiving message ");
+            return -1;  // Indicate it's a disconnection from the remote end
+        }
+        
+	if(err < 0) {
+	    if(debug >0 ) logger.log( "Error receiving message ");
+	    return 500;
+	}
+	
+	int type = (int)hBuf.getByte();
+        //        System.out.println("XXX " + this );
+        return handleMessage( type, hBuf, req );
+    }
+
+    /** Override for ajp14, temporary
+     */
+    public int handleMessage( int type, Ajp13Packet hBuf, BaseRequest req )
+        throws IOException
+    {
+        if( type > handlers.length ) {
+	    logger.log( "Invalid handler " + type );
+	    return 500;
+	}
+
+        if( debug > 0 )
+            logger.log( "Received " + type + " " + handlerName[type]);
+        
+        // Ajp14, unlogged
+	if( ! backwardCompat && ! isLogged() ) {
+	    if( type != NegociationHandler.JK_AJP14_LOGINIT_CMD &&
+		type != NegociationHandler.JK_AJP14_LOGCOMP_CMD ) {
+
+                logger.log( "Ajp14 error: not logged " +
+                            type + " " + handlerName[type]);
+
+		return 300;
+	    }
+	    // else continue
+	}
+
+        // Ajp13 messages
+	switch(type) {
+	case RequestHandler.JK_AJP13_FORWARD_REQUEST:
+	    return reqHandler.decodeRequest(this, hBuf, req);
+	    
+	case RequestHandler.JK_AJP13_CPING_REQUEST:
+		return reqHandler.sendCPong(this, outBuf);
+		
+	case RequestHandler.JK_AJP13_SHUTDOWN:
+	    return -2;
+	}
+
+	// logged || loging message
+	AjpHandler handler=handlers[type];
+	if( handler==null ) {
+	    logger.log( "Unknown message " + type + handlerName[type] );
+	    return 200;
+	}
+
+        if( debug > 0 )
+            logger.log( "Ajp14 handler " + handler );
+	return handler.handleAjpMessage( type, this, hBuf, req );
+    }
+
+    // ==================== Servlet Input Support =================
+
+    /** @deprecated -- Will use reqHandler, make sure nobody else
+     calls this */
+
+    
+    public int available() throws IOException {
+        return reqHandler.available(this);
+    }
+
+    public int doRead() throws IOException 
+    {
+        return reqHandler.doRead( this );
+    }
+    
+    public int doRead(byte[] b, int off, int len) throws IOException 
+    {
+        return reqHandler.doRead( this, b, off, len );
+    }
+    
+    private boolean refillReadBuffer() throws IOException 
+    {
+        return reqHandler.refillReadBuffer(this);
+    }    
+
+    public void beginSendHeaders(int status,
+                                 String statusMessage,
+                                 int numHeaders) throws IOException {
+        reqHandler.beginSendHeaders( this, outBuf,
+                                         status, statusMessage,
+                                         numHeaders);
+    }        
+
+	public void sendHeader(String name, String value) throws IOException {
+		reqHandler.sendHeader(  outBuf, name, value );
+	}
+
+
+    public void endSendHeaders() throws IOException {
+        reqHandler.endSendHeaders(this, outBuf);
+    }
+
+    public void sendHeaders(int status, MimeHeaders headers)
+        throws IOException
+    {
+        reqHandler.sendHeaders(this, outBuf, status,
+                                   HttpMessages.getMessage(status),
+                                   headers);
+    }
+
+    public void sendHeaders(int status, String statusMessage,
+                            MimeHeaders headers)
+        throws IOException
+    {
+        reqHandler.sendHeaders( this, outBuf, status,
+                                    statusMessage, headers );
+    }
+
+    public void finish() throws IOException {
+        reqHandler.finish(this, outBuf );
+    }
+
+    public void doWrite(byte b[], int off, int len) throws IOException {
+        reqHandler.doWrite( this, outBuf, b, off, len );
+    }
+    
+
+    // ========= Internal Packet-Handling Methods =================
+
+    /**
+     * Read N bytes from the InputStream, and ensure we got them all
+     * Under heavy load we could experience many fragmented packets
+     * just read Unix Network Programming to recall that a call to
+     * read didn't ensure you got all the data you want
+     *
+     * from read() Linux manual
+     *
+     * On success, the number of bytes read is returned (zero indicates end of file),
+     * and the file position is advanced by this number.
+     * It is not an error if this number is smaller than the number of bytes requested;
+     * this may happen for example because fewer bytes
+     * are actually available right now (maybe because we were close to end-of-file,
+     * or because we are reading from a pipe, or  from  a
+     * terminal),  or  because  read()  was interrupted by a signal.
+     * On error, -1 is returned, and errno is set appropriately. In this
+     * case it is left unspecified whether the file position (if any) changes.
+     *
+     **/
+    private int readN(InputStream in, byte[] b, int offset, int len) throws IOException {
+        int pos = 0;
+        int got;
+
+        while(pos < len) {
+            got = in.read(b, pos + offset, len - pos);
+
+            if (debug > 10) {
+                logger.log("read got # " + got);
+            }
+
+            // connection just closed by remote. 
+            if (got <= 0) {
+                // This happens periodically, as apache restarts
+                // periodically.
+                // It should be more gracefull ! - another feature for Ajp14 
+                return JK_AJP13_COMM_BROKEN;
+            }
+
+            pos += got;
+        }
+        return pos;
+     }
+
+    /**
+     * Read in a packet from the web server and store it in the passed-in
+     * <CODE>Ajp13Packet</CODE> object.
+     *
+     * @param msg The object into which to store the incoming packet -- any
+     * current contents will be overwritten.
+     *
+     * @return The number of bytes read on a successful read or -1 if there 
+     * was an error.
+     **/
+    public int receive(Ajp13Packet msg) throws IOException {
+        if (debug > 0) {
+            logger.log("receive()");
+        }
+
+	// XXX If the length in the packet header doesn't agree with the
+	// actual number of bytes read, it should probably return an error
+	// value.  Also, callers of this method never use the length
+	// returned -- should probably return true/false instead.
+	byte b[] = msg.getBuff();
+	
+        int rd = readN(in, b, 0, H_SIZE );
+        
+        // XXX - connection closed (JK_AJP13_COMM_CLOSED)
+        //     - connection broken (JK_AJP13_COMM_BROKEN)
+        //
+        if(rd < 0) {
+            // Most likely normal apache restart.
+            return rd;
+        }
+        
+	int len = msg.checkIn();
+	if( debug > 5 )
+            logger.log( "Received " + rd + " " + len + " " + b[0] );
+        
+	// XXX check if enough space - it's assert()-ed !!!
+        
+ 	int total_read = 0;
+        
+        total_read = readN(in, b, H_SIZE, len);
+
+        // it's ok to have read 0 bytes when len=0 -- this means
+        // the end of the stream has been reached.
+        if (total_read < 0) {
+            logger.log("can't read body, waited #" + len);
+            return JK_AJP13_BAD_BODY;
+        }
+        
+        if (total_read != len) {
+            logger.log( "incomplete read, waited #" + len +
+                        " got only " + total_read);
+            return JK_AJP13_INCOMPLETE_BODY;
+        }
+        
+        if (debug > 0)
+            logger.log("receive:  total read = " + total_read);
+	return total_read;
+    }
+    
+    /**
+     * Send a packet to the web server.  Works for any type of message.
+     *
+     * @param msg A packet with accumulated data to send to the server --
+     * this method will write out the length in the header.  
+     */
+    public void send( Ajp13Packet msg ) throws IOException {
+        if (debug > 0) {
+            logger.log("send()");
+        }
+
+	msg.end(); // Write the packet header
+	byte b[] = msg.getBuff();
+	int len  = msg.getLen();
+
+        if (debug > 5 )
+            logger.log("send() " + len + " " + b[0] );
+
+	out.write( b, 0, len );
+    }
+	
+    /**
+     * Close the socket connection to the web server.  In general, sockets
+     * are maintained across many requests, so this will not be called
+     * after finish().  
+     */
+    public void close() throws IOException {
+        if (debug > 0) {
+            logger.log("close()");
+        }
+
+	if(null != out) {        
+	    out.close();
+	}
+	if(null !=in) {
+	    in.close();
+	}
+        setLogged( false );	// no more logged now 
+    }
+
+    // -------------------- Debug --------------------
+    protected int debug = 0;
+    
+    public void setDebug(int debug) {
+        this.debug = debug;
+        this.reqHandler.setDebug(debug);
+    }
+
+    public void setLogger(Logger l) {
+        this.logger = l;
+        this.reqHandler.setLogger(l);
+    }
+
+    /**
+     * XXX place holder...
+     */
+    Logger logger = new Logger();
+}
diff --git a/connectors/jk/java/org/apache/ajp/Ajp13Packet.java b/connectors/jk/java/org/apache/ajp/Ajp13Packet.java
new file mode 100644
index 0000000..78ae917
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/Ajp13Packet.java
@@ -0,0 +1,507 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp;
+
+import java.io.UnsupportedEncodingException;
+
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.MimeHeaders;
+
+
+/**
+ * A single packet for communication between the web server and the
+ * container.  Designed to be reused many times with no creation of
+ * garbage.  Understands the format of data types for these packets.
+ * Can be used (somewhat confusingly) for both incoming and outgoing
+ * packets.  
+ *
+ * See Ajp14/Ajp13Packet.
+ *
+ * @author Henri Gomez [hgomez@apache.org]
+ * @author Dan Milstein [danmil@shore.net]
+ * @author Keith Wannamaker [Keith@Wannamaker.org]
+ * @author Kevin Seguin
+ * @author Costin Manolache
+ */
+public class Ajp13Packet {
+
+    public static final String DEFAULT_CHAR_ENCODING = "8859_1";
+    public static final int    AJP13_WS_HEADER       = 0x1234;
+    public static final int    AJP13_SW_HEADER       = 0x4142;  // 'AB'
+
+    /**
+     * encoding to use when converting byte[] <-> string
+     */
+    String encoding = DEFAULT_CHAR_ENCODING;
+
+    /**
+     * Holds the bytes of the packet
+     */
+    byte buff[];
+
+    /**
+     * The current read or write position in the buffer
+     */
+    int pos;    
+
+    /**
+     * This actually means different things depending on whether the
+     * packet is read or write.  For read, it's the length of the
+     * payload (excluding the header).  For write, it's the length of
+     * the packet as a whole (counting the header).  Oh, well.
+     */
+    int len; 
+
+    /**
+     * Create a new packet with an internal buffer of given size.
+     * @param size packet size
+     */
+    public Ajp13Packet( int size ) {
+        buff = new byte[size];
+    }
+
+    /**
+     * Create a new packet with given bytes
+     * @param b this packet's bytes.
+     */
+    public Ajp13Packet( byte b[] ) {
+        buff = b;
+    }
+
+    /**
+     * Set the encoding to use for byte[] <-> string
+     * conversions.
+     * @param encoding the encoding to use.
+     */
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * Get the encoding used for byte[] <-> string
+     * conversions.
+     * @return the encoding used.
+     */
+    public String getEncoding() {
+        return encoding;
+    }
+
+    /**
+     * Get the internal buffer
+     * @return internal buffer
+     */
+    public byte[] getBuff() {
+        return buff;
+    }
+
+    /**
+     * Get length.
+     * @return length -- This actually means different things depending on whether the
+     *                   packet is read or write.  For read, it's the length of the
+     *                   payload (excluding the header).  For write, it's the length of
+     *                   the packet as a whole (counting the header).  Oh, well.
+     */
+    public int getLen() {
+        return len;
+    }
+
+    /**
+     * Get offset into internal buffer.
+     * @return offset
+     */
+    public int getByteOff() {
+        return pos;
+    }
+
+    /**
+     * Set offset into internal buffer.
+     * @param c new offset
+     */
+    public void setByteOff(int c) {
+        pos=c;
+    }
+
+    /** 
+     * Parse the packet header for a packet sent from the web server to
+     * the container.  Set the read position to immediately after
+     * the header.
+     *
+     * @return The length of the packet payload, as encoded in the
+     * header, or -1 if the packet doesn't have a valid header.  
+     */
+    public int checkIn() {
+        pos = 0;
+        int mark = getInt();
+        len      = getInt();
+	    
+        if( mark != AJP13_WS_HEADER ) {
+            // XXX Logging
+            System.out.println("BAD packet " + mark);
+            dump( "In: " );
+            return -1;
+        }
+        return len;
+    }
+    
+    /**
+     * Prepare this packet for accumulating a message from the container to
+     * the web server.  Set the write position to just after the header
+     * (but leave the length unwritten, because it is as yet unknown).
+     */
+    public void reset() {
+        len = 4;
+        pos = 4;
+        buff[0] = (byte)(AJP13_SW_HEADER >> 8);
+        buff[1] = (byte)(AJP13_SW_HEADER & 0xFF);
+    }
+	
+    /**
+     * For a packet to be sent to the web server, finish the process of
+     * accumulating data and write the length of the data payload into
+     * the header.  
+     */
+    public void end() {
+        len = pos;
+        setInt( 2, len-4 );
+    }
+	
+    // ============ Data Writing Methods ===================
+
+    /**
+     * Write an 32 bit integer at an arbitrary position in the packet,but don't
+     * change the write position.
+     *
+     * @param bpos The 0-indexed position within the buffer at which to
+     * write the integer (where 0 is the beginning of the header).
+     * @param val The integer to write.
+     */
+    private void setInt( int bPos, int val ) {
+        buff[bPos]   = (byte) ((val >>>  8) & 0xFF);
+        buff[bPos+1] = (byte) (val & 0xFF);
+    }
+
+    public void appendInt( int val ) {
+        setInt( pos, val );
+        pos += 2;
+    }
+	
+    public void appendByte( byte val ) {
+        buff[pos++] = val;
+    }
+	
+    public void appendBool( boolean val) {
+        buff[pos++] = (byte) (val ? 1 : 0);
+    }
+
+    /**
+     * Write a String out at the current write position.  Strings are
+     * encoded with the length in two bytes first, then the string, and
+     * then a terminating \0 (which is <B>not</B> included in the
+     * encoded length).  The terminator is for the convenience of the C
+     * code, where it saves a round of copying.  A null string is
+     * encoded as a string with length 0.  
+     */
+    public void appendString(String str) throws UnsupportedEncodingException {
+        // Dual use of the buffer - as Ajp13Packet and as OutputBuffer
+        // The idea is simple - fewer buffers, smaller footprint and less
+        // memcpy. The code is a bit tricky, but only local to this
+        // function.
+        if(str == null) {
+            setInt( pos, 0);
+            buff[pos + 2] = 0;
+            pos += 3;
+            return;
+        }
+
+        //
+        // XXX i don't have OutputBuffer in tc4... ks.
+        // fix this later...
+        //
+        byte[] bytes = str.getBytes(encoding);
+        appendBytes(bytes, 0, bytes.length);
+        
+        /* XXX XXX XXX XXX Try to add it back.
+        int strStart=pos;
+
+        // This replaces the old ( buggy and slow ) str.length()
+        // and str.getBytes(). str.length() is chars, may be != bytes
+        // and getBytes is _very_ slow.
+        // XXX setEncoding !!!
+
+        ob.setByteOff( pos+2 ); 
+        try {
+            ob.write( str );
+            ob.flushChars();
+        } catch( IOException ex ) {
+            ex.printStackTrace();
+        }
+        int strEnd=ob.getByteOff();
+        
+        buff[strEnd]=0; // The \0 terminator
+        int strLen=strEnd-strStart;
+        setInt( pos, strEnd - strStart );
+        pos += strLen + 3; 
+        */
+    }
+
+    /** 
+     * Copy a chunk of bytes into the packet, starting at the current
+     * write position.  The chunk of bytes is encoded with the length
+     * in two bytes first, then the data itself, and finally a
+     * terminating \0 (which is <B>not</B> included in the encoded
+     * length).
+     *
+     * @param b The array from which to copy bytes.
+     * @param off The offset into the array at which to start copying
+     * @param numBytes The number of bytes to copy.  
+     */
+    public void appendBytes( byte b[], int off, int numBytes ) {
+        appendInt( numBytes );
+        if( pos + numBytes >= buff.length ) {
+            System.out.println("Buffer overflow " + buff.length + " " + pos + " " + numBytes );
+            // XXX Log
+        }
+        System.arraycopy( b, off, buff, pos, numBytes);
+        buff[pos + numBytes] = 0; // Terminating \0
+        pos += numBytes + 1;
+    }
+
+        /**
+     * Write a 32 bits integer at an arbitrary position in the packet, but don't
+     * change the write position.
+     *
+     * @param bpos The 0-indexed position within the buffer at which to
+     * write the integer (where 0 is the beginning of the header).
+     * @param val The integer to write.
+     */
+    private void setLongInt( int bPos, int val ) {
+        buff[bPos]   = (byte) ((val >>>  24) & 0xFF);
+        buff[bPos+1] = (byte) ((val >>>  16) & 0xFF);
+        buff[bPos+2] = (byte) ((val >>>   8) & 0xFF);
+        buff[bPos+3] = (byte) (val & 0xFF);
+    }
+
+    public void appendLongInt( int val ) {
+        setLongInt( pos, val );
+        pos += 4;
+    }
+
+    /**
+     * Copy a chunk of bytes into the packet, starting at the current
+     * write position.  The chunk of bytes IS NOT ENCODED with ANY length
+     * header.
+     *
+     * @param b The array from which to copy bytes.
+     * @param off The offset into the array at which to start copying
+     * @param numBytes The number of bytes to copy.
+     */
+    public void appendXBytes(byte[] b, int off, int numBytes) {
+        if( pos + numBytes > buff.length ) {
+        System.out.println("appendXBytes - Buffer overflow " + buff.length + " " + pos + " " + numBytes );
+        // XXX Log
+        }
+        System.arraycopy( b, off, buff, pos, numBytes);
+        pos += numBytes;
+    }
+	
+    
+    // ============ Data Reading Methods ===================
+
+    /**
+     * Read an integer from packet, and advance the read position past
+     * it.  Integers are encoded as two unsigned bytes with the
+     * high-order byte first, and, as far as I can tell, in
+     * little-endian order within each byte.  
+     */
+    public int getInt() {
+        int result = peekInt();
+        pos += 2;
+        return result;
+    }
+
+    /**
+     * Read an integer from the packet, but don't advance the read
+     * position past it.  
+     */
+    public int peekInt() {
+        int b1 = buff[pos] & 0xFF;  // No swap, Java order
+        int b2 = buff[pos + 1] & 0xFF;
+
+        return  (b1<<8) + b2;
+    }
+
+    public byte getByte() {
+        byte res = buff[pos];
+        pos++;
+        return res;
+    }
+
+    public byte peekByte() {
+        return buff[pos];
+    }
+
+    public boolean getBool() {
+        return (getByte() == (byte) 1);
+    }
+
+    public void getMessageBytes(MessageBytes mb) {
+        int length = getInt();
+        if( (length == 0xFFFF) || (length == -1) ) {
+            mb.setString( null );
+            return;
+        }
+        mb.setBytes( buff, pos, length );
+        pos += length;
+        pos++; // Skip the terminating \0
+    }
+    
+    public MessageBytes addHeader(MimeHeaders headers) {
+        int length = getInt();
+        if( (length == 0xFFFF) || (length == -1) ) {
+            return null;
+        }
+        MessageBytes vMB=headers.addValue( buff, pos, length );
+        pos += length;
+        pos++; // Skip the terminating \0
+	    
+        return vMB;
+    }
+	
+    /**
+     * Read a String from the packet, and advance the read position
+     * past it.  See appendString for details on string encoding.
+     **/
+    public String getString() throws java.io.UnsupportedEncodingException {
+        int length = getInt();
+        if( (length == 0xFFFF) || (length == -1) ) {
+            //	    System.out.println("null string " + length);
+            return null;
+        }
+        String s = new String(buff, pos, length, encoding);
+
+        pos += length;
+        pos++; // Skip the terminating \0
+        return s;
+    }
+
+    /**
+     * Copy a chunk of bytes from the packet into an array and advance
+     * the read position past the chunk.  See appendBytes() for details
+     * on the encoding.
+     *
+     * @return The number of bytes copied.
+     */
+    public int getBytes(byte dest[]) {
+        int length = getInt();
+        if( length > buff.length ) {
+            // XXX Should be if(pos + length > buff.legth)?
+            System.out.println("XXX Assert failed, buff too small ");
+        }
+	
+        if( (length == 0xFFFF) || (length == -1) ) {
+            System.out.println("null string " + length);
+            return 0;
+        }
+
+        System.arraycopy( buff, pos,  dest, 0, length );
+        pos += length;
+        pos++; // Skip terminating \0  XXX I believe this is wrong but harmless
+        return length;
+    }
+
+        /**
+     * Read a 32 bits integer from packet, and advance the read position past
+     * it.  Integers are encoded as four unsigned bytes with the
+     * high-order byte first, and, as far as I can tell, in
+     * little-endian order within each byte.
+     */
+    public int getLongInt() {
+        int result = peekLongInt();
+        pos += 4;
+        return result;
+    }
+
+    /**
+     * Copy a chunk of bytes from the packet into an array and advance
+     * the read position past the chunk.  See appendXBytes() for details
+     * on the encoding.
+     *
+     * @return The number of bytes copied.
+     */
+    public int getXBytes(byte[] dest, int length) {
+        if( length > buff.length ) {
+        // XXX Should be if(pos + length > buff.legth)?
+        System.out.println("XXX Assert failed, buff too small ");
+        }
+
+        System.arraycopy( buff, pos,  dest, 0, length );
+        pos += length;
+        return length;
+    }
+
+    /**
+     * Read a 32 bits integer from the packet, but don't advance the read
+     * position past it.
+     */
+    public int peekLongInt() {
+        int b1 = buff[pos] & 0xFF;  // No swap, Java order
+        b1 <<= 8;
+        b1 |= (buff[pos + 1] & 0xFF);
+        b1 <<= 8;
+        b1 |= (buff[pos + 2] & 0xFF);
+        b1 <<=8;
+        b1 |= (buff[pos + 3] & 0xFF);
+        return  b1;
+    }
+
+    // ============== Debugging code =========================
+    private String hex( int x ) {
+        //	    if( x < 0) x=256 + x;
+        String h=Integer.toHexString( x );
+        if( h.length() == 1 ) h = "0" + h;
+        return h.substring( h.length() - 2 );
+    }
+
+    private void hexLine( int start ) {
+	int pkgEnd = len + 4;
+	if( pkgEnd > buff.length )
+	    pkgEnd = buff.length;
+        for( int i=start; i< start+16 ; i++ ) {
+            if( i < pkgEnd)
+                System.out.print( hex( buff[i] ) + " ");
+            else 
+                System.out.print( "   " );
+        }
+        System.out.print(" | ");
+        for( int i=start; i < start+16 && i < pkgEnd; i++ ) {
+            if( Character.isLetterOrDigit( (char)buff[i] ))
+                System.out.print( new Character((char)buff[i]) );
+            else
+                System.out.print( "." );
+        }
+        System.out.println();
+    }
+    
+    public void dump(String msg) {
+        System.out.println( msg + ": " + buff + " " + pos +"/" + (len + 4));
+
+        for( int j=0; j < len + 4; j+=16 )
+            hexLine( j );
+	
+        System.out.println();
+    }
+}
diff --git a/connectors/jk/java/org/apache/ajp/AjpHandler.java b/connectors/jk/java/org/apache/ajp/AjpHandler.java
new file mode 100644
index 0000000..1e6661c
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/AjpHandler.java
@@ -0,0 +1,53 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.http.BaseRequest;
+
+/**
+ * Base class for handlers of Ajp messages. Jk provide a simple bidirectional 
+ * communication mechanism between the web server and a servlet container. It is
+ * based on messages that are passed between the 2 server, using a single
+ * thread on each side.
+ *
+ * The container side must be able to deal with at least the "REQUEST FORWARD",
+ * the server side must be able to deal with at least "HEADERS", "BODY",
+ * "END" messages.
+ *
+ * @author Henri Gomez
+ * @author Costin Manolache
+ */
+public class AjpHandler
+{
+    public static final int UNKNOWN=-1;
+    Ajp13 channel;
+    
+    public void init( Ajp13 channel ) {
+        this.channel=channel;
+    }
+    
+    /** Execute the callback 
+     */
+    public int handleAjpMessage( int type, Ajp13 channel,
+				 Ajp13Packet ajp, BaseRequest req )
+	throws IOException
+    {
+	return UNKNOWN;
+    }
+}    
diff --git a/connectors/jk/java/org/apache/ajp/Logger.java b/connectors/jk/java/org/apache/ajp/Logger.java
new file mode 100644
index 0000000..3570231
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/Logger.java
@@ -0,0 +1,47 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp;
+
+/**
+ * A simple logger class used by classes in this package.
+ * The intention is for this class to be overridden so that
+ * log messages from classes in this package can be adapted
+ * to loggers used by the connector implementations for various
+ * servlet containers.
+ *
+ * @author Kevin Seguin [seguin@apache.org]
+ */
+public class Logger {
+    
+    /**
+     * Log the given message.
+     * @param msg The message to log.
+     */
+    public void log(String msg) {
+        System.out.println("[Ajp13] " + msg);
+    }
+    
+    /**
+     * Log the given message and error.
+     * @param msg The message to log.
+     * @param t The error to log.
+     */
+    public void log(String msg, Throwable t) {
+        System.out.println("[Ajp13] " + msg);
+        t.printStackTrace(System.out);
+    }
+}
diff --git a/connectors/jk/java/org/apache/ajp/NegociationHandler.java b/connectors/jk/java/org/apache/ajp/NegociationHandler.java
new file mode 100644
index 0000000..fa3ce38
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/NegociationHandler.java
@@ -0,0 +1,527 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+
+import org.apache.tomcat.util.buf.HexUtils;
+import org.apache.tomcat.util.http.BaseRequest;
+
+
+/**
+ * Handler for the protocol negotiation. It will authenticate and 
+ * exchange information about supported messages on each end.
+ * 
+ * 
+ * @author Henri Gomez [hgomez@apache.org]
+ * @author Dan Milstein [danmil@shore.net]
+ * @author Keith Wannamaker [Keith@Wannamaker.org]
+ * @author Costin Manolache
+ */
+public class NegociationHandler extends AjpHandler
+{
+    // Initial Login Phase (web server -> servlet engine)
+    public static final byte JK_AJP14_LOGINIT_CMD=0x10;
+    
+    // Second Login Phase (servlet engine -> web server), md5 seed is received
+    public static final byte JK_AJP14_LOGSEED_CMD=0x11;
+    
+    // Third Login Phase (web server -> servlet engine),
+    // md5 of seed + secret is sent
+    public static final byte JK_AJP14_LOGCOMP_CMD=0x12;
+    
+    // Login Accepted (servlet engine -> web server)
+    public static final byte JK_AJP14_LOGOK_CMD=0x13;
+
+    // Login Rejected (servlet engine -> web server), will be logged
+    public static final byte JK_AJP14_LOGNOK_CMD=0x14;
+    
+    // Context Query (web server -> servlet engine),
+    // which URI are handled by servlet engine ?
+    public static final byte JK_AJP14_CONTEXT_QRY_CMD=0x15;
+    
+    // Context Info (servlet engine -> web server), URI handled response
+    public static final byte JK_AJP14_CONTEXT_INFO_CMD= 0x16;
+    
+    // Context Update (servlet engine -> web server), status of context changed
+    public static final byte JK_AJP14_CONTEXT_UPDATE_CMD= 0x17;
+    
+    // Servlet Engine Status (web server -> servlet engine),
+    // what's the status of the servlet engine ?
+    public static final byte JK_AJP14_STATUS_CMD= 0x18;
+    
+    // Secure Shutdown command (web server -> servlet engine),
+    //please servlet stop yourself.
+    public static final byte JK_AJP14_SHUTDOWN_CMD= 0x19;
+    
+    // Secure Shutdown command Accepted (servlet engine -> web server)
+    public static final byte JK_AJP14_SHUTOK_CMD= 0x1A;
+    
+    // Secure Shutdown Rejected (servlet engine -> web server)
+    public static final byte JK_AJP14_SHUTNOK_CMD= 0x1B;
+    
+    // Context Status (web server -> servlet engine),
+    //what's the status of the context ?
+    public static final byte JK_AJP14_CONTEXT_STATE_CMD= 0x1C;
+    
+    // Context Status Reply (servlet engine -> web server), status of context
+    public static final byte JK_AJP14_CONTEXT_STATE_REP_CMD = 0x1D;
+    
+    // Unknown Packet Reply (web server <-> servlet engine),
+    //when a packet couldn't be decoded
+    public static final byte JK_AJP14_UNKNOW_PACKET_CMD = 0x1E;
+
+
+    // -------------------- Other constants -------------------- 
+
+    // Entropy Packet Size
+    public static final int AJP14_ENTROPY_SEED_LEN= 32;
+    public static final int AJP14_COMPUTED_KEY_LEN= 32;
+    
+    // web-server want context info after login
+    public static final int AJP14_CONTEXT_INFO_NEG= 0x80000000;
+    
+    // web-server want context updates
+    public static final int AJP14_CONTEXT_UPDATE_NEG= 0x40000000;
+    
+    // web-server want compressed stream
+    public static final int AJP14_GZIP_STREAM_NEG= 0x20000000;
+    
+    // web-server want crypted DES56 stream with secret key
+    public static final int AJP14_DES56_STREAM_NEG= 0x10000000;
+    
+    // Extended info on server SSL vars
+    public static final int AJP14_SSL_VSERVER_NEG= 0x08000000;
+    
+    // Extended info on client SSL vars
+    public static final int AJP14_SSL_VCLIENT_NEG= 0x04000000;
+    
+    // Extended info on crypto SSL vars
+    public static final int AJP14_SSL_VCRYPTO_NEG= 0x02000000;
+    
+    // Extended info on misc SSL vars
+    public static final int  AJP14_SSL_VMISC_NEG= 0x01000000;
+    
+    // mask of protocol supported
+    public static final int  AJP14_PROTO_SUPPORT_AJPXX_NEG=0x00FF0000;
+    
+    // communication could use AJP14
+    public static final int  AJP14_PROTO_SUPPORT_AJP14_NEG=0x00010000;
+    
+    // communication could use AJP15
+    public static final int  AJP14_PROTO_SUPPORT_AJP15_NEG=0x00020000;
+    
+    // communication could use AJP16
+    public static final int  AJP14_PROTO_SUPPORT_AJP16_NEG=0x00040000;
+    
+    // Some failure codes
+    public static final int AJP14_BAD_KEY_ERR= 0xFFFFFFFF;
+    public static final int AJP14_ENGINE_DOWN_ERR = 0xFFFFFFFE;
+    public static final int AJP14_RETRY_LATER_ERR = 0xFFFFFFFD;
+    public static final int AJP14_SHUT_AUTHOR_FAILED_ERR = 0xFFFFFFFC;
+    
+    // Some status codes
+    public static final byte AJP14_CONTEXT_DOWN= 0x01;
+    public static final byte AJP14_CONTEXT_UP= 0x02;
+    public static final byte AJP14_CONTEXT_OK= 0x03;
+
+
+    // -------------------- Parameters --------------------
+    String  containerSignature="Ajp14-based container";
+    String  seed="seed";// will use random
+    String  password;
+
+    int	webserverNegociation=0;
+    //    String  webserverName;
+    
+    public NegociationHandler() {
+        setSeed("myveryrandomentropy");
+	setPassword("myverysecretkey");
+    }
+
+    public void setContainerSignature( String s ) {
+	containerSignature=s;
+    }
+
+    // -------------------- State --------------------
+
+    //     public String getWebserverName() {
+    // 	return webserverName;
+    //     }
+    
+    // -------------------- Parameters --------------------
+    
+    /**
+     * Set the original entropy seed
+     */
+    public void setSeed(String pseed) 
+    {
+	String[] credentials = new String[1];
+	credentials[0] = pseed;
+	seed = digest(credentials, "md5");
+    }
+
+    /**
+     * Get the original entropy seed
+     */
+    public String getSeed()
+    {
+	return seed;
+    }
+
+    /**
+     * Set the secret password
+     */
+    public void setPassword(String ppwd) 
+    {
+	password = ppwd;
+    }
+    
+    /**
+     * Get the secret password
+     */
+    public String getPassword()
+    {
+	return password;
+    }
+
+    // -------------------- Initialization --------------------
+
+    public void init( Ajp13 ajp14 ) {
+        super.init(ajp14);
+	// register incoming message handlers
+	ajp14.registerMessageType( JK_AJP14_LOGINIT_CMD,"JK_AJP14_LOGINIT_CMD",
+				   this, null); //
+	ajp14.registerMessageType( JK_AJP14_LOGCOMP_CMD,"JK_AJP14_LOGCOMP_CMD",
+				   this, null); //
+	ajp14.registerMessageType( RequestHandler.JK_AJP13_SHUTDOWN,"JK_AJP13_SHUTDOWN",
+				   this, null); //
+	ajp14.registerMessageType( JK_AJP14_CONTEXT_QRY_CMD,
+				   "JK_AJP14_CONTEXT_QRY_CMD",
+				   this, null); //
+	ajp14.registerMessageType( JK_AJP14_STATUS_CMD,"JK_AJP14_STATUS_CMD",
+				   this, null); //
+	ajp14.registerMessageType( JK_AJP14_SHUTDOWN_CMD,
+                                   "JK_AJP14_SHUTDOWN_CMD",
+				   this, null); //
+	ajp14.registerMessageType( JK_AJP14_CONTEXT_STATE_CMD,
+				   "JK_AJP14_CONTEXT_STATE_CMD",
+				   this, null); //
+	ajp14.registerMessageType( JK_AJP14_UNKNOW_PACKET_CMD,
+				   "JK_AJP14_UNKNOW_PACKET_CMD",
+				   this, null); //
+	
+	// register outgoing messages handler
+	ajp14.registerMessageType( JK_AJP14_LOGNOK_CMD,"JK_AJP14_LOGNOK_CMD",
+				   this,null );
+	
+    }
+    
+
+    
+    // -------------------- Dispatch --------------------
+
+    public int handleAjpMessage( int type, Ajp13 ch, Ajp13Packet hBuf,
+				 BaseRequest req )
+	throws IOException
+    {
+        System.out.println("handleAjpMessage: " + type );
+	Ajp13Packet outBuf=ch.outBuf;
+	// Valid requests when not logged:
+	switch( type ) {
+	case JK_AJP14_LOGINIT_CMD :
+	    return handleLogInit(ch, hBuf, outBuf);
+	case JK_AJP14_LOGCOMP_CMD :
+	    return handleLogComp(ch, hBuf, outBuf);
+	case RequestHandler.JK_AJP13_SHUTDOWN:
+	    return -2;
+	case JK_AJP14_CONTEXT_QRY_CMD :
+	    return handleContextQuery(ch, hBuf, outBuf);
+	case JK_AJP14_STATUS_CMD :
+	    return handleStatus(hBuf, outBuf);
+	case JK_AJP14_SHUTDOWN_CMD :
+	    return handleShutdown(hBuf, outBuf);
+	case JK_AJP14_CONTEXT_STATE_CMD :
+	    return handleContextState(hBuf, outBuf);
+	case JK_AJP14_UNKNOW_PACKET_CMD :
+	    return handleUnknowPacket(hBuf, outBuf);
+	default:
+	    log("unknown command " + type + " received");
+	    return 200; // XXX This is actually an error condition
+	}
+	//return UNKNOWN;
+    }
+    
+    //----------- Implementation for various protocol commands -----------
+
+    /**
+     * Handle the Initial Login Message from Web-Server
+     *
+     * Get the requested Negociation Flags
+     * Get also the Web-Server Name
+     * 
+     * Send Login Seed (MD5 of seed)
+     */
+    private int handleLogInit( Ajp13 ch, Ajp13Packet msg,
+			       Ajp13Packet outBuf )
+	throws IOException
+    {
+	webserverNegociation = msg.getLongInt();
+	String webserverName  = msg.getString();
+	log("in handleLogInit with nego " +
+	    decodeNegociation(webserverNegociation) +
+            " from webserver " + webserverName);
+	
+	outBuf.reset();
+        outBuf.appendByte(JK_AJP14_LOGSEED_CMD);
+	String[] credentials = new String[1];
+	credentials[0] = getSeed();
+        outBuf.appendXBytes(getSeed().getBytes(), 0, AJP14_ENTROPY_SEED_LEN);
+	log("in handleLogInit: sent entropy " + getSeed());
+        outBuf.end();
+        ch.send(outBuf);
+	return 304;
+    }
+    
+    /**
+     * Handle the Second Phase of Login (accreditation)
+     * 
+     * Get the MD5 digest of entropy + secret password
+     * If the authentification is valid send back LogOk
+     * If the authentification failed send back LogNok
+     */
+    private int handleLogComp( Ajp13 ch, Ajp13Packet msg,
+			       Ajp13Packet outBuf )
+	throws IOException
+    {
+	// log("in handleLogComp :");
+	
+	byte [] rdigest = new byte[AJP14_ENTROPY_SEED_LEN];
+	
+	if (msg.getXBytes(rdigest, AJP14_ENTROPY_SEED_LEN) < 0)
+	    return 200;
+	
+	String[] credentials = new String[2];
+	credentials[0] = getSeed();
+	credentials[1] = getPassword();
+	String computed = digest(credentials, "md5");
+	String received = new String(rdigest);
+	
+	// XXX temp workaround, to test the rest of the connector.
+	
+	if ( ! computed.equalsIgnoreCase(received)) {
+	    log("in handleLogComp : authentification failure received=" +
+		received + " awaited=" + computed);
+	}
+	
+	if (false ) { // ! computed.equalsIgnoreCase(received)) {
+	    log("in handleLogComp : authentification failure received=" +
+		received + " awaited=" + computed);
+	    
+	    // we should have here a security mecanism which could maintain
+	    // a list of remote IP which failed too many times
+	    // so we could reject them quickly at next connect
+	    outBuf.reset();
+	    outBuf.appendByte(JK_AJP14_LOGNOK_CMD);
+	    outBuf.appendLongInt(AJP14_BAD_KEY_ERR);
+	    outBuf.end();
+	    ch.send(outBuf);
+	    return 200;
+	} else {
+            // logged we can go process requests
+	    channel.setLogged(true);
+	    outBuf.reset();
+	    outBuf.appendByte(JK_AJP14_LOGOK_CMD);
+	    outBuf.appendLongInt(getProtocolFlags(webserverNegociation));
+	    outBuf.appendString( containerSignature );
+	    outBuf.end(); 
+	    ch.send(outBuf);
+	}
+	
+	return (304);
+    }
+
+    private int handleContextQuery( Ajp13 ch, Ajp13Packet msg,
+				    Ajp13Packet outBuf )
+	throws IOException
+    {
+	log("in handleContextQuery :");
+    String virtualHost = msg.getString();
+    log("in handleContextQuery for virtual" + virtualHost); 
+
+    outBuf.reset();
+    outBuf.appendByte(JK_AJP14_CONTEXT_INFO_CMD);
+    outBuf.appendString( virtualHost );
+
+    log("in handleContextQuery for virtual " + virtualHost +
+        "examples URI/MIMES");
+    outBuf.appendString("examples");    // first context - examples
+    outBuf.appendString("servlet/*");   // examples/servlet/*
+    outBuf.appendString("*.jsp");       // examples/*.jsp
+    outBuf.appendString("");            // no more URI/MIMES
+
+    log("in handleContextQuery for virtual " + virtualHost +
+        "send admin URI/MIMES"); 
+    outBuf.appendString("admin");       // second context - admin
+    outBuf.appendString("servlet/*");   // /admin//servlet/*
+    outBuf.appendString("*.jsp");       // /admin/*.jsp
+    outBuf.appendString("");            // no more URI/MIMES
+
+    outBuf.appendString("");            // no more contexts
+    outBuf.end();
+    ch.send(outBuf);
+    
+	return (304);
+    }
+    
+    private int handleStatus( Ajp13Packet msg, Ajp13Packet outBuf )
+        throws IOException
+    {
+	log("in handleStatus :");
+	return (304);
+    }
+
+    private int handleShutdown( Ajp13Packet msg, Ajp13Packet outBuf )
+        throws IOException
+    {
+	log("in handleShutdown :");
+	return (304);
+    }
+    
+    private int handleContextState( Ajp13Packet msg , Ajp13Packet outBuf)
+        throws IOException
+    {
+	log("in handleContextState :");
+	return (304);
+    }
+    
+    private int handleUnknowPacket( Ajp13Packet msg, Ajp13Packet outBuf )
+        throws IOException
+    {
+	log("in handleUnknowPacket :");
+	return (304);
+    }
+
+    // -------------------- Utils --------------------
+
+    /**
+     * Compute the Protocol Negociation Flags
+     * 
+     * Depending the protocol fatures implemented on servet-engine,
+     * we'll drop requested features which could be asked by web-server
+     *
+     * Hopefully this functions could be overrided by decendants
+     */
+    private int getProtocolFlags(int wanted)
+    {
+                    // no real-time context update
+	wanted &= ~(AJP14_CONTEXT_UPDATE_NEG |
+                    // no gzip compression yet
+		    AJP14_GZIP_STREAM_NEG    | 
+                     // no DES56 cyphering yet
+		    AJP14_DES56_STREAM_NEG   |
+                    // no Extended info on server SSL vars yet
+		    AJP14_SSL_VSERVER_NEG    |
+                    // no Extended info on client SSL vars yet
+		    AJP14_SSL_VCLIENT_NEG    |
+                    // no Extended info on crypto SSL vars yet
+		    AJP14_SSL_VCRYPTO_NEG    |
+                    // no Extended info on misc SSL vars yet
+		    AJP14_SSL_VMISC_NEG	     |
+                    // Reset AJP protocol mask
+		    AJP14_PROTO_SUPPORT_AJPXX_NEG);
+        
+	// Only strict AJP14 supported
+	return (wanted | AJP14_PROTO_SUPPORT_AJP14_NEG);	
+    }
+
+    /**
+     * Compute a digest (MD5 in AJP14) for an array of String
+     */
+    public final String digest(String[] credentials, String algorithm) {
+        try {
+            // Obtain a new message digest with MD5 encryption
+            MessageDigest md =
+                (MessageDigest)MessageDigest.getInstance(algorithm).clone();
+            // encode the credentials items
+	    for (int i = 0; i < credentials.length; i++) {
+		if( debug > 0 )
+                    log("Credentials : " + i + " " + credentials[i]);
+		if( credentials[i] != null  )
+		    md.update(credentials[i].getBytes());
+	    }
+            // obtain the byte array from the digest
+            byte[] dig = md.digest();
+	    return HexUtils.convert(dig);
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            return null;
+        }
+    }
+
+    // -------------------- Debugging --------------------
+    // Very usefull for develoment
+
+    /**
+     * Display Negociation field in human form 
+     */
+    private String decodeNegociation(int nego)
+    {
+	StringBuffer buf = new StringBuffer(128);
+	
+	if ((nego & AJP14_CONTEXT_INFO_NEG) != 0) 
+	    buf.append(" CONTEXT-INFO");
+	
+	if ((nego & AJP14_CONTEXT_UPDATE_NEG) != 0)
+	    buf.append(" CONTEXT-UPDATE");
+	
+	if ((nego & AJP14_GZIP_STREAM_NEG) != 0)
+	    buf.append(" GZIP-STREAM");
+	
+	if ((nego & AJP14_DES56_STREAM_NEG) != 0)
+	    buf.append(" DES56-STREAM");
+	
+	if ((nego & AJP14_SSL_VSERVER_NEG) != 0)
+	    buf.append(" SSL-VSERVER");
+	
+	if ((nego & AJP14_SSL_VCLIENT_NEG) != 0)
+	    buf.append(" SSL-VCLIENT");
+	
+	if ((nego & AJP14_SSL_VCRYPTO_NEG) != 0)
+	    buf.append(" SSL-VCRYPTO");
+	
+	if ((nego & AJP14_SSL_VMISC_NEG) != 0)
+	    buf.append(" SSL-VMISC");
+	
+	if ((nego & AJP14_PROTO_SUPPORT_AJP14_NEG) != 0)
+	    buf.append(" AJP14");
+	
+	if ((nego & AJP14_PROTO_SUPPORT_AJP15_NEG) != 0)
+	    buf.append(" AJP15");
+	
+	if ((nego & AJP14_PROTO_SUPPORT_AJP16_NEG) != 0)
+	    buf.append(" AJP16");
+	
+	return (buf.toString());
+    }
+    
+    private static int debug=10;
+    void log(String s) {
+	System.out.println("Ajp14Negotiation: " + s );
+    }
+ }
diff --git a/connectors/jk/java/org/apache/ajp/RequestHandler.java b/connectors/jk/java/org/apache/ajp/RequestHandler.java
new file mode 100644
index 0000000..226b3e2
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/RequestHandler.java
@@ -0,0 +1,806 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.BaseRequest;
+import org.apache.tomcat.util.http.HttpMessages;
+import org.apache.tomcat.util.http.MimeHeaders;
+
+
+/**
+ * Handle messages related with basic request information.
+ *
+ * This object can handle the following incoming messages:
+ * - "FORWARD_REQUEST" input message ( sent when a request is passed from the web server )
+ * - "PING REQUEST" input message (sent by the web server to determine if tomcat is not frozen,
+ *                                 a PONG REPLY will be sent back)
+ * - "RECEIVE_BODY_CHUNK" input ( sent by container to pass more body, in response to GET_BODY_CHUNK )
+ *
+ * It can handle the following outgoing messages:
+ * - SEND_HEADERS. Pass the status code and headers.
+ * - SEND_BODY_CHUNK. Send a chunk of body
+ * - GET_BODY_CHUNK. Request a chunk of body data
+ * - END_RESPONSE. Notify the end of a request processing.
+ *
+ * @author Henri Gomez [hgomez@apache.org]
+ * @author Dan Milstein [danmil@shore.net]
+ * @author Keith Wannamaker [Keith@Wannamaker.org]
+ * @author Costin Manolache
+ */
+public class RequestHandler extends AjpHandler
+{
+    // XXX Will move to a registry system.
+    
+    // Prefix codes for message types from server to container
+	public static final byte JK_AJP13_FORWARD_REQUEST   = 2;
+	public static final byte JK_AJP13_SHUTDOWN          = 7;
+	public static final byte JK_AJP13_PING_REQUEST      = 8;    
+	public static final byte JK_AJP13_CPING_REQUEST     = 10;    
+
+    // Prefix codes for message types from container to server
+    public static final byte JK_AJP13_SEND_BODY_CHUNK   = 3;
+    public static final byte JK_AJP13_SEND_HEADERS      = 4;
+    public static final byte JK_AJP13_END_RESPONSE      = 5;
+	public static final byte JK_AJP13_GET_BODY_CHUNK    = 6;
+	public static final byte JK_AJP13_CPONG_REPLY       = 9;
+	
+    // Integer codes for common response header strings
+    public static final int SC_RESP_CONTENT_TYPE        = 0xA001;
+    public static final int SC_RESP_CONTENT_LANGUAGE    = 0xA002;
+    public static final int SC_RESP_CONTENT_LENGTH      = 0xA003;
+    public static final int SC_RESP_DATE                = 0xA004;
+    public static final int SC_RESP_LAST_MODIFIED       = 0xA005;
+    public static final int SC_RESP_LOCATION            = 0xA006;
+    public static final int SC_RESP_SET_COOKIE          = 0xA007;
+    public static final int SC_RESP_SET_COOKIE2         = 0xA008;
+    public static final int SC_RESP_SERVLET_ENGINE      = 0xA009;
+    public static final int SC_RESP_STATUS              = 0xA00A;
+    public static final int SC_RESP_WWW_AUTHENTICATE    = 0xA00B;
+	
+    // Integer codes for common (optional) request attribute names
+    public static final byte SC_A_CONTEXT       = 1;  // XXX Unused
+    public static final byte SC_A_SERVLET_PATH  = 2;  // XXX Unused
+    public static final byte SC_A_REMOTE_USER   = 3;
+    public static final byte SC_A_AUTH_TYPE     = 4;
+    public static final byte SC_A_QUERY_STRING  = 5;
+    public static final byte SC_A_JVM_ROUTE     = 6;
+    public static final byte SC_A_SSL_CERT      = 7;
+    public static final byte SC_A_SSL_CIPHER    = 8;
+    public static final byte SC_A_SSL_SESSION   = 9;
+    public static final byte SC_A_SSL_KEY_SIZE  = 11; // ajp14 originally, now in ajp13 with jk 1.2/2.0
+    public static final byte SC_A_SECRET        = 12;
+    public static final byte SC_A_STORED_METHOD = 13;
+
+    // Used for attributes which are not in the list above
+    public static final byte SC_A_REQ_ATTRIBUTE = 10; 
+
+    // Terminates list of attributes
+    public static final byte SC_A_ARE_DONE      = (byte)0xFF;
+    
+    // Translates integer codes to names of HTTP methods
+    public static final String []methodTransArray = {
+        "OPTIONS",
+        "GET",
+        "HEAD",
+        "POST",
+        "PUT",
+        "DELETE",
+        "TRACE",
+        "PROPFIND",
+        "PROPPATCH",
+        "MKCOL",
+        "COPY",
+        "MOVE",
+        "LOCK",
+        "UNLOCK",
+        "ACL",
+        "REPORT",
+        "VERSION-CONTROL",
+        "CHECKIN",
+        "CHECKOUT",
+        "UNCHECKOUT",
+        "SEARCH",
+        "MKWORKSPACE",
+        "UPDATE",
+        "LABEL",
+        "MERGE",
+        "BASELINE-CONTROL",
+        "MKACTIVITY"
+    };
+    public static final int SC_M_JK_STORED = (byte) 0xFF;
+
+    
+    // id's for common request headers
+    public static final int SC_REQ_ACCEPT          = 1;
+    public static final int SC_REQ_ACCEPT_CHARSET  = 2;
+    public static final int SC_REQ_ACCEPT_ENCODING = 3;
+    public static final int SC_REQ_ACCEPT_LANGUAGE = 4;
+    public static final int SC_REQ_AUTHORIZATION   = 5;
+    public static final int SC_REQ_CONNECTION      = 6;
+    public static final int SC_REQ_CONTENT_TYPE    = 7;
+    public static final int SC_REQ_CONTENT_LENGTH  = 8;
+    public static final int SC_REQ_COOKIE          = 9;
+    public static final int SC_REQ_COOKIE2         = 10;
+    public static final int SC_REQ_HOST            = 11;
+    public static final int SC_REQ_PRAGMA          = 12;
+    public static final int SC_REQ_REFERER         = 13;
+    public static final int SC_REQ_USER_AGENT      = 14;
+
+    // Translates integer codes to request header names    
+    public static final String []headerTransArray = {
+        "accept",
+        "accept-charset",
+        "accept-encoding",
+        "accept-language",
+        "authorization",
+        "connection",
+        "content-type",
+        "content-length",
+        "cookie",
+        "cookie2",
+        "host",
+        "pragma",
+        "referer",
+        "user-agent"
+    };
+
+    public RequestHandler() 
+    {
+    }
+
+    public void init( Ajp13 ajp14 ) {
+	// register incoming message handlers
+	ajp14.registerMessageType( JK_AJP13_FORWARD_REQUEST,
+				   "JK_AJP13_FORWARD_REQUEST",
+				   this, null); // 2
+	// register outgoing messages handler
+	ajp14.registerMessageType( JK_AJP13_SEND_BODY_CHUNK, // 3
+				   "JK_AJP13_SEND_BODY_CHUNK",
+				   this,null );
+	ajp14.registerMessageType( JK_AJP13_SEND_HEADERS,  // 4
+				   "JK_AJP13_SEND_HEADERS",
+				   this,null );
+	ajp14.registerMessageType( JK_AJP13_END_RESPONSE, // 5
+				   "JK_AJP13_END_RESPONSE",
+				   this,null );
+	ajp14.registerMessageType( JK_AJP13_GET_BODY_CHUNK, // 6
+				   "JK_AJP13_GET_BODY_CHUNK",
+				   this, null );
+	ajp14.registerMessageType( JK_AJP13_CPING_REQUEST,
+				   "JK_AJP13_PING_REQUEST",
+				   this, null); // 10
+	ajp14.registerMessageType( JK_AJP13_CPONG_REPLY,
+				   "JK_AJP13_PONG_REPLY",
+				   this, null); // 9
+    }
+    
+    /**
+     * Send a CPONG REPLY to web server to its CPING request
+     * 
+     * @param ch the Ajp13 channel
+     * @param outBuf the Ajp13Packet output packet to use
+     */
+    public int sendCPong(Ajp13 ch, Ajp13Packet outBuf)
+    {
+		outBuf.reset();
+		outBuf.appendByte(JK_AJP13_CPONG_REPLY);
+    	
+    	try
+    	{
+			ch.send(outBuf);
+    	}
+    	catch (IOException ioe)
+    	{
+    		log("can't send pong reply");
+    	}
+    	
+    	return (999);	// success but no need to process farther
+    }
+    
+    // -------------------- Incoming message --------------------
+    public int handleAjpMessage( int type, Ajp13 channel,
+				 Ajp13Packet ajp, BaseRequest req )
+	throws IOException
+    {
+	switch( type ) {
+	case RequestHandler.JK_AJP13_FORWARD_REQUEST:
+	    return decodeRequest(channel, channel.hBuf, req );
+		
+	default:
+	    return UNKNOWN;
+	}
+    }
+    
+    /**
+     * Parse a FORWARD_REQUEST packet from the web server and store its
+     * properties in the passed-in request object.
+     *
+     * @param req An empty (newly-recycled) request object.
+     * @param msg Holds the packet which has just been sent by the web
+     * server, with its read position just past the packet header (which in
+     * this case includes the prefix code for FORWARD_REQUEST).
+     *
+     * @return 200 in case of a successful decoduing, 500 in case of error.  
+     */
+    protected int decodeRequest(Ajp13 ch, Ajp13Packet msg, BaseRequest req)
+        throws IOException
+    {
+        
+        if (debug > 0) {
+            log("decodeRequest()");
+        }
+
+	// XXX Awful return values
+
+        boolean isSSL = false;
+
+        // Translate the HTTP method code to a String.
+        byte methodCode = msg.getByte();
+        if (methodCode != SC_M_JK_STORED)
+          req.method().setString(methodTransArray[(int)methodCode - 1]);
+
+        msg.getMessageBytes(req.protocol()); 
+        msg.getMessageBytes(req.requestURI());
+
+        msg.getMessageBytes(req.remoteAddr());
+        msg.getMessageBytes(req.remoteHost());
+        msg.getMessageBytes(req.serverName());
+        req.setServerPort(msg.getInt());
+
+	isSSL = msg.getBool();
+
+	// Decode headers
+	MimeHeaders headers = req.headers();
+	int hCount = msg.getInt();
+        for(int i = 0 ; i < hCount ; i++) {
+            String hName = null;
+
+	    // Header names are encoded as either an integer code starting
+	    // with 0xA0, or as a normal string (in which case the first
+	    // two bytes are the length).
+            int isc = msg.peekInt();
+            int hId = isc & 0xFF;
+
+	    MessageBytes vMB=null;
+            isc &= 0xFF00;
+            if(0xA000 == isc) {
+                //
+                // header name is encoded as an int
+                //
+                msg.getInt(); // To advance the read position
+                hName = headerTransArray[hId - 1];
+		vMB= headers.addValue(hName);
+                msg.getMessageBytes(vMB);
+
+                if (hId == SC_REQ_CONTENT_LENGTH) {
+                    // just read content-length header
+                    int contentLength = (vMB == null) ? -1 : vMB.getInt();
+                    req.setContentLength(contentLength);
+                } else if (hId == SC_REQ_CONTENT_TYPE) {
+                    // just read content-type header
+                    ByteChunk bchunk = vMB.getByteChunk();
+                    req.contentType().setBytes(bchunk.getBytes(),
+                                               bchunk.getOffset(),
+                                               bchunk.getLength());
+                } else if (hId == SC_REQ_AUTHORIZATION) {
+                    ByteChunk bchunk = vMB.getByteChunk();
+                    req.authorization().setBytes(bchunk.getBytes(),
+                                               bchunk.getOffset(),
+                                               bchunk.getLength());
+                }
+            } else {
+                //
+                // header name is a string
+                //
+		// XXX Not very elegant
+		vMB = msg.addHeader(headers);
+		if (vMB == null) {
+                    return 500; // wrong packet
+                }
+                msg.getMessageBytes(vMB);
+            }
+        }
+
+	byte attributeCode;
+        for(attributeCode = msg.getByte() ;
+            attributeCode != SC_A_ARE_DONE ;
+            attributeCode = msg.getByte()) {
+            switch(attributeCode) {
+	    case SC_A_CONTEXT      :
+                break;
+		
+	    case SC_A_SERVLET_PATH :
+                break;
+		
+	    case SC_A_REMOTE_USER  :
+                msg.getMessageBytes(req.remoteUser());
+                break;
+		
+	    case SC_A_AUTH_TYPE    :
+                msg.getMessageBytes(req.authType());
+                break;
+		
+	    case SC_A_QUERY_STRING :
+		msg.getMessageBytes(req.queryString());
+                break;
+		
+	    case SC_A_JVM_ROUTE    :
+                msg.getMessageBytes(req.jvmRoute());
+                break;
+		
+	    case SC_A_SSL_CERT     :
+		isSSL = true;
+                // Transform the string into certificate.
+                String certString = msg.getString();
+                byte[] certData = certString.getBytes();
+                ByteArrayInputStream bais = new ByteArrayInputStream(certData);
+ 
+                // Fill the first element.
+                X509Certificate jsseCerts[] = null;
+                try {
+                    CertificateFactory cf =
+                        CertificateFactory.getInstance("X.509");
+                    X509Certificate cert = (X509Certificate)
+                        cf.generateCertificate(bais);
+                    jsseCerts =  new X509Certificate[1];
+                    jsseCerts[0] = cert;
+                } catch(java.security.cert.CertificateException e) {
+                    log("Certificate convertion failed" + e );
+                }
+ 
+                req.setAttribute("javax.servlet.request.X509Certificate",
+                                 jsseCerts);
+                break;
+		
+	    case SC_A_SSL_CIPHER   :
+		isSSL = true;
+		req.setAttribute("javax.servlet.request.cipher_suite",
+				 msg.getString());
+                break;
+		
+	    case SC_A_SECRET   :
+                // If a request has a secret attribute, set it on
+                // channel - it'll be visible to the caller ( Interceptor,
+                // Connector ) and it can check it against its settings before
+                // trusting us.
+                String secret=msg.getString();
+                if(secret!=null) {
+                    ch.setSecret( secret );
+                }
+                break;
+		
+	    case SC_A_SSL_SESSION  :
+		isSSL = true;
+		req.setAttribute("javax.servlet.request.ssl_session",
+				  msg.getString());
+                break;
+		
+	    case SC_A_REQ_ATTRIBUTE :
+		req.setAttribute(msg.getString(), 
+				 msg.getString());
+                break;
+
+	    case SC_A_SSL_KEY_SIZE: // Ajp13 !
+                isSSL = true;
+		req.setAttribute("javax.servlet.request.key_size",
+				 Integer.toString(msg.getInt()));
+		break;
+    
+        case SC_A_STORED_METHOD:
+                req.method().setString(msg.getString());
+                break;
+    
+	    default:
+                // Ignore. Assume a single-string value - we shouldn't
+                // allow anything else.
+                msg.getString();
+                break;
+	    }
+        }
+
+        if(isSSL) {
+            req.setScheme(req.SCHEME_HTTPS);
+            req.setSecure(true);
+        }
+
+        // set cookies on request now that we have all headers
+        req.cookies().setHeaders(req.headers());
+
+	// Check to see if there should be a body packet coming along
+	// immediately after
+    	if(req.getContentLength() > 0) {
+
+	    /* Read present data */
+	    int err = ch.receive(ch.inBuf);
+            if(err < 0) {
+            	return 500;
+	    }
+	    
+	    ch.blen = ch.inBuf.peekInt();
+	    ch.pos = 0;
+	    ch.inBuf.getBytes(ch.bodyBuff);
+    	}
+    
+        if (debug > 5) {
+            log(req.toString());
+        }
+
+        return 200; // Success
+    }
+    
+
+    // -------------------- Messages from container to server ------------------
+    
+    /**
+     * Send the HTTP headers back to the web server and on to the browser.
+     *
+     * @param status The HTTP status code to send.
+     * @param statusMessage the HTTP status message to send.
+     * @param headers The set of all headers.
+     */
+    public void sendHeaders(Ajp13 ch, Ajp13Packet outBuf,
+			    int status, String statusMessage,
+                            MimeHeaders headers)
+        throws IOException
+    {
+	// XXX if more headers that MAX_SIZE, send 2 packets!
+	if( statusMessage==null ) statusMessage=HttpMessages.getMessage(status);
+	outBuf.reset();
+        outBuf.appendByte(JK_AJP13_SEND_HEADERS);
+        outBuf.appendInt(status);
+	
+	outBuf.appendString(statusMessage);
+        
+	int numHeaders = headers.size();
+        outBuf.appendInt(numHeaders);
+        
+	for( int i=0 ; i < numHeaders ; i++ ) {
+	    String headerName = headers.getName(i).toString();
+	    int sc = headerNameToSc(headerName);
+            if(-1 != sc) {
+                outBuf.appendInt(sc);
+            } else {
+                outBuf.appendString(headerName);
+            }
+            outBuf.appendString(headers.getValue(i).toString() );
+        }
+
+        outBuf.end();
+        ch.send(outBuf);
+    } 
+
+
+    /**
+     * Signal the web server that the servlet has finished handling this
+     * request, and that the connection can be reused.
+     */
+    public void finish(Ajp13 ch, Ajp13Packet outBuf) throws IOException {
+        if (debug > 0)  log("finish()");
+
+	outBuf.reset();
+        outBuf.appendByte(JK_AJP13_END_RESPONSE);
+        outBuf.appendBool(true); // Reuse this connection
+        outBuf.end();
+        ch.send(outBuf);
+    }
+
+    /**
+     * Send a chunk of response body data to the web server and on to the
+     * browser.
+     *
+     * @param b A huffer of bytes to send.
+     * @param off The offset into the buffer from which to start sending.
+     * @param len The number of bytes to send.
+     */    
+    public void doWrite(Ajp13 ch, Ajp13Packet outBuf,
+			byte b[], int off, int len)
+	throws IOException
+    {
+        if (debug > 0) log("doWrite(byte[], " + off + ", " + len + ")");
+
+	int sent = 0;
+	while(sent < len) {
+	    int to_send = len - sent;
+	    to_send = to_send > Ajp13.MAX_SEND_SIZE ? Ajp13.MAX_SEND_SIZE : to_send;
+
+	    outBuf.reset();
+	    outBuf.appendByte(JK_AJP13_SEND_BODY_CHUNK);	        	
+	    outBuf.appendBytes(b, off + sent, to_send);	        
+	    ch.send(outBuf);
+	    sent += to_send;
+	}
+    }
+
+    // -------------------- Utils -------------------- 
+
+    /**
+     * Translate an HTTP response header name to an integer code if
+     * possible.  Case is ignored.
+     * 
+     * @param name The name of the response header to translate.
+     *
+     * @return The code for that header name, or -1 if no code exists.
+     */
+    protected int headerNameToSc(String name)
+    {       
+        switch(name.charAt(0)) {
+	case 'c':
+	case 'C':
+	    if(name.equalsIgnoreCase("Content-Type")) {
+		return SC_RESP_CONTENT_TYPE;
+	    } else if(name.equalsIgnoreCase("Content-Language")) {
+		return SC_RESP_CONTENT_LANGUAGE;
+	    } else if(name.equalsIgnoreCase("Content-Length")) {
+		return SC_RESP_CONTENT_LENGTH;
+	    }
+            break;
+            
+	case 'd':
+	case 'D':
+	    if(name.equalsIgnoreCase("Date")) {
+                return SC_RESP_DATE;
+	    }
+            break;
+            
+	case 'l':
+	case 'L':
+	    if(name.equalsIgnoreCase("Last-Modified")) {
+		return SC_RESP_LAST_MODIFIED;
+	    } else if(name.equalsIgnoreCase("Location")) {
+		return SC_RESP_LOCATION;
+	    }
+            break;
+
+	case 's':
+	case 'S':
+	    if(name.equalsIgnoreCase("Set-Cookie")) {
+		return SC_RESP_SET_COOKIE;
+	    } else if(name.equalsIgnoreCase("Set-Cookie2")) {
+		return SC_RESP_SET_COOKIE2;
+	    }
+            break;
+            
+	case 'w':
+	case 'W':
+	    if(name.equalsIgnoreCase("WWW-Authenticate")) {
+		return SC_RESP_WWW_AUTHENTICATE;
+	    }
+            break;          
+        }
+        
+        return -1;
+    }
+   
+    private int debug=0;
+    private Logger logger = new Logger();
+    
+    public void setDebug(int debug) {
+        this.debug = debug;
+    }
+
+    public void setLogger(Logger l) {
+        this.logger = l;
+    }
+    
+    void log(String s) {
+        logger.log("[RequestHandler] " + s );
+    }
+
+    // ==================== Servlet Input Support =================
+    // XXX DEPRECATED
+    
+    public int available(Ajp13 ch) throws IOException {
+        if (debug > 0) {
+            log("available()");
+        }
+
+        if (ch.pos >= ch.blen) {
+            if( ! refillReadBuffer(ch)) {
+		return 0;
+	    }
+        }
+        return ch.blen - ch.pos;
+    }
+
+    /**
+     * Return the next byte of request body data (to a servlet).
+     */
+    public int doRead(Ajp13 ch) throws IOException 
+    {
+        if (debug > 0) {
+            log("doRead()");
+        }
+
+        if(ch.pos >= ch.blen) {
+            if( ! refillReadBuffer(ch)) {
+		return -1;
+	    }
+        }
+        return ch.bodyBuff[ch.pos++] & 0xFF;
+    }
+    
+    /**
+     * Store a chunk of request data into the passed-in byte buffer.
+     *
+     * @param b A buffer to fill with data from the request.
+     * @param off The offset in the buffer at which to start filling.
+     * @param len The number of bytes to copy into the buffer.
+     *
+     * @return The number of bytes actually copied into the buffer, or -1
+     * if the end of the stream has been reached.
+     */
+    public int doRead(Ajp13 ch, byte[] b, int off, int len) throws IOException 
+    {
+        if (debug > 0) {
+            log("doRead(byte[], int, int)");
+        }
+
+	if(ch.pos >= ch.blen) {
+	    if( ! refillReadBuffer(ch)) {
+		return -1;
+	    }
+	}
+
+	if(ch.pos + len <= ch.blen) { // Fear the off by one error
+	    // Sanity check b.length > off + len?
+	    System.arraycopy(ch.bodyBuff, ch.pos, b, off, len);
+	    ch.pos += len;
+	    return len;
+	}
+
+	// Not enough data (blen < pos + len)
+	int toCopy = len;
+	while(toCopy > 0) {
+	    int bytesRemaining = ch.blen - ch.pos;
+	    if(bytesRemaining < 0) 
+		bytesRemaining = 0;
+	    int c = bytesRemaining < toCopy ? bytesRemaining : toCopy;
+            
+	    System.arraycopy(ch.bodyBuff, ch.pos, b, off, c);
+
+	    toCopy    -= c;
+
+	    off       += c;
+	    ch.pos       += c; // In case we exactly consume the buffer
+
+	    if(toCopy > 0) 
+		if( ! refillReadBuffer(ch)) { // Resets blen and pos
+		    break;
+		}
+	}
+
+	return len - toCopy;
+    }
+    
+    /**
+     * Get more request body data from the web server and store it in the 
+     * internal buffer.
+     *
+     * @return true if there is more data, false if not.    
+     */
+    public boolean refillReadBuffer(Ajp13 ch) throws IOException 
+    {
+        if (debug > 0) {
+            log("refillReadBuffer()");
+        }
+
+	// If the server returns an empty packet, assume that that end of
+	// the stream has been reached (yuck -- fix protocol??).
+
+	// Why not use outBuf??
+	ch.inBuf.reset();
+	ch.inBuf.appendByte(JK_AJP13_GET_BODY_CHUNK);
+	ch.inBuf.appendInt(Ajp13.MAX_READ_SIZE);
+	ch.send(ch.inBuf);
+	
+	int err = ch.receive(ch.inBuf);
+        if(err < 0) {
+	    throw new IOException();
+	}
+	
+        // check for empty packet, which means end of stream
+        if (ch.inBuf.getLen() == 0) {
+            if (debug > 0) {
+                log("refillReadBuffer():  "
+                    + "received empty packet -> end of stream");
+            }
+            ch.blen = 0;
+            ch.pos = 0;
+            return false;
+        }
+
+    	ch.blen = ch.inBuf.peekInt();
+    	ch.pos = 0;
+    	ch.inBuf.getBytes(ch.bodyBuff);
+
+	return (ch.blen > 0);
+    }    
+
+    // ==================== Servlet Output Support =================
+    
+    /**
+     */
+    public void beginSendHeaders(Ajp13 ch, Ajp13Packet outBuf,
+				 int status,
+                                 String statusMessage,
+                                 int numHeaders) throws IOException {
+
+        if (debug > 0) {
+            log("sendHeaders()");
+        }
+
+	// XXX if more headers that MAX_SIZE, send 2 packets!
+
+	outBuf.reset();
+        outBuf.appendByte(JK_AJP13_SEND_HEADERS);
+
+        if (debug > 0) {
+            log("status is:  " + status +
+                       "(" + statusMessage + ")");
+        }
+
+        // set status code and message
+        outBuf.appendInt(status);
+        outBuf.appendString(statusMessage);
+
+        // write the number of headers...
+        outBuf.appendInt(numHeaders);
+    }
+
+    public void sendHeader(Ajp13Packet outBuf,
+			   String name, String value)
+	throws IOException
+    {
+        int sc = headerNameToSc(name);
+        if(-1 != sc) {
+            outBuf.appendInt(sc);
+        } else {
+            outBuf.appendString(name);
+        }
+        outBuf.appendString(value);
+    }
+
+    public void endSendHeaders(Ajp13 ch, Ajp13Packet outBuf)
+	throws IOException
+    {
+        outBuf.end();
+        ch.send(outBuf);
+    }
+
+    /**
+     * Send the HTTP headers back to the web server and on to the browser.
+     *
+     * @param status The HTTP status code to send.
+     * @param headers The set of all headers.
+     */
+    public void sendHeaders(Ajp13 ch, Ajp13Packet outBuf,
+			    int status, MimeHeaders headers)
+        throws IOException
+    {
+        sendHeaders(ch, outBuf, status, HttpMessages.getMessage(status),
+                    headers);
+    }
+    
+
+ }
diff --git a/connectors/jk/java/org/apache/ajp/tomcat33/Ajp14Interceptor.java b/connectors/jk/java/org/apache/ajp/tomcat33/Ajp14Interceptor.java
new file mode 100644
index 0000000..d93a786
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat33/Ajp14Interceptor.java
@@ -0,0 +1,460 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp.tomcat33;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+
+import org.apache.ajp.Ajp13;
+import org.apache.ajp.NegociationHandler;
+import org.apache.ajp.RequestHandler;
+import org.apache.tomcat.core.Context;
+import org.apache.tomcat.core.ContextManager;
+import org.apache.tomcat.core.Request;
+import org.apache.tomcat.core.Response;
+import org.apache.tomcat.core.TomcatException;
+import org.apache.tomcat.modules.server.PoolTcpConnector;
+import org.apache.tomcat.util.buf.UDecoder;
+import org.apache.tomcat.util.http.BaseRequest;
+import org.apache.tomcat.util.http.Cookies;
+import org.apache.tomcat.util.http.HttpMessages;
+import org.apache.tomcat.util.net.TcpConnection;
+import org.apache.tomcat.util.net.TcpConnectionHandler;
+
+/** Note. PoolTcpConnector is a convenience base class used for
+    TCP-based connectors in tomcat33. It allows all those modules
+    to share the thread pool and listener code.
+
+    In future it's likely other optimizations will be implemented in
+    the PoolTcpConnector, so it's better to use it if you don't have
+    a good reason not to ( like a connector for J2ME, where you want
+    minimal footprint and don't care about high load )
+*/
+
+/** Tomcat 33 module implementing the Ajp14 protocol.
+ *
+ *  The actual protocol implementation is in Ajp14.java, this is just an
+ *  adapter to plug it into tomcat.
+ */
+public class Ajp14Interceptor extends PoolTcpConnector
+    implements  TcpConnectionHandler
+{
+    int ajp14_note=-1;
+    String password;
+    RequestHandler reqHandler=new RequestHandler();
+    NegociationHandler negHandler=new NegociationHandler();
+    
+    public Ajp14Interceptor()
+    {
+        super();
+ 	super.setSoLinger( 100 );
+	super.setTcpNoDelay( true );
+    }
+
+    // initialization
+    public void engineInit(ContextManager cm) throws TomcatException {
+	log("engineInit");
+    }
+
+    public void engineStart(ContextManager cm) throws TomcatException {
+	super.engineInit( cm );
+	ajp14_note=cm.getNoteId( ContextManager.REQUEST_NOTE, "ajp14" );
+	super.engineStart(cm);
+   }
+
+    
+    // -------------------- Ajp14 specific parameters --------------------
+
+    public void setPassword( String s ) {
+	this.password=s;
+    }
+
+    /**
+     * Set the original entropy seed
+     */
+    public void setSeed(String pseed) 
+    {
+	negHandler.setSeed( pseed );
+    }
+    
+    // -------------------- PoolTcpConnector --------------------
+
+    /** Called by PoolTcpConnector to allow childs to init.
+     */
+    protected void localInit() throws Exception {
+	ep.setConnectionHandler( this );
+    }
+
+    // -------------------- Handler implementation --------------------
+
+    /*  The TcpConnectionHandler interface is used by the PoolTcpConnector to
+     *  handle incoming connections.
+     */
+
+    /** Called by the thread pool when a new thread is added to the pool,
+	in order to create the (expensive) objects that will be stored
+	as thread data.
+	XXX we should use a single object, not array ( several reasons ),
+	XXX Ajp14 should be storead as a request note, to be available in
+	all modules
+    */
+    public Object[] init()
+    {
+	if( debug > 0 ) log("Init ");
+        Object thData[]=new Object[1];
+	thData[0]=initRequest( null );
+	return thData;
+    }
+
+    /** Construct the request object, with probably unnecesary
+	sanity tests ( should work without thread pool - but that is
+	not supported in PoolTcpConnector, maybe in future )
+    */
+    private Ajp14Request initRequest(Object thData[] ) {
+	if( ajp14_note < 0 ) throw new RuntimeException( "assert: ajp14_note>0" );
+	Ajp14Request req=null;
+	if( thData != null ) {
+	    req=(Ajp14Request)thData[0];
+	}
+	if( req != null ) {
+	    Response res=req.getResponse();
+	    req.recycle();
+	    res.recycle();
+	    // make the note available to other modules
+	    req.setNote( ajp14_note, req.ajp13);
+	    return req;
+	}
+	// either thData==null or broken ( req==null)
+       	Ajp13 ajp13=new Ajp13(reqHandler);
+        negHandler.init( ajp13 );
+
+	negHandler.setContainerSignature( ContextManager.TOMCAT_NAME +
+                                          " v" + ContextManager.TOMCAT_VERSION);
+	if( password!= null ) {
+            negHandler.setPassword( password );
+            ajp13.setBackward(false); 
+        }
+
+	BaseRequest ajpreq=new BaseRequest();
+
+	req=new Ajp14Request(ajp13, ajpreq);
+	Ajp14Response res=new Ajp14Response(ajp13);
+	cm.initRequest(req, res);
+	return  req;
+    }
+    
+    /** Called whenever a new TCP connection is received. The connection
+	is reused.
+     */
+    public void processConnection(TcpConnection connection, Object thData[])
+    {
+        try {
+	    if( debug>0)
+		log( "Received ajp14 connection ");
+            Socket socket = connection.getSocket();
+	    // assert: socket!=null, connection!=null ( checked by PoolTcpEndpoint )
+	    
+            socket.setSoLinger( true, 100);
+
+            Ajp14Request req=initRequest( thData );
+            Ajp14Response res= (Ajp14Response)req.getResponse();
+            Ajp13 ajp13=req.ajp13;
+	    BaseRequest ajpReq=req.ajpReq;
+
+            ajp13.setSocket(socket);
+
+	    // first request should be the loginit.
+	    int status=ajp13.receiveNextRequest( ajpReq );
+	    if( status != 304 )  { // XXX use better codes
+		log( "Failure in logInit ");
+		return;
+	    }
+
+	    status=ajp13.receiveNextRequest( ajpReq );
+	    if( status != 304 ) { // XXX use better codes
+		log( "Failure in login ");
+		return;
+	    }
+	    
+            boolean moreRequests = true;
+            while(moreRequests) {
+		status=ajp13.receiveNextRequest( ajpReq );
+
+		if( status==-2) {
+		    // special case - shutdown
+		    // XXX need better communication, refactor it
+		    if( !doShutdown(socket.getLocalAddress(),
+				    socket.getInetAddress())) {
+			moreRequests = false;
+			continue;
+		    }                        
+		}
+		
+		// 999 low level requests are just ignored (ie cping/cpong)
+		if( status  == 200)
+		    cm.service(req, res);
+		else if (status == 500) {
+		    log( "Invalid request received " + req );
+		    break;
+		}
+		
+		req.recycle();
+		res.recycle();
+            }
+            if( debug > 0 ) log("Closing ajp14 connection");
+            ajp13.close();
+	    socket.close();
+        } catch (Exception e) {
+	    log("Processing connection " + connection, e);
+        }
+    }
+
+    // We don't need to check isSameAddress if we authenticate !!!
+    protected boolean doShutdown(InetAddress serverAddr,
+                                 InetAddress clientAddr)
+    {
+        try {
+	    // close the socket connection before handling any signal
+	    // but get the addresses first so they are not corrupted			
+            if(isSameAddress(serverAddr, clientAddr)) {
+		cm.stop();
+		// same behavior as in past, because it seems that
+		// stopping everything doesn't work - need to figure
+		// out what happens with the threads ( XXX )
+
+		// XXX It should work now - but will fail if servlets create
+		// threads
+		System.exit(0);
+	    }
+	} catch(Exception ignored) {
+	    log("Ignored " + ignored);
+	}
+	log("Shutdown command ignored");
+	return false;
+    }
+
+    // legacy, should be removed 
+    public void setServer(Object contextM)
+    {
+        this.cm=(ContextManager)contextM;
+    }
+
+    public Object getInfo( Context ctx, Request request,
+			   int id, String key ) {
+	if( ! ( request instanceof Ajp14Request ) ) {
+	    return null;
+	}
+	Ajp14Request ajp14req = (Ajp14Request)request;
+	return ajp14req.ajpReq.getAttribute( key );
+    }
+    public int setInfo( Context ctx, Request request,
+			int id, String key, Object obj ) {
+	if( ! ( request instanceof Ajp14Request ) ) {
+	    return DECLINED;
+	}
+	Ajp14Request ajp14req = (Ajp14Request)request;
+	ajp14req.ajpReq.setAttribute(key, obj);
+	return OK;
+    }
+		
+
+
+}
+
+
+//-------------------- Glue code for request/response.
+// Probably not needed ( or can be simplified ), but it's
+// not that bad.
+
+class Ajp14Request extends Request 
+{
+    Ajp13 ajp13;
+    BaseRequest ajpReq;
+    
+    public Ajp14Request(Ajp13 ajp13, BaseRequest ajpReq) 
+    {
+	headers = ajpReq.headers();
+	methodMB=ajpReq.method();
+	protoMB=ajpReq.protocol();
+	uriMB = ajpReq.requestURI();
+	queryMB = ajpReq.queryString();
+	remoteAddrMB = ajpReq.remoteAddr();
+	remoteHostMB = ajpReq.remoteHost();
+	serverNameMB = ajpReq.serverName();
+
+	// XXX sync cookies 
+	scookies = new Cookies( headers );
+	urlDecoder=new UDecoder();
+
+	// XXX sync headers
+	
+	params.setQuery( queryMB );
+	params.setURLDecoder( urlDecoder );
+	params.setHeaders( headers );
+	initRequest(); 	
+
+        this.ajp13=ajp13;
+	this.ajpReq=ajpReq;
+    }
+
+    // -------------------- Wrappers for changed method names, and to use the buffers
+    // XXX Move BaseRequest into util !!! ( it's just a stuct with some MessageBytes )
+
+    public int getServerPort() {
+        return ajpReq.getServerPort();
+    }
+
+    public void setServerPort(int i ) {
+	ajpReq.setServerPort( i );
+    }
+
+    public  void setRemoteUser( String s ) {
+	super.setRemoteUser(s);
+	ajpReq.remoteUser().setString(s);
+    }
+
+    public String getRemoteUser() {
+	String s=ajpReq.remoteUser().toString();
+	if( s == null )
+	    s=super.getRemoteUser();
+	return s;
+    }
+
+    public String getAuthType() {
+	return ajpReq.authType().toString();
+    }
+    
+    public void setAuthType(String s ) {
+	ajpReq.authType().setString(s);
+    }
+
+    public String getJvmRoute() {
+	return ajpReq.jvmRoute().toString();
+    }
+    
+    public void setJvmRoute(String s ) {
+	ajpReq.jvmRoute().setString(s);
+    }
+
+    // XXX scheme
+    
+    public boolean isSecure() {
+	return ajpReq.getSecure();
+    }
+    
+    public int getContentLength() {
+        int i=ajpReq.getContentLength();
+	if( i >= 0 ) return i;
+	i= super.getContentLength();
+	return i;
+    }
+
+    public void setContentLength( int i ) {
+	super.setContentLength(i); // XXX sync
+    }
+
+    // XXX broken
+//     public Iterator getAttributeNames() {
+//         return attributes.keySet().iterator();
+//     }
+
+
+    // --------------------
+
+    public void recycle() {
+	super.recycle();
+	ajpReq.recycle();
+	if( ajp13!=null) ajp13.recycle();
+    }
+
+    public String dumpRequest() {
+	return ajpReq.toString();
+    }
+    
+    // -------------------- 
+    
+    // XXX This should go away if we introduce an InputBuffer.
+    // We almost have it as result of encoding fixes, but for now
+    // just keep this here, doesn't hurt too much.
+    public int doRead() throws IOException 
+    {
+	if( available <= 0 )
+	    return -1;
+	available--;
+	return ajp13.reqHandler.doRead(ajp13);
+    }
+    
+    public int doRead(byte[] b, int off, int len) throws IOException 
+    {
+	if( available <= 0 )
+	    return -1;
+	int rd=ajp13.reqHandler.doRead( ajp13, b,off, len );
+	available -= rd;
+	return rd;
+    }
+    
+}
+
+class Ajp14Response extends Response 
+{
+    Ajp13 ajp13;
+    boolean finished=false;
+    
+    public Ajp14Response(Ajp13 ajp13) 
+    {
+	super();
+	this.ajp13=ajp13;
+    }
+
+    public void recycle() {
+	super.recycle();
+	finished=false;
+    }
+
+    // XXX if more headers that MAX_SIZE, send 2 packets!
+    // XXX Can be implemented using module notification, no need to extend
+    public void endHeaders() throws IOException 
+    {
+        super.endHeaders();
+    
+        if (request.protocol().isNull()) {
+            return;
+        }
+
+	ajp13.reqHandler.sendHeaders(ajp13, ajp13.outBuf, getStatus(),
+				     HttpMessages.getMessage(status),
+				     getMimeHeaders());
+    } 
+
+    // XXX Can be implemented using module notification, no need to extend
+    public void finish() throws IOException 
+    {
+	if(!finished) {
+	    super.finish();
+		finished = true; // Avoid END_OF_RESPONSE sent 2 times
+	    ajp13.reqHandler.finish(ajp13, ajp13.outBuf);
+	}
+    }
+
+    // XXX Can be implemented using the buffers, no need to extend
+    public void doWrite(  byte b[], int off, int len) throws IOException 
+    {
+	ajp13.reqHandler.doWrite(ajp13, ajp13.outBuf, b, off, len );
+    }
+    
+}
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Connector.java b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Connector.java
new file mode 100644
index 0000000..5fe7d28
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Connector.java
@@ -0,0 +1,1108 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp.tomcat4;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.security.AccessControlException;
+import java.util.Stack;
+import java.util.Vector;
+
+import org.apache.catalina.Connector;
+import org.apache.catalina.Container;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Request;
+import org.apache.catalina.Response;
+import org.apache.catalina.Service;
+import org.apache.catalina.net.DefaultServerSocketFactory;
+import org.apache.catalina.net.ServerSocketFactory;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+
+/**
+ * Implementation of an Ajp13 connector.
+ *
+ * @author Kevin Seguin
+ * @version $Revision$ $Date$
+ */
+
+
+public final class Ajp13Connector
+    implements Connector, Lifecycle, Runnable {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The accept count for this Connector.
+     */
+    private int acceptCount = 10;
+
+
+    /**
+     * The IP address on which to bind, if any.  If <code>null</code>, all
+     * addresses on the server will be bound.
+     */
+    private String address = null;
+
+
+    /**
+     * The input buffer size we should create on input streams.
+     */
+    private int bufferSize = 2048;
+
+
+    /**
+     * The Container used for processing requests received by this Connector.
+     */
+    protected Container container = null;
+
+
+    /**
+     * The set of processors that have ever been created.
+     */
+    private Vector created = new Vector();
+
+
+    /**
+     * The current number of processors that have been created.
+     */
+    private int curProcessors = 0;
+
+
+    /**
+     * The debugging detail level for this component.
+     */
+    private int debug = 0;
+
+
+    /**
+     * The server socket factory for this component.
+     */
+    private ServerSocketFactory factory = null;
+
+
+    /**
+     * Descriptive information about this Connector implementation.
+     */
+    private static final String info =
+        "org.apache.catalina.connector.ajp.Ajp13Connector/1.0";
+
+
+    /**
+     * redirect port.
+     */
+    private int redirectPort = -1;
+
+    /**
+     * enable DNS lookups.
+     */
+    private boolean enableLookups = false;
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The minimum number of processors to start at initialization time.
+     */
+    protected int minProcessors = 5;
+
+
+    /**
+     * The maximum number of processors allowed, or <0 for unlimited.
+     */
+    private int maxProcessors = 20;
+
+
+    /**
+     * Timeout value on the incoming connection.
+     * Note : a value of 0 means no timeout.
+     */
+    private int connectionTimeout = -1;
+
+
+    /**
+     * Linger value to be used on socket close.
+     * Note : a value of -1 means no linger used on close.
+     */
+    private int connectionLinger = -1;
+
+
+    /**
+     * The port number on which we listen for ajp13 requests.
+     */
+    private int port = 8009;
+
+
+    /**
+     * The set of processors that have been created but are not currently
+     * being used to process a request.
+     */
+    private Stack processors = new Stack();
+
+
+    /**
+     * The request scheme that will be set on all requests received
+     * through this connector.
+     */
+    private String scheme = "http";
+
+
+    /**
+     * The secure connection flag that will be set on all requests received
+     * through this connector.
+     */
+    private boolean secure = false;
+
+
+    /**
+     * The server socket through which we listen for incoming TCP connections.
+     */
+    private ServerSocket serverSocket = null;
+
+
+    /**
+     * The string manager for this package.
+     */
+    private StringManager sm =
+	StringManager.getManager(Constants.PACKAGE);
+
+
+    /**
+     * Has this component been started yet?
+     */
+    private boolean started = false;
+
+
+    /**
+     * The shutdown signal to our background thread
+     */
+    private boolean stopped = false;
+
+
+    /**
+     * The background thread.
+     */
+    private Thread thread = null;
+
+
+    /**
+     * This connector's thread group.
+     */
+    private ThreadGroup threadGroup = null;
+
+
+    /**
+     * The name to register for the background thread.
+     */
+    private String threadName = null;
+
+
+    /**
+     * A thread that periodically logs debug info if debug > 0.
+     */
+    private DebugThread debugThread = null;
+
+
+    /**
+     * The thread synchronization object.
+     */
+    private Object threadSync = new Object();
+
+    private Ajp13Logger logger = new Ajp13Logger();
+
+    /**
+     * The service which which the connector is associated
+     */
+    private Service service = null;
+
+    private String secret = null;
+
+
+    /**
+     * Tomcat authentication flag. If true, the authnetication is done by
+     * Tomcat, otherwise, it is done by the native webserver.
+     */
+    private boolean tomcatAuthentication = true;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the connection timeout for this Connector.
+     */
+    public int getConnectionTimeout() {
+
+	return (connectionTimeout);
+
+    }
+
+
+    /**
+     * Set the connection timeout for this Connector.
+     *
+     * @param connectionTimeout The new connection timeout
+     */
+    public void setConnectionTimeout(int connectionTimeout) {
+
+	this.connectionTimeout = connectionTimeout;
+
+    }
+
+    /**
+     * Return the connection linger settings for this Connector.
+     */
+    public int getConnectionLinger() {
+
+	return (connectionLinger);
+
+    }
+
+
+    /**
+     * Set the connection linger for this Connector.
+     *
+     * @param connectionLinger The new connection linger
+     */
+    public void setConnectionLinger(int connectionLinger) {
+
+	this.connectionLinger = connectionLinger;
+
+    }
+
+    public void setSecret( String s ) {
+        secret=s;
+    }
+
+    public String getSecret() {
+        return secret;
+    }
+    
+
+    /**
+     * Return the accept count for this Connector.
+     */
+    public int getAcceptCount() {
+
+	return (acceptCount);
+
+    }
+
+
+    /**
+     * Set the accept count for this Connector.
+     *
+     * @param count The new accept count
+     */
+    public void setAcceptCount(int count) {
+
+	this.acceptCount = count;
+
+    }
+
+
+
+    /**
+     * Return the bind IP address for this Connector.
+     */
+    public String getAddress() {
+
+	return (this.address);
+
+    }
+
+
+    /**
+     * Set the bind IP address for this Connector.
+     *
+     * @param address The bind IP address
+     */
+    public void setAddress(String address) {
+
+	this.address = address;
+
+    }
+
+
+    /**
+     * Is this connector available for processing requests?
+     */
+    public boolean isAvailable() {
+
+	return (started);
+
+    }
+
+
+    /**
+     * Return the input buffer size for this Connector.
+     */
+    public int getBufferSize() {
+
+	return (this.bufferSize);
+
+    }
+
+
+    /**
+     * Set the input buffer size for this Connector.
+     *
+     * @param bufferSize The new input buffer size.
+     */
+    public void setBufferSize(int bufferSize) {
+
+	this.bufferSize = bufferSize;
+
+    }
+
+
+    /**
+     * Return the Container used for processing requests received by this
+     * Connector.
+     */
+    public Container getContainer() {
+
+	return (container);
+
+    }
+
+
+    /**
+     * Set the Container used for processing requests received by this
+     * Connector.
+     *
+     * @param container The new Container to use
+     */
+    public void setContainer(Container container) {
+
+	this.container = container;
+
+    }
+
+
+    /**
+     * Return the current number of processors that have been created.
+     */
+    public int getCurProcessors() {
+
+	return (curProcessors);
+
+    }
+
+
+    /**
+     * Return the debugging detail level for this component.
+     */
+    public int getDebug() {
+
+        return (debug);
+
+    }
+
+
+    /**
+     * Set the debugging detail level for this component.
+     *
+     * @param debug The new debugging detail level
+     */
+    public void setDebug(int debug) {
+
+        this.debug = debug;
+
+    }
+
+    /**
+     * Return the "enable DNS lookups" flag.
+     */
+    public boolean getEnableLookups() {
+        return this.enableLookups;
+    }
+
+    /**
+     * Set the "enable DNS lookups" flag.
+     *
+     * @param enableLookups The new "enable DNS lookups" flag value
+     */
+    public void setEnableLookups(boolean enableLookups) {
+        this.enableLookups = enableLookups;
+    }
+
+    /**
+     * Return the port number to which a request should be redirected if
+     * it comes in on a non-SSL port and is subject to a security constraint
+     * with a transport guarantee that requires SSL.
+     */
+    public int getRedirectPort() {
+        return this.redirectPort;
+    }
+
+
+    /**
+     * Set the redirect port number.
+     *
+     * @param redirectPort The redirect port number (non-SSL to SSL)
+     */
+    public void setRedirectPort(int redirectPort) {
+        this.redirectPort = redirectPort;
+    }
+
+    /**
+     * Return the server socket factory used by this Container.
+     */
+    public ServerSocketFactory getFactory() {
+
+        if (this.factory == null) {
+            synchronized (this) {
+                this.factory = new DefaultServerSocketFactory();
+            }
+        }
+        return (this.factory);
+
+    }
+
+
+    /**
+     * Set the server socket factory used by this Container.
+     *
+     * @param factory The new server socket factory
+     */
+    public void setFactory(ServerSocketFactory factory) {
+
+        this.factory = factory;
+
+    }
+
+
+    /**
+     * Return descriptive information about this Connector implementation.
+     */
+    public String getInfo() {
+
+	return (info);
+
+    }
+
+
+    /**
+     * Return the minimum number of processors to start at initialization.
+     */
+    public int getMinProcessors() {
+
+	return (minProcessors);
+
+    }
+
+
+    /**
+     * Set the minimum number of processors to start at initialization.
+     *
+     * @param minProcessors The new minimum processors
+     */
+    public void setMinProcessors(int minProcessors) {
+
+	this.minProcessors = minProcessors;
+
+    }
+
+
+    /**
+     * Return the maximum number of processors allowed, or <0 for unlimited.
+     */
+    public int getMaxProcessors() {
+
+	return (maxProcessors);
+
+    }
+
+
+    /**
+     * Set the maximum number of processors allowed, or <0 for unlimited.
+     *
+     * @param maxProcessors The new maximum processors
+     */
+    public void setMaxProcessors(int maxProcessors) {
+
+	this.maxProcessors = maxProcessors;
+
+    }
+
+
+    /**
+     * Return the port number on which we listen for AJP13 requests.
+     */
+    public int getPort() {
+
+	return (this.port);
+
+    }
+
+
+    /**
+     * Set the port number on which we listen for AJP13 requests.
+     *
+     * @param port The new port number
+     */
+    public void setPort(int port) {
+
+	this.port = port;
+
+    }
+
+
+    /**
+     * Return the scheme that will be assigned to requests received
+     * through this connector.  Default value is "http".
+     */
+    public String getScheme() {
+
+	return (this.scheme);
+
+    }
+
+
+    /**
+     * Set the scheme that will be assigned to requests received through
+     * this connector.
+     *
+     * @param scheme The new scheme
+     */
+    public void setScheme(String scheme) {
+
+	this.scheme = scheme;
+
+    }
+
+
+    /**
+     * Return the secure connection flag that will be assigned to requests
+     * received through this connector.  Default value is "false".
+     */
+    public boolean getSecure() {
+
+	return (this.secure);
+
+    }
+
+
+    /**
+     * Set the secure connection flag that will be assigned to requests
+     * received through this connector.
+     *
+     * @param secure The new secure connection flag
+     */
+    public void setSecure(boolean secure) {
+
+	this.secure = secure;
+
+    }
+
+
+    /**
+     * Returns the <code>Service</code> with which we are associated.
+     */
+    public Service getService() {
+	return service;
+    }
+
+
+    /**
+     * Set the <code>Service</code> with which we are associated.
+     */
+    public void setService(Service service) {
+	this.service = service;
+    }
+
+
+    /**
+     * Get the value of the tomcatAuthentication flag.
+     */
+    public boolean getTomcatAuthentication() {
+        return tomcatAuthentication;
+    }
+
+
+    /**
+     * Set the value of the tomcatAuthentication flag.
+     */
+    public void setTomcatAuthentication(boolean tomcatAuthentication) {
+        this.tomcatAuthentication = tomcatAuthentication;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Create (or allocate) and return a Request object suitable for
+     * specifying the contents of a Request to the responsible Container.
+     */
+    public Request createRequest() {
+
+	Ajp13Request request = new Ajp13Request(this);
+	request.setConnector(this);
+	return (request);
+
+    }
+
+
+    /**
+     * Create (or allocate) and return a Response object suitable for
+     * receiving the contents of a Response from the responsible Container.
+     */
+    public Response createResponse() {
+
+	Ajp13Response response = new Ajp13Response();
+	response.setConnector(this);
+	return (response);
+
+    }
+
+    /**
+     * Invoke a pre-startup initialization. This is used to allow connectors
+     * to bind to restricted ports under Unix operating environments.
+     * ServerSocket (we start as root and change user? or I miss something?).
+     */
+    public void initialize() throws LifecycleException {
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Recycle the specified Processor so that it can be used again.
+     *
+     * @param processor The processor to be recycled
+     */
+    void recycle(Ajp13Processor processor) {
+
+        synchronized(processors) {
+            if (debug > 0) {
+                logger.log("added processor to available processors, available="
+                           + processors.size());
+            }
+            processors.push(processor);
+        }
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Create (or allocate) and return an available processor for use in
+     * processing a specific AJP13 request, if possible.  If the maximum
+     * allowed processors have already been created and are in use, return
+     * <code>null</code> instead.
+     */
+    private Ajp13Processor createProcessor() {
+
+	synchronized (processors) {
+	    if (processors.size() > 0)
+		return ((Ajp13Processor) processors.pop());
+	    if ((maxProcessors > 0) && (curProcessors < maxProcessors))
+	        return (newProcessor());
+	    else
+	        return (null);
+	}
+
+    }
+
+
+    /**
+     * Create and return a new processor suitable for processing AJP13
+     * requests and returning the corresponding responses.
+     */
+    private Ajp13Processor newProcessor() {
+
+        Ajp13Processor processor = new Ajp13Processor(this, curProcessors++, threadGroup);
+	if (processor instanceof Lifecycle) {
+	    try {
+	        ((Lifecycle) processor).start();
+	    } catch (LifecycleException e) {
+	        logger.log("newProcessor", e);
+                curProcessors--;
+	        return (null);
+	    }
+	}
+	created.addElement(processor);
+	return (processor);
+
+    }
+
+
+    /**
+     * Open and return the server socket for this Connector.  If an IP
+     * address has been specified, the socket will be opened only on that
+     * address; otherwise it will be opened on all addresses.
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    private ServerSocket open() throws IOException {
+
+        // Acquire the server socket factory for this Connector
+        ServerSocketFactory factory = getFactory();
+
+	// If no address is specified, open a connection on all addresses
+        if (address == null) {
+	    logger.log(sm.getString("ajp13Connector.allAddresses"));
+            try {
+		return (factory.createSocket(port, acceptCount));
+	    } catch(Exception ex ) {
+		ex.printStackTrace();
+		return null;
+	    }
+	}
+
+	// Open a server socket on the specified address
+        try {
+            InetAddress is = InetAddress.getByName(address);
+	    logger.log(sm.getString("ajp13Connector.anAddress", address));
+            return (factory.createSocket(port, acceptCount, is));
+	} catch (Exception e) {
+	    try {
+		logger.log(sm.getString("ajp13Connector.noAddress", address));
+		return (factory.createSocket(port, acceptCount));
+	    } catch( Exception e1 ) {
+		e1.printStackTrace();
+		return null;
+	    }
+	}
+
+    }
+
+
+    // ---------------------------------------------- Background Thread Methods
+
+
+    /**
+     * The background thread that listens for incoming TCP/IP connections and
+     * hands them off to an appropriate processor.
+     */
+    public void run() {
+
+        // Loop until we receive a shutdown command
+	while (!stopped) {
+
+	    // Accept the next incoming connection from the server socket
+	    Socket socket = null;
+	    try {
+                if (debug > 0) {
+                    logger.log("accepting socket...");
+                }
+                
+		socket = serverSocket.accept();
+
+                if (debug > 0) {
+                    logger.log("accepted socket, assigning to processor.");
+                }
+                
+                /* Warning :
+                 * 
+                 * To be able to close more quickly a connection, it's recommanded
+                 * to set linger to a small value.
+                 * 
+                 * AJP13 connection SHOULD be closed under webserver responsability and 
+                 * in such case it's safe to close socket on Tomcat side without delay,
+                 * which may be also the case for HTTP connectors.
+                 * 
+                 * I (henri) recommand to set Linger to 0, making socket closed immediatly
+                 * so the OS will free faster the underlying io descriptor and resources.
+                 * It's very important under heavy load !
+                 */
+                
+                if (connectionLinger < 0)
+                	socket.setSoLinger(false, 0);
+                else	
+                	socket.setSoLinger(true, connectionLinger);
+                	
+                /* We don't need it since it's the native side which 
+                 * will set the connection with keep alive
+                 * if specified in workers.properties.
+                 * 
+                 * socket.setKeepAlive(true); 
+                 */
+                
+                /* Warning :
+                 * 
+                 * AJP13 shouldn't use socket timeout on tomcat site since
+                 * when Tomcat close a connection after a timeout is reached
+                 * the socket stay in half-closed state until the webserver
+                 * try to send a request to tomcat and detect the socket close
+                 * when it will try to read the reply.
+                 * 
+                 * On many Unix platforms the write() call didn't told
+                 * webserver that the socket is closed.
+                 */
+                 
+                if (connectionTimeout >= 0) {
+                    socket.setSoTimeout(connectionTimeout);
+                }
+            } catch (AccessControlException ace) {
+                logger.log("socket accept security exception: "
+                           + ace.getMessage());
+                continue;
+	    } catch (IOException e) {
+		if (started && !stopped)
+		    logger.log("accept: ", e);
+		try {
+                    if (serverSocket != null) {
+                        serverSocket.close();
+                    }
+                    if (stopped) {
+                        if (debug > 0) {
+                            logger.log("run():  stopped, so breaking");
+                        }
+                        break;
+                    } else {
+                        if (debug > 0) {
+                            logger.log("run():  not stopped, " +
+                                       "so reopening server socket");
+                        }
+                        serverSocket = open();
+                    }
+                } catch (IOException ex) {
+                    // If reopening fails, exit
+                    logger.log("socket reopen: ", ex);
+                    break;
+                }
+                continue;
+	    }
+
+	    // Hand this socket off to an appropriate processor
+            if (debug > 0) {
+                synchronized(processors) {
+                    logger.log("about to create a processor, available="
+                               + processors.size() + ", created=" + created.size()
+                               + ", maxProcessors=" + maxProcessors);
+                }
+            }
+	    Ajp13Processor processor = createProcessor();
+	    if (processor == null) {
+		try {
+		    logger.log(sm.getString("ajp13Connector.noProcessor"));
+		    socket.close();
+		} catch (IOException e) {
+		    ;
+		}
+		continue;
+	    }
+	    processor.assign(socket);
+
+	    // The processor will recycle itself when it finishes
+
+	}
+
+	// Notify the threadStop() method that we have shut ourselves down
+	synchronized (threadSync) {
+	    threadSync.notifyAll();
+	}
+
+    }
+
+
+    /**
+     * Start the background processing thread.
+     */
+    private void threadStart() {
+
+	logger.log(sm.getString("ajp13Connector.starting"));
+
+	thread = new Thread(threadGroup, this, threadName);
+	thread.setDaemon(true);
+	thread.start();
+
+    }
+
+
+    /**
+     * Stop the background processing thread.
+     */
+    private void threadStop() {
+
+	logger.log(sm.getString("ajp13Connector.stopping"));
+
+	stopped = true;
+	synchronized (threadSync) {
+	    try {
+		threadSync.wait(5000);
+	    } catch (InterruptedException e) {
+		;
+	    }
+	}
+	thread = null;
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+	lifecycle.addLifecycleListener(listener);
+
+    }
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+        return null; // FIXME: lifecycle.findLifecycleListeners();
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to add
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+	lifecycle.removeLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Begin processing requests via this Connector.
+     *
+     * @exception LifecycleException if a fatal startup error occurs
+     */
+    public void start() throws LifecycleException {
+
+	// Validate and update our current state
+	if (started)
+	    throw new LifecycleException
+		(sm.getString("ajp13Connector.alreadyStarted"));
+
+        if (debug > 0) {
+            debugThread = new DebugThread();
+            debugThread.setDaemon(true);
+            debugThread.start();
+        }
+
+        threadName = "Ajp13Connector[" + port + "]";
+        threadGroup = new ThreadGroup(threadName);
+        threadGroup.setDaemon(true);
+        logger.setConnector(this);
+        logger.setName(threadName);
+	lifecycle.fireLifecycleEvent(START_EVENT, null);
+	started = true;
+
+	// Establish a server socket on the specified port
+	try {
+	    serverSocket = open();
+	} catch (IOException e) {
+	    throw new LifecycleException(threadName + ".open", e);
+	}
+
+	// Start our background thread
+	threadStart();
+
+	// Create the specified minimum number of processors
+	while (curProcessors < minProcessors) {
+	    if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
+		break;
+	    Ajp13Processor processor = newProcessor();
+	    recycle(processor);
+	}
+
+    }
+
+
+    /**
+     * Terminate processing requests via this Connector.
+     *
+     * @exception LifecycleException if a fatal shutdown error occurs
+     */
+    public void stop() throws LifecycleException {
+
+	// Validate and update our current state
+	if (!started)
+	    throw new LifecycleException
+		(sm.getString("ajp13Connector.notStarted"));
+	lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+	started = false;
+
+	// Gracefully shut down all processors we have created
+	for (int i = created.size() - 1; i >= 0; i--) {
+	    Ajp13Processor processor = (Ajp13Processor) created.elementAt(i);
+	    if (processor instanceof Lifecycle) {
+		try {
+		    ((Lifecycle) processor).stop();
+		} catch (LifecycleException e) {
+		    logger.log("Ajp13Connector.stop", e);
+		}
+	    }
+	}
+
+	// Stop our background thread
+	threadStop();
+
+	// Close the server socket we were using
+	if (serverSocket != null) {
+	    try {
+		serverSocket.close();
+	    } catch (IOException e) {
+		;
+	    }
+	    serverSocket = null;
+	}
+
+    }
+
+    /**
+     * Debugging thread used to debug thread activity in this
+     * connector.
+     */
+    private class DebugThread extends Thread
+    {
+        public void run() {
+            while (true) {
+                try {
+                    sleep(60 * 1000);
+                } catch (InterruptedException e) {
+                    break;
+                }
+                logger.log("active threads=" + threadGroup.activeCount());
+                System.out.println("===================================");
+                System.out.println("Ajp13Connector active threads="
+                                   + threadGroup.activeCount());
+                threadGroup.list();
+                System.out.println("===================================");
+            }
+        }
+    }
+
+}
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13InputStream.java b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13InputStream.java
new file mode 100644
index 0000000..330f123
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13InputStream.java
@@ -0,0 +1,66 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp.tomcat4;
+
+import java.io.IOException;
+
+import javax.servlet.ServletInputStream;
+
+import org.apache.ajp.Ajp13;
+
+public class Ajp13InputStream extends ServletInputStream {
+
+    private Ajp13 ajp13;
+
+    Ajp13InputStream(Ajp13 ajp13) {
+        this.ajp13 = ajp13;
+    }
+
+    public int available() throws IOException {
+        return ajp13.available();
+    }
+
+    public void close() throws IOException {
+    }
+
+    public void mark(int readLimit) {
+    }
+
+    public boolean markSupported() {
+        return false;
+    }
+
+    public void reset() throws IOException {
+        throw new IOException("reset() not supported");
+    }
+
+    public int read() throws IOException {
+        return ajp13.doRead();
+    }
+
+    public int read(byte[] b, int off, int len) throws IOException {
+        return ajp13.doRead(b, off, len);
+    }
+
+    public long skip(long n) throws IOException {
+        if (n > Integer.MAX_VALUE) {
+            throw new IOException("can't skip than many:  " + n);
+        }
+        byte[] b = new byte[(int)n];
+        return ajp13.doRead(b, 0, b.length);
+    }
+}
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Logger.java b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Logger.java
new file mode 100644
index 0000000..13067eb
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Logger.java
@@ -0,0 +1,87 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp.tomcat4;
+
+import org.apache.catalina.Logger;
+
+class Ajp13Logger {
+
+    private String name = null;
+    private Ajp13Connector connector = null;
+    private boolean logStackTrace = false;
+    
+    Ajp13Logger() {
+        name = toString();
+    }
+
+    void setConnector(Ajp13Connector connector) {
+        this.connector = connector;
+    }
+
+    void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Log a message on the Logger associated with our Container (if any)
+     *
+     * @param message Message to be logged
+     */
+    void log(String message) {
+
+        if (logStackTrace) {
+            log(message, new Throwable());
+        } else {
+            Logger logger = getLogger();
+            
+            if (logger != null)
+                logger.log(name + " " + message);
+            else
+                System.out.println(name + " " + message);
+        }
+    }
+
+    /**
+     * Log a message on the Logger associated with our Container (if any)
+     *
+     * @param message Message to be logged
+     * @param throwable Associated exception
+     */
+    void log(String message, Throwable throwable) {
+
+        Logger logger = getLogger();
+
+	if (logger != null)
+	    logger.log(name + " " + message, throwable);
+	else {
+	    System.out.println(name + " " + message);
+	    throwable.printStackTrace(System.out);
+	}
+
+    }
+
+    private Logger getLogger() {
+
+        if (connector != null) {
+            return connector.getContainer().getLogger();
+        } else {
+            return null;
+        }
+
+    }
+
+}
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13OutputStream.java b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13OutputStream.java
new file mode 100644
index 0000000..0051029
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13OutputStream.java
@@ -0,0 +1,46 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp.tomcat4;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.ajp.Ajp13;
+
+public class Ajp13OutputStream extends OutputStream {
+
+    private Ajp13 ajp13;
+    
+    Ajp13OutputStream(Ajp13 ajp13) {
+        this.ajp13 = ajp13;
+    }
+
+    public void write(int b) throws IOException {
+        byte[] bb = new byte[]{(byte)b};
+        ajp13.doWrite(bb, 0, 1);
+    }
+
+    public void write(byte[] b, int off, int len) throws IOException {
+        ajp13.doWrite(b, off, len);
+    }
+
+    public void close() throws IOException {
+    }
+
+    public void flush() throws IOException {
+    }
+}
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Processor.java b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Processor.java
new file mode 100644
index 0000000..a20833d
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Processor.java
@@ -0,0 +1,666 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp.tomcat4;
+
+
+import java.io.IOException;
+import java.net.Socket;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.ajp.Ajp13;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.util.LifecycleSupport;
+import org.apache.catalina.util.StringManager;
+import org.apache.tomcat.util.http.BaseRequest;
+
+/**
+ * @author Kevin Seguin
+ * @version $Revision$ $Date$
+ */
+
+final class Ajp13Processor
+    implements Lifecycle, Runnable {
+
+    /**
+     * A simple class to provide synchronized access
+     * to a boolean.
+     */
+    private class Bool {
+
+        private boolean b = false;
+
+        Bool() {
+        }
+        
+        Bool(boolean b) {
+            this.b = b;
+        }
+
+        synchronized boolean value() {
+            return b;
+        }
+
+        synchronized void set(boolean b) {
+            this.b = b;
+        }
+    }
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new Ajp13Processor associated with the specified connector.
+     *
+     * @param connector Ajp13Connector that owns this processor
+     * @param id Identifier of this Ajp13Processor (unique per connector)
+     * @param threadGroup The thread group any threads created by the processor
+     *        should be in.
+     */
+    public Ajp13Processor(Ajp13Connector connector,
+                          int id,
+                          ThreadGroup threadGroup) {
+
+	super();
+	this.connector = connector;
+	this.debug = connector.getDebug();
+	this.id = id;
+	this.request = (Ajp13Request) connector.createRequest();
+        this.request.setConnector(connector);
+        this.request.setConnector(connector);
+	this.response = (Ajp13Response) connector.createResponse();
+        this.response.setConnector(connector);
+	this.threadName =
+	  "Ajp13Processor[" + connector.getPort() + "][" + id + "]";
+        this.threadGroup = threadGroup;
+
+        this.logger.setConnector(connector);
+        this.logger.setName(this.threadName);
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    private Ajp13Logger logger = new Ajp13Logger();
+    private BaseRequest ajpRequest = new BaseRequest();
+
+    /**
+     * Is there a new socket available?
+     */
+    private boolean available = false;
+
+
+    /**
+     * The Ajp13Connector with which this processor is associated.
+     */
+    private Ajp13Connector connector = null;
+
+
+    /**
+     * The debugging detail level for this component.
+     */
+    private int debug = 0;
+
+
+    /**
+     * The identifier of this processor, unique per connector.
+     */
+    private int id = 0;
+
+
+    /**
+     * The lifecycle event support for this component.
+     */
+    private LifecycleSupport lifecycle = new LifecycleSupport(this);
+
+
+    /**
+     * The AJP13 request object we will pass to our associated container.
+     */
+    private Ajp13Request request = null;
+
+
+    /**
+     * The AJP13 response object we will pass to our associated container.
+     */
+    private Ajp13Response response = null;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm =
+	StringManager.getManager(Constants.PACKAGE);
+
+
+    /**
+     * The socket we are currently processing a request for.  This object
+     * is used for inter-thread communication only.
+     */
+    private Socket socket = null;
+
+
+    /**
+     * Has this component been started yet?
+     */
+    private boolean started = false;
+
+
+    /**
+     * The shutdown signal to our background thread
+     */
+    private Bool stopped = new Bool(true);
+
+    /**
+     * Are we currently handling a request?
+     */
+    private Bool handlingRequest = new Bool(false);
+
+
+    /**
+     * The background thread.
+     */
+    private Thread thread = null;
+
+
+    /**
+     * The name to register for the background thread.
+     */
+    private String threadName = null;
+
+
+    /**
+     * This processor's thread group.
+     */
+    private ThreadGroup threadGroup = null;
+
+
+    /**
+     * The thread synchronization object.
+     */
+    private Object threadSync = new Object();
+
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Process an incoming TCP/IP connection on the specified socket.  Any
+     * exception that occurs during processing must be logged and swallowed.
+     * <b>NOTE</b>:  This method is called from our Connector's thread.  We
+     * must assign it to our own thread so that multiple simultaneous
+     * requests can be handled.
+     *
+     * @param socket TCP socket to process
+     */
+    synchronized void assign(Socket socket) {
+
+        // Wait for the Processor to get the previous Socket
+        while (available) {
+	    try {
+	        wait();
+	    } catch (InterruptedException e) {
+	    }
+        }
+
+	// Store the newly available Socket and notify our thread
+	this.socket = socket;
+	available = true;
+	notifyAll();
+
+	if ((debug > 0) && (socket != null))
+	    logger.log(" An incoming request is being assigned");
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Await a newly assigned Socket from our Connector, or <code>null</code>
+     * if we are supposed to shut down.
+     */
+    private synchronized Socket await() {
+
+        // Wait for the Connector to provide a new Socket
+        while (!available) {
+	    try {
+	        wait();
+	    } catch (InterruptedException e) {
+	    }
+        }
+
+	// Notify the Connector that we have received this Socket
+	Socket socket = this.socket;
+	available = false;
+	notifyAll();
+
+	if ((debug > 0) && (socket != null))
+	    logger.log("  The incoming request has been awaited");
+
+	return (socket);
+
+    }
+
+    /**
+     * Parse and record the connection parameters related to this request.
+     *
+     * @param socket The socket on which we are connected
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception ServletException if a parsing error occurs
+     */
+    private void parseConnection(Socket socket)
+        throws IOException, ServletException {
+
+	if (debug > 1)
+	    logger.log("  parseConnection: address=" + socket.getInetAddress() +
+		", port=" + connector.getPort());
+	request.setServerPort(connector.getPort());
+        request.setSocket(socket);
+
+    }
+
+    /**
+     * Process an incoming AJP13 request on the Socket that has been assigned
+     * to this Processor.  Any exceptions that occur during processing must be
+     * swallowed and dealt with.
+     *
+     * @param socket The socket on which we are connected to the client
+     */
+    private void process(Socket socket) {
+
+        Ajp13 ajp13 = new Ajp13();
+        ajp13.setDebug(debug);
+        ajp13.setLogger(new org.apache.ajp.Logger() {
+                public void log(String msg) {
+                    logger.log("[Ajp13] " + msg);
+                }
+                
+                public void log(String msg, Throwable t) {
+                    logger.log("[Ajp13] " + msg, t);
+                }
+            });
+
+        Ajp13InputStream input = new Ajp13InputStream(ajp13);
+        Ajp13OutputStream output = new Ajp13OutputStream(ajp13);
+        response.setAjp13(ajp13);
+
+        try {
+            ajp13.setSocket(socket);
+        } catch (IOException e) {
+            logger.log("process: ajp13.setSocket", e);
+        }
+
+        boolean moreRequests = true;
+        String expectedSecret=connector.getSecret();
+        
+        boolean needAuth= ( expectedSecret != null );
+        
+        while (moreRequests && !stopped.value()) {
+            
+            int status = 0;
+            try {
+                if (debug > 0) {
+                    logger.log("waiting on next request...");
+                }
+                
+                status = ajp13.receiveNextRequest(ajpRequest);
+                
+                if (debug > 0) {
+                    logger.log("received next request, status=" + status);
+                }
+            } catch (IOException e) {
+                logger.log("process: ajp13.receiveNextRequest", e);
+            }
+
+            if( needAuth ) {
+                String connSecret=ajp13.getSecret();
+                if( connSecret == null ) {
+                    logger.log( "Connection without password, " +
+                                "tomcat is configured to require one" );
+                    break;
+                }
+                if( ! connSecret.equals(expectedSecret) ) {
+                    logger.log( "Connection with wrong password" );
+                    break;
+                }
+                
+                needAuth=false;
+            }
+            
+            if (stopped.value()) {
+                if (debug > 0) {
+                    logger.log("process:  received request, but we're stopped");
+                }
+                break;
+            }
+            
+            if( status==-2) {
+                // special case - shutdown
+                // XXX need better communication, refactor it
+//                  if( !doShutdown(socket.getLocalAddress(),
+//                                  socket.getInetAddress())) {
+//                      moreRequests = false;
+//                      continue;
+//                  }
+                break;
+            }
+            
+			// Allready handled by low level proto, don't go farther
+			if( status == 999 )
+			{
+				ajpRequest.recycle();
+				request.recycle();
+
+				// recycle ajp13 object
+				ajp13.recycle();
+
+				continue;
+			}
+
+			if( status != 200 )
+				break;
+
+            try {
+                // set flag
+                handlingRequest.set(true);
+
+                boolean bad_request = false;
+
+                // set up request
+                try {
+                    request.setAjpRequest(ajpRequest);
+                } catch (IllegalArgumentException e) {
+                    bad_request = true;
+                }
+                request.setResponse(response);
+                request.setStream(input);
+                
+                // setup response
+                response.setRequest(request);
+                response.setStream(output);
+                
+                if (debug > 0) {
+                    logger.log("invoking...");
+                }
+
+                if (!bad_request) {
+                    try {
+                        connector.getContainer().invoke(request, response);
+                    } catch (IOException ioe) {
+                        // Pass the IOException through
+                        throw ioe;
+                    } catch (Throwable e) {
+                        // A throwable here could be caused by a Valve,
+                        // Filter, or other component in the chain.
+                        // Processing of the request failed, return an
+                        // Internal Server Error
+                        logger.log("process: invoke", e);
+                        response.sendError
+                            (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                    }
+                } else {
+                    response.sendError
+                        (HttpServletResponse.SC_BAD_REQUEST);
+                }
+
+                if (debug > 0) {
+                    logger.log("done invoking, finishing request/response....");
+                }
+
+                response.finishResponse();
+                request.finishRequest();
+
+                if (debug > 0) {
+                    logger.log("finished handling request.");
+                }
+
+            } catch (IOException ioe) {
+                // Normally this catches a socket Broken Pipe caused by the
+                // remote client aborting the request. Don't print the stack
+                // trace in this case. Then let the Processor recycle.
+                logger.log("process: IOException " + ioe.getMessage());
+                moreRequests = false;
+            } catch (Throwable e) {
+                // Processing the request and sending the response failed.
+                // We don't know what the state of the Ajp Connector socket
+                // is in. Bail out and recycle the Processor.
+                logger.log("process: finish", e);
+                moreRequests = false;
+            }
+
+            // Recycling the request and the response objects
+            if (debug > 0) {
+                logger.log("recyling objects ...");
+            }
+            
+            ajpRequest.recycle();
+            request.recycle();
+            response.recycle();
+
+            // recycle ajp13 object
+            ajp13.recycle();
+
+            // reset flag
+            handlingRequest.set(false);
+        }
+        
+	try {
+            if (debug > 0) {
+                logger.log("closing ajp13 object...");
+            }
+
+            ajp13.close();
+
+            if (debug > 0) {
+                logger.log("ajp13 object closed.");
+            }
+	} catch (IOException e) {
+	    logger.log("process: ajp13.close", e);
+	}
+
+	try {
+            if (debug > 0) {
+                logger.log("closing socket...");
+            }
+
+            socket.close();
+
+            if (debug > 0) {
+                logger.log("socket closed.");
+            }
+	} catch (IOException e) {
+	    logger.log("process: socket.close", e);
+	}
+	socket = null;
+
+        if (debug > 0) {
+            logger.log("process:  done");
+        }
+    }
+
+
+    // ---------------------------------------------- Background Thread Methods
+
+
+    /**
+     * The background thread that listens for incoming TCP/IP connections and
+     * hands them off to an appropriate processor.
+     */
+    public void run() {
+
+        // Process requests until we receive a shutdown signal
+	while (!stopped.value()) {
+
+	    // Wait for the next socket to be assigned
+            if (debug > 0) {
+                logger.log("waiting for next socket to be assigned...");
+            }
+	    Socket socket = await();
+	    if (socket == null)
+		continue;
+
+            if (debug > 0) {
+                logger.log("socket assigned.");
+            }
+
+	    // Process the request from this socket
+	    process(socket);
+
+	    // Finish up this request
+            if (debug > 0) {
+                logger.log("recycling myself ...");
+            }
+	    connector.recycle(this);
+	}
+
+	// Tell threadStop() we have shut ourselves down successfully
+	synchronized (threadSync) {
+	    threadSync.notifyAll();
+	}
+
+    }
+
+
+    /**
+     * Start the background processing thread.
+     */
+    private void threadStart() {
+
+	logger.log(sm.getString("ajp13Processor.starting"));
+
+        stopped.set(false);
+	thread = new Thread(threadGroup, this, threadName);
+	thread.setDaemon(true);
+	thread.start();
+
+	if (debug > 0)
+	    logger.log(" Background thread has been started");
+
+    }
+
+
+    /**
+     * Stop the background processing thread.
+     */
+    private void threadStop() {
+
+	logger.log(sm.getString("ajp13Processor.stopping"));
+
+	stopped.set(true);
+        assign(null);
+	synchronized (threadSync) {
+	    try {
+                if (handlingRequest.value()) {
+                    if (debug > 0) {
+                        logger.log
+                            ("currentling handling a request, so waiting....");
+                    }
+                    threadSync.wait(5000);
+                } else {
+                    if (debug > 0) {
+                        logger.log
+                            ("not currently handling a request, not waiting.");
+                    }
+                }
+	    } catch (InterruptedException e) {
+		;
+	    }
+	}
+	thread = null;
+
+    }
+
+
+    // ------------------------------------------------------ Lifecycle Methods
+
+
+    /**
+     * Add a lifecycle event listener to this component.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+
+	lifecycle.addLifecycleListener(listener);
+
+    }
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+        return null; // FIXME: lifecycle.findLifecycleListeners();
+    }
+
+
+    /**
+     * Remove a lifecycle event listener from this component.
+     *
+     * @param listener The listener to add
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+
+	lifecycle.removeLifecycleListener(listener);
+
+    }
+
+
+    /**
+     * Start the background thread we will use for request processing.
+     *
+     * @exception LifecycleException if a fatal startup error occurs
+     */
+    public void start() throws LifecycleException {
+
+	if (started)
+	    throw new LifecycleException
+		(sm.getString("ajp13Processor.alreadyStarted"));
+	lifecycle.fireLifecycleEvent(START_EVENT, null);
+	started = true;
+
+	threadStart();
+
+    }
+
+
+    /**
+     * Stop the background thread we will use for request processing.
+     *
+     * @exception LifecycleException if a fatal shutdown error occurs
+     */
+    public void stop() throws LifecycleException {
+
+	if (!started)
+	    throw new LifecycleException
+		(sm.getString("ajp13Processor.notStarted"));
+	lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+	started = false;
+
+	threadStop();
+
+    }
+
+
+}
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Request.java b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Request.java
new file mode 100644
index 0000000..371fa4f
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Request.java
@@ -0,0 +1,320 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp.tomcat4;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.TreeMap;
+
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.connector.HttpRequestBase;
+import org.apache.catalina.util.StringParser;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.BaseRequest;
+import org.apache.tomcat.util.http.Cookies;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.http.ServerCookie;
+
+public class Ajp13Request extends HttpRequestBase {
+
+    private static final String match =
+	";" + Globals.SESSION_PARAMETER_NAME + "=";
+
+    private static int id = 1;
+
+    private Ajp13Logger logger = new Ajp13Logger();
+    private int debug;
+
+    public Ajp13Request(Ajp13Connector connector) {
+        super();
+        this.debug = connector.getDebug();
+        this.logger.setConnector(connector);
+        this.logger.setName("Ajp13Request[" + (id++) + "]");
+    }
+
+    public void recycle() {
+        super.recycle();        
+    }
+
+    void setAjpRequest(BaseRequest ajp) throws UnsupportedEncodingException {
+        // XXX make this guy wrap AjpRequest so
+        // we're more efficient (that's the whole point of
+        // all of the MessageBytes in AjpRequest)
+
+        setMethod(ajp.method().toString());
+        setProtocol(ajp.protocol().toString());
+        setRequestURI(ajp.requestURI().toString());
+        setRemoteAddr(ajp.remoteAddr().toString());
+        setRemoteHost(ajp.remoteHost().toString());
+        setServerName(ajp.serverName().toString());
+        setServerPort(ajp.getServerPort());
+
+        if ((!(((Ajp13Connector) connector).getTomcatAuthentication())) 
+            && (ajp.remoteUser() != null)) {
+            setUserPrincipal(new Ajp13Principal(ajp.remoteUser().toString()));
+        } else {
+            setUserPrincipal(null);
+        }
+
+        setAuthType(ajp.authType().toString());
+        setAuthorization(ajp.authorization().toString());
+        setQueryString(ajp.queryString().toString());
+        setScheme(ajp.getScheme());
+        setSecure(ajp.getSecure());
+        setContentLength(ajp.getContentLength());
+
+        String contentType = ajp.contentType().toString();
+        if (contentType != null) {
+            setContentType(contentType);
+        }
+
+        MimeHeaders mheaders = ajp.headers();
+        int nheaders = mheaders.size();
+        for (int i = 0; i < nheaders; ++i) {
+            MessageBytes name = mheaders.getName(i);
+            MessageBytes value = mheaders.getValue(i);
+            addHeader(name.toString(), value.toString());
+            if ("accept-language".equals(name.toString()))
+                parseLocalesHeader(value.toString());	
+        }
+
+        Iterator itr = ajp.getAttributeNames();
+        while (itr.hasNext()) {
+            String name = (String)itr.next();
+            setAttribute(name, ajp.getAttribute(name));
+        }
+
+        addCookies(ajp.cookies());
+    }
+
+//      public Object getAttribute(String name) {
+//          return ajp.getAttribute(name);
+//      }
+
+//      public Enumeration getAttributeNames() {
+//          return new Enumerator(ajp.getAttributeNames());
+//      }
+
+    public void setRequestURI(String uri) {
+	int semicolon = uri.indexOf(match);
+	if (semicolon >= 0) {
+	    String rest = uri.substring(semicolon + match.length());
+	    int semicolon2 = rest.indexOf(";");
+	    if (semicolon2 >= 0) {
+		setRequestedSessionId(rest.substring(0, semicolon2));
+		rest = rest.substring(semicolon2);
+	    } else {
+		setRequestedSessionId(rest);
+		rest = "";
+	    }
+	    setRequestedSessionURL(true);
+	    uri = uri.substring(0, semicolon) + rest;
+	    if (debug >= 1)
+	        logger.log(" Requested URL session id is " +
+                           ((HttpServletRequest) getRequest())
+                           .getRequestedSessionId());
+	} else {
+	    setRequestedSessionId(null);
+	    setRequestedSessionURL(false);
+	}
+
+        super.setRequestURI(uri);
+    }
+
+    private void addCookies(Cookies cookies) {
+        int ncookies = cookies.getCookieCount();
+        for (int j = 0; j < ncookies; j++) {
+            ServerCookie scookie = cookies.getCookie(j);
+            Cookie cookie = new Cookie(scookie.getName().toString(),
+                                       scookie.getValue().toString());
+            if (cookie.getName().equals(Globals.SESSION_COOKIE_NAME)) {
+                // Override anything requested in the URL
+                if (!isRequestedSessionIdFromCookie()) {
+                                // Accept only the first session id cookie
+                    setRequestedSessionId(cookie.getValue());
+                    setRequestedSessionCookie(true);
+                    setRequestedSessionURL(false);
+                    if (debug > 0) {
+                        logger.log(" Requested cookie session id is " +
+                                   ((HttpServletRequest) getRequest())
+                                   .getRequestedSessionId());
+                    }
+                }
+            }
+            if (debug > 0) {
+                logger.log(" Adding cookie " + cookie.getName() + "=" +
+                           cookie.getValue());
+            }
+            addCookie(cookie);                    
+        }        
+    }
+
+    public ServletInputStream createInputStream() throws IOException {
+        return (ServletInputStream)getStream();
+    }
+
+
+    /**
+     * Parse accept-language header value.
+     */
+    protected void parseLocalesHeader(String value) {
+
+        // Store the accumulated languages that have been requested in
+        // a local collection, sorted by the quality value (so we can
+        // add Locales in descending order).  The values will be ArrayLists
+        // containing the corresponding Locales to be added
+        TreeMap locales = new TreeMap();
+
+        // Preprocess the value to remove all whitespace
+        int white = value.indexOf(' ');
+        if (white < 0)
+            white = value.indexOf('\t');
+        if (white >= 0) {
+            StringBuffer sb = new StringBuffer();
+            int len = value.length();
+            for (int i = 0; i < len; i++) {
+                char ch = value.charAt(i);
+                if ((ch != ' ') && (ch != '\t'))
+                    sb.append(ch);
+            }
+            value = sb.toString();
+        }
+
+        // Process each comma-delimited language specification
+	StringParser parser = new StringParser();
+        parser.setString(value);        
+        int length = parser.getLength();
+        while (true) {
+
+            // Extract the next comma-delimited entry
+            int start = parser.getIndex();
+            if (start >= length)
+                break;
+            int end = parser.findChar(',');
+            String entry = parser.extract(start, end).trim();
+            parser.advance();   // For the following entry
+
+            // Extract the quality factor for this entry
+            double quality = 1.0;
+            int semi = entry.indexOf(";q=");
+            if (semi >= 0) {
+                try {
+                    quality = Double.parseDouble(entry.substring(semi + 3));
+                } catch (NumberFormatException e) {
+                    quality = 0.0;
+                }
+                entry = entry.substring(0, semi);
+            }
+
+            // Skip entries we are not going to keep track of
+            if (quality < 0.00005)
+                continue;       // Zero (or effectively zero) quality factors
+            if ("*".equals(entry))
+                continue;       // FIXME - "*" entries are not handled
+
+            // Extract the language and country for this entry
+            String language = null;
+            String country = null;
+            String variant = null;
+            int dash = entry.indexOf('-');
+            if (dash < 0) {
+                language = entry;
+                country = "";
+                variant = "";
+            } else {
+                language = entry.substring(0, dash);
+                country = entry.substring(dash + 1);
+                int vDash = country.indexOf('-');
+                if (vDash > 0) {
+                    String cTemp = country.substring(0, vDash);
+                    variant = country.substring(vDash + 1);
+                    country = cTemp;
+                } else {
+                    variant = "";
+                }
+            }
+
+            // Add a new Locale to the list of Locales for this quality level
+            Locale locale = new Locale(language, country, variant);
+            Double key = new Double(-quality);  // Reverse the order
+            ArrayList values = (ArrayList) locales.get(key);
+            if (values == null) {
+                values = new ArrayList();
+                locales.put(key, values);
+            }
+            values.add(locale);
+
+        }
+
+        // Process the quality values in highest->lowest order (due to
+        // negating the Double value when creating the key)
+        Iterator keys = locales.keySet().iterator();
+        while (keys.hasNext()) {
+            Double key = (Double) keys.next();
+            ArrayList list = (ArrayList) locales.get(key);
+            Iterator values = list.iterator();
+            while (values.hasNext()) {
+                Locale locale = (Locale) values.next();
+                addLocale(locale);
+            }
+        }
+
+    }
+}
+
+class Ajp13Principal implements java.security.Principal {
+    String user;
+    
+    Ajp13Principal(String user) {
+        this.user = user;
+    }
+    public boolean equals(Object o) {
+        if (o == null) {
+            return false;
+        } else if (!(o instanceof Ajp13Principal)) {
+            return false;
+        } else if (o == this) {
+            return true;
+        } else if (this.user == null && ((Ajp13Principal)o).user == null) {
+            return true;
+        } else if (user != null) {
+            return user.equals( ((Ajp13Principal)o).user);
+        } else {
+            return false;
+        }
+    }
+    
+    public String getName() {
+        return user;
+    }
+    
+    public int hashCode() {
+        if (user == null) return 0;
+        else return user.hashCode();
+    }
+    
+    public String toString() {
+        return getName();
+    }
+}
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Response.java b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Response.java
new file mode 100644
index 0000000..5e8aa0d
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/Ajp13Response.java
@@ -0,0 +1,165 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp.tomcat4;
+
+import java.io.IOException;
+
+import java.util.Iterator;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.catalina.connector.HttpResponseBase;
+import org.apache.catalina.Globals;
+import org.apache.catalina.util.CookieTools;
+
+import org.apache.ajp.Ajp13;
+import org.apache.tomcat.util.http.MimeHeaders;
+
+public class Ajp13Response extends HttpResponseBase {
+
+    private Ajp13 ajp13;
+    private boolean finished = false;
+    private boolean headersSent = false;
+    private MimeHeaders headers = new MimeHeaders();
+    private StringBuffer cookieValue = new StringBuffer();
+
+    String getStatusMessage() {
+        return getStatusMessage(getStatus());
+    }
+
+    public void recycle() {
+        super.recycle();
+        this.finished = false;
+        this.headersSent = false;
+        this.headers.recycle();
+    }
+
+    protected void sendHeaders()  throws IOException {
+
+        if (headersSent) {
+            // don't send headers twice
+            return;
+        }
+        headersSent = true;
+
+        int numHeaders = 0;
+
+        if (getContentType() != null) {
+            numHeaders++;
+	}
+        
+	if (getContentLength() >= 0) {
+            numHeaders++;
+	}
+
+	// Add the session ID cookie if necessary
+	HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
+	HttpSession session = hreq.getSession(false);
+
+	if ((session != null) && session.isNew() && (getContext() != null) 
+            && getContext().getCookies()) {
+	    Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,
+				       session.getId());
+	    cookie.setMaxAge(-1);
+	    String contextPath = null;
+            if (context != null)
+                contextPath = context.getPath();
+	    if ((contextPath != null) && (contextPath.length() > 0))
+		cookie.setPath(contextPath);
+	    else
+	        cookie.setPath("/");
+	    if (hreq.isSecure())
+		cookie.setSecure(true);
+	    addCookie(cookie);
+	}
+
+        // Send all specified cookies (if any)
+	synchronized (cookies) {
+	    Iterator items = cookies.iterator();
+	    while (items.hasNext()) {
+		Cookie cookie = (Cookie) items.next();
+
+                cookieValue.delete(0, cookieValue.length());
+                CookieTools.getCookieHeaderValue(cookie, cookieValue);
+                
+                addHeader(CookieTools.getCookieHeaderName(cookie),
+                          cookieValue.toString());
+	    }
+	}
+
+        // figure out how many headers...
+        // can have multiple headers of the same name...
+        // need to loop through headers once to get total
+        // count, once to add header to outBuf
+        String[] hnames = getHeaderNames();
+        Object[] hvalues = new Object[hnames.length];
+
+        int i;
+        for (i = 0; i < hnames.length; ++i) {
+            String[] tmp = getHeaderValues(hnames[i]);
+            numHeaders += tmp.length;
+            hvalues[i] = tmp;
+        }
+
+        ajp13.beginSendHeaders(getStatus(), getStatusMessage(getStatus()), numHeaders);
+
+        // send each header
+        if (getContentType() != null) {
+	    ajp13.sendHeader("Content-Type", getContentType());
+	}
+        
+	if (getContentLength() >= 0) {
+	    ajp13.sendHeader("Content-Length", String.valueOf(getContentLength()));
+	}
+
+        for (i = 0; i < hnames.length; ++i) {
+	    String name = hnames[i];
+            String[] values = (String[])hvalues[i];
+
+            for (int j = 0; j < values.length; ++j) {
+                ajp13.sendHeader(name, values[j]);
+            }
+        }
+
+        ajp13.endSendHeaders();
+
+        // The response is now committed
+        committed = true;
+    }
+
+    public void finishResponse() throws IOException {
+	if(!this.finished) {
+            try {
+                super.finishResponse();
+            } catch( Throwable t ) {
+                t.printStackTrace();
+            }
+            this.finished = true; // Avoid END_OF_RESPONSE sent 2 times
+	    ajp13.finish();
+	}        
+    }
+
+    void setAjp13(Ajp13 ajp13) {
+        this.ajp13 = ajp13;
+    }
+
+    Ajp13 getAjp13() {
+        return this.ajp13;
+    }
+}
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/Constants.java b/connectors/jk/java/org/apache/ajp/tomcat4/Constants.java
new file mode 100644
index 0000000..4cb093c
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/Constants.java
@@ -0,0 +1,21 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp.tomcat4;
+
+public interface Constants {
+    public static final String PACKAGE = "org.apache.ajp.tomcat4";
+}
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/JkServlet.java b/connectors/jk/java/org/apache/ajp/tomcat4/JkServlet.java
new file mode 100644
index 0000000..b6ae612
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/JkServlet.java
@@ -0,0 +1,95 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp.tomcat4;
+
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.ContainerServlet;
+import org.apache.catalina.Context;
+import org.apache.catalina.Wrapper;
+
+
+/**
+ * Module loader for JkServlet
+ *
+ * @author Costin Manolache
+ */
+public class JkServlet
+    extends HttpServlet implements ContainerServlet
+{
+    // -------------------- ContainerServlet interface --------------------
+    Wrapper wrapper;
+    
+    public Wrapper getWrapper() {
+	if( dL > 0 ) d("getWrapper()");
+	return wrapper;
+    }
+
+    public void setWrapper(Wrapper wrapper) {
+	if( dL > 0 ) d("setWrapper() " + wrapper );
+	this.wrapper=wrapper;
+    }
+
+    Context ctx;
+    
+    /**
+     * Initialize this servlet.
+     */
+    public void init() throws ServletException {
+	super.init();
+        if(wrapper == null) {
+	    log("No wrapper available, make sure the app is trusted");
+	    System.out.println("No wrapper available, make sure the app is trusted");
+	    return;
+	}
+
+	ctx=(Context) wrapper.getParent();
+
+	if( dL > 0 ) {
+	    d("Wrapper: " + wrapper.getClass().getName() + " " + wrapper );
+	    d("Ctx: " + ctx.getClass().getName() + " " + ctx );
+	    Object parent=ctx.getParent();
+	    d("P: " + parent.getClass().getName() + " " + parent );
+	    while( parent instanceof Container ) {
+		parent=((Container)parent).getParent();
+		d("P: " + parent.getClass().getName() + " " + parent );
+	    }
+	}
+	
+    }
+
+    public void service(HttpServletRequest request,
+                      HttpServletResponse response)
+        throws IOException, ServletException
+    {
+	throw new ServletException("Shouldn't be called direclty");
+    }
+    
+    private static final int dL=10;
+    private static void d(String s ) {
+        System.err.println( "JkServlet: " + s );
+    }
+
+
+}
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/LocalStrings.properties b/connectors/jk/java/org/apache/ajp/tomcat4/LocalStrings.properties
new file mode 100644
index 0000000..81468a2
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/LocalStrings.properties
@@ -0,0 +1,22 @@
+# $Id$
+
+# language
+
+# package org.apache.jk.ajp.tomcat4;
+
+ajp13Connector.alreadyStarted=Ajp13 connector has already been started
+ajp13Connector.allAddresses=Opening server socket on all host IP addresses
+ajp13Connector.failedSocketFactoryLoading=Failed to load socket factory
+ajp13Connector.failedToCreateSocket=Socket factory failed to create socket
+ajp13Connector.anAddress=Opening server socket on host IP address {0}
+ajp13Connector.noAddress=No host IP address matching {0}, opening on all addresses
+ajp13Connector.noProcessor=No processor available, rejecting this connection
+ajp13Connector.notStarted=Ajp13 connector has not yet been started
+ajp13Connector.starting=Starting background thread
+ajp13Connector.stopping=Stopping background thread
+
+ajp13Processor.alreadyStarted=Ajp13 processor has already been started
+ajp13Processor.notStarted=Ajp13 processor has not yet been started
+ajp13Processor.start=Ajp13 processor has already been started
+ajp13Processor.starting=Starting background thread
+ajp13Processor.stopping=Stopping background thread
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/LocalStrings_es.properties b/connectors/jk/java/org/apache/ajp/tomcat4/LocalStrings_es.properties
new file mode 100644
index 0000000..3883308
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/LocalStrings_es.properties
@@ -0,0 +1,22 @@
+# $Id$
+
+# language es
+
+# package org.apache.jk.ajp.tomcat4;
+
+ajp13Connector.alreadyStarted=Ya ha sido arrancado el conector Ajp13
+ajp13Connector.allAddresses=Abriendo enchufe (socket) de servidor en todas la direcciones IP de máquinas
+ajp13Connector.failedSocketFactoryLoading=No pude cargar fábrica de echufes (sockets)
+ajp13Connector.failedToCreateSocket=La fábrica de enchufes (sockets) no pudo crear enchufe (socket)
+ajp13Connector.anAddress=Abriendo enchufe (socket) de servidor en dirección IP de máquina {0}
+ajp13Connector.noAddress=No hay dirección IP de máquina que coincida con {0}, abriendo en todas las direcciones
+ajp13Connector.noProcessor=El procesador no está disponible, rechazando esta conexión
+ajp13Connector.notStarted=Aún no ha sido arrancado el conector Ajp13
+ajp13Connector.starting=Arrancado hilo en segundo plano
+ajp13Connector.stopping=Parando hilo en segundo plano
+
+ajp13Processor.alreadyStarted=Ya ha sido arrancado el procesador Ajp13
+ajp13Processor.notStarted=Aún no ha sido arrancado el procesador Ajp13
+ajp13Processor.start=Ya ha sido arrancado el procesador Ajp13
+ajp13Processor.starting=Arrancando hilo en segundo plano
+ajp13Processor.stopping=Parando hilo en segundo plano
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/LocalStrings_fr.properties b/connectors/jk/java/org/apache/ajp/tomcat4/LocalStrings_fr.properties
new file mode 100644
index 0000000..82b8c69
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/LocalStrings_fr.properties
@@ -0,0 +1,22 @@
+# $Id$
+
+# language fr
+
+# package org.apache.jk.ajp.tomcat4;
+
+ajp13Connector.alreadyStarted=le connecteur ajp13 a déjà été démarré
+ajp13Connector.allAddresses=Ouverture du serveur socket sur toutes les adresses IP
+ajp13Connector.failedSocketFactoryLoading=Impossible de charger le créateur de socket (socket factory)
+ajp13Connector.failedToCreateSocket=Le créateur de socket (socket factory) n'a pu créer un socket
+ajp13Connector.anAddress=Ouverture du serveur socket sur l''adresse IP {0}
+ajp13Connector.noAddress=Pas d''adresse IP correspondant à {0}, ouverture sur toutes les adresses
+ajp13Connector.noProcessor=Pas de processeur disponible, rejet de cette connexion
+ajp13Connector.notStarted=Le connecteur ajp13 n'a pas encore été démarré
+ajp13Connector.starting=Démarrage de la tache de fond (background thread)
+ajp13Connector.stopping=Arrêt de la tache de fond (background thread)
+
+ajp13Processor.alreadyStarted=Le processeur ajp13 a déjà été démarré
+ajp13Processor.notStarted=Le processeur ajp13 n'a pas encore été démarré
+ajp13Processor.start=Le processeur ajp13 a déjà été démarré
+ajp13Processor.starting=Démarrage de la tache de fond (background thread)
+ajp13Processor.stopping=Arrêt de la tache de fond (background thread)
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/LocalStrings_ja.properties b/connectors/jk/java/org/apache/ajp/tomcat4/LocalStrings_ja.properties
new file mode 100644
index 0000000..805a569
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/LocalStrings_ja.properties
@@ -0,0 +1,22 @@
+# $Id$
+
+# language ja
+
+# package org.apache.jk.ajp.tomcat4;
+
+ajp13Connector.alreadyStarted=AJP13\u30b3\u30cd\u30af\u30bf\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+ajp13Connector.allAddresses=\u3059\u3079\u3066\u306e\u30db\u30b9\u30c8IP\u30a2\u30c9\u30ec\u30b9\u306e\u30b5\u30fc\u30d0\u30bd\u30b1\u30c3\u30c8\u3092\u30aa\u30fc\u30d7\u30f3\u3057\u307e\u3059
+ajp13Connector.failedSocketFactoryLoading=\u30bd\u30b1\u30c3\u30c8\u30d5\u30a1\u30af\u30c8\u30ea\u306e\u30ed\u30fc\u30c9\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+ajp13Connector.failedToCreateSocket=\u30bd\u30b1\u30c3\u30c8\u30d5\u30a1\u30af\u30c8\u30ea\u306f\u30bd\u30b1\u30c3\u30c8\u306e\u4f5c\u6210\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+ajp13Connector.anAddress=\u30db\u30b9\u30c8IP\u30a2\u30c9\u30ec\u30b9 {0} \u306e\u30b5\u30fc\u30d0\u30bd\u30b1\u30c3\u30c8\u3092\u30aa\u30fc\u30d7\u30f3\u3057\u307e\u3059
+ajp13Connector.noAddress={0} \u306b\u4e00\u81f4\u3059\u308b\u30db\u30b9\u30c8IP\u30a2\u30c9\u30ec\u30b9\u306f\u5b58\u5728\u3057\u306a\u3044\u306e\u3067\u3001\u3059\u3079\u3066\u306e\u30a2\u30c9\u30ec\u30b9\u3092\u30aa\u30fc\u30d7\u30f3\u3057\u307e\u3059
+ajp13Connector.noProcessor=\u5229\u7528\u53ef\u80fd\u306a\u30d7\u30ed\u30bb\u30c3\u30b5\u304c\u306a\u3044\u306e\u3067\uff0c\u3053\u306e\u30b3\u30cd\u30af\u30b7\u30e7\u30f3\u3092\u62d2\u5426\u3057\u307e\u3059
+ajp13Connector.notStarted=AJP13\u30b3\u30cd\u30af\u30bf\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+ajp13Connector.starting=\u30d0\u30c3\u30af\u30b0\u30e9\u30a6\u30f3\u30c9\u30b9\u30ec\u30c3\u30c9\u3092\u8d77\u52d5\u3057\u307e\u3059
+ajp13Connector.stopping=\u30d0\u30c3\u30af\u30b0\u30e9\u30a6\u30f3\u30c9\u30b9\u30ec\u30c3\u30c9\u3092\u505c\u6b62\u3057\u307e\u3059
+
+ajp13Processor.alreadyStarted=AJP13\u30d7\u30ed\u30bb\u30c3\u30b5\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+ajp13Processor.notStarted=AJP13\u30d7\u30ed\u30bb\u30c3\u30b5\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+ajp13Processor.start=AJP13\u30d7\u30ed\u30bb\u30c3\u30b5\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+ajp13Processor.starting=\u30d0\u30c3\u30af\u30b0\u30e9\u30a6\u30f3\u30c9\u30b9\u30ec\u30c3\u30c9\u3092\u8d77\u52d5\u3057\u307e\u3059
+ajp13Processor.stopping=\u30d0\u30c3\u30af\u30b0\u30e9\u30a6\u30f3\u30c9\u30b9\u30ec\u30c3\u30c9\u3092\u505c\u6b62\u3057\u307e\u3059
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/config/ApacheConfig.java b/connectors/jk/java/org/apache/ajp/tomcat4/config/ApacheConfig.java
new file mode 100644
index 0000000..81945fc
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/config/ApacheConfig.java
@@ -0,0 +1,570 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp.tomcat4.config;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Date;
+import java.util.Hashtable;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
+
+/* The idea is to keep all configuration in server.xml and
+   the normal apache config files. We don't want people to
+   touch apache ( or IIS, NES ) config files unless they
+   want to and know what they're doing ( better than we do :-).
+
+   One nice feature ( if someone sends it ) would be to
+   also edit httpd.conf to add the include.
+
+   We'll generate a number of configuration files - this one
+   is trying to generate a native apache config file.
+
+   Some web.xml mappings do not "map" to server configuration - in
+   this case we need to fallback to forward all requests to tomcat.
+
+   Ajp14 will add to that the posibility to have tomcat and
+   apache on different machines, and many other improvements -
+   but this should also work for Ajp12, Ajp13 and Jni.
+
+*/
+
+/**
+    Generates automatic apache mod_jk configurations based on
+    the Tomcat server.xml settings and the war contexts
+    initialized during startup.
+    <p>
+    This config interceptor is enabled by inserting an ApacheConfig
+    <code>Listener</code> in 
+    the server.xml file like so:
+    <pre>
+    * < Server ... >
+    *   ...
+    *   <Listener className=<b>org.apache.ajp.tomcat4.config.ApacheConfig</b> 
+    *       <i>options</i> />
+    *   ...
+    * < /Server >
+    </pre>
+    where <i>options</i> can include any of the following attributes:
+    <ul>
+     <li><b>configHome</b> - default parent directory for the following paths.
+                            If not set, this defaults to TOMCAT_HOME. Ignored
+                            whenever any of the following paths is absolute.
+                             </li>
+     <li><b>jkConfig</b> - path to use for writing Apache mod_jk conf file. If
+                            not set, defaults to
+                            "conf/auto/mod_jk.conf".</li>
+     <li><b>workersConfig</b> - path to workers.properties file used by 
+                            mod_jk. If not set, defaults to
+                            "conf/jk/workers.properties".</li>
+     <li><b>modJk</b> - path to Apache mod_jk plugin file.  If not set,
+                        defaults to "modules/mod_jk.dll" on windows,
+                        "modules/mod_jk.nlm" on netware, and
+                        "libexec/mod_jk.so" everywhere else.</li>
+     <li><b>jkLog</b> - path to log file to be used by mod_jk.</li>
+     <li><b>jkDebug</b> - JK Loglevel setting.  May be debug, info, error, or emerg.
+                          If not set, defaults to emerg.</li>
+     <li><b>jkWorker</b> The desired worker.  Must be set to one of the workers
+                         defined in the workers.properties file. "ajp12", "ajp13"
+                         or "inprocess" are the workers found in the default
+                         workers.properties file. If not specified, defaults
+                         to "ajp13" if an Ajp13Interceptor is in use, otherwise
+                         it defaults to "ajp12".</li>
+     <li><b>forwardAll</b> - If true, forward all requests to Tomcat. This helps
+                             insure that all the behavior configured in the web.xml
+                             file functions correctly.  If false, let Apache serve
+                             static resources. The default is true.
+                             Warning: When false, some configuration in
+                             the web.xml may not be duplicated in Apache.
+                             Review the mod_jk conf file to see what
+                             configuration is actually being set in Apache.</li>
+     <li><b>noRoot</b> - If true, the root context is not mapped to
+                         Tomcat.  If false and forwardAll is true, all requests
+                         to the root context are mapped to Tomcat. If false and
+                         forwardAll is false, only JSP and servlets requests to
+                         the root context are mapped to Tomcat. When false,
+                         to correctly serve Tomcat's root context you must also
+                         modify the DocumentRoot setting in Apache's httpd.conf
+                         file to point to Tomcat's root context directory.
+                         Otherwise some content, such as Apache's index.html,
+                         will be served by Apache before mod_jk gets a chance
+                         to claim the request and pass it to Tomcat.
+                         The default is true.</li>
+    </ul>
+    <p>
+    @author Costin Manolache
+    @author Larry Isaacs
+    @author Mel Martinez
+    @author Bill Barker
+ */
+public class ApacheConfig  extends BaseJkConfig { 
+    
+    /** default path to mod_jk .conf location */
+    public static final String MOD_JK_CONFIG = "conf/auto/mod_jk.conf";
+    /** default path to workers.properties file
+	This should be also auto-generated from server.xml.
+    */
+    public static final String WORKERS_CONFIG = "conf/jk/workers.properties";
+    /** default mod_jk log file location */
+    public static final String JK_LOG_LOCATION = "logs/mod_jk.log";
+    /** default location of mod_jk Apache plug-in. */
+    public static final String MOD_JK;
+    
+    //set up some defaults based on OS type
+    static{
+        String os = System.getProperty("os.name").toLowerCase();
+        if(os.indexOf("windows")>=0){ 
+           MOD_JK = "modules/mod_jk.dll";
+        }else if(os.indexOf("netware")>=0){
+           MOD_JK = "modules/mod_jk.nlm";
+        }else{
+           MOD_JK = "libexec/mod_jk.so";
+        }
+    }
+    
+    private File jkConfig = null;
+    private File modJk = null;
+
+    // ssl settings 
+    private boolean sslExtract=true;
+    private String sslHttpsIndicator="HTTPS";
+    private String sslSessionIndicator="SSL_SESSION_ID";
+    private String sslCipherIndicator="SSL_CIPHER";
+    private String sslCertsIndicator="SSL_CLIENT_CERT";
+
+    Hashtable NamedVirtualHosts=null;
+    
+    public ApacheConfig() {
+    }
+
+    //-------------------- Properties --------------------
+
+    /**
+        set the path to the output file for the auto-generated
+        mod_jk configuration file.  If this path is relative
+        then it will be resolved absolutely against
+        the getConfigHome() path.
+        <p>
+        @param path String path to a file
+    */
+    public void setJkConfig(String path){
+	jkConfig= (path==null)?null:new File(path);
+    }
+
+    /**
+        set the path to the mod_jk Apache Module
+        @param path String path to a file
+    */
+    public void setModJk(String path){
+        modJk=( path==null?null:new File(path));
+    }
+
+    /** By default mod_jk is configured to collect SSL information from
+	the apache environment and send it to the Tomcat workers. The
+	problem is that there are many SSL solutions for Apache and as
+	a result the environment variable names may change.
+
+	The following JK related SSL configureation
+	can be used to customize mod_jk's SSL behaviour.
+
+	Should mod_jk send SSL information to Tomact (default is On)
+    */
+    public void setExtractSSL( boolean sslMode ) {
+	this.sslExtract=sslMode;
+    }
+
+    /** What is the indicator for SSL (default is HTTPS)
+     */
+    public void setHttpsIndicator( String s ) {
+	sslHttpsIndicator=s;
+    }
+
+    /**What is the indicator for SSL session (default is SSL_SESSION_ID)
+     */
+    public void setSessionIndicator( String s ) {
+	sslSessionIndicator=s;
+    }
+    
+    /**What is the indicator for client SSL cipher suit (default is SSL_CIPHER)
+     */
+    public void setCipherIndicator( String s ) {
+	sslCipherIndicator=s;
+    }
+
+    /** What is the indicator for the client SSL certificated(default
+	is SSL_CLIENT_CERT
+     */
+    public void setCertsIndicator( String s ) {
+	sslCertsIndicator=s;
+    }
+
+    // -------------------- Initialize/guess defaults --------------------
+
+    /** Initialize defaults for properties that are not set
+	explicitely
+    */
+    protected void initProperties() {
+        super.initProperties();
+
+	jkConfig= getConfigFile( jkConfig, configHome, MOD_JK_CONFIG);
+	workersConfig=getConfigFile( workersConfig, configHome,
+				     WORKERS_CONFIG);
+	if( modJk == null )
+	    modJk=new File(MOD_JK);
+	else
+	    modJk=getConfigFile( modJk, configHome, MOD_JK );
+	jkLog=getConfigFile( jkLog, configHome, JK_LOG_LOCATION);
+    }
+    // -------------------- Generate config --------------------
+    
+    protected PrintWriter getWriter() throws IOException {
+	String abJkConfig = jkConfig.getAbsolutePath();
+	return new PrintWriter(new FileWriter(abJkConfig, append));
+    }
+			       
+
+    // -------------------- Config sections  --------------------
+
+    /** Generate the loadModule and general options
+     */
+    protected boolean generateJkHead(PrintWriter mod_jk)
+    {
+
+	mod_jk.println("########## Auto generated on " +  new Date() +
+		       "##########" );
+	mod_jk.println();
+
+	// Fail if mod_jk not found, let the user know the problem
+	// instead of running into problems later.
+	if( ! modJk.exists() ) {
+	    log( "mod_jk location: " + modJk );
+	    log( "Make sure it is installed corectly or " +
+		 " set the config location" );
+	    log( "Using <Listener className=\""+getClass().getName()+"\"  modJk=\"PATH_TO_MOD_JK.SO_OR_DLL\" />" );
+	}
+            
+	// Verify the file exists !!
+	mod_jk.println("<IfModule !mod_jk.c>");
+	mod_jk.println("  LoadModule jk_module \""+
+		       modJk.toString().replace('\\','/') +
+                       "\"");
+	mod_jk.println("</IfModule>");
+	mod_jk.println();                
+
+	
+	// Fail if workers file not found, let the user know the problem
+	// instead of running into problems later.
+	if( ! workersConfig.exists() ) {
+	    log( "Can't find workers.properties at " + workersConfig );
+	    log( "Please install it in the default location or " +
+		 " set the config location" );
+	    log( "Using <Listener className=\"" + getClass().getName() + "\"  workersConfig=\"FULL_PATH\" />" );
+	    return false;
+	}
+            
+	mod_jk.println("JkWorkersFile \"" 
+		       + workersConfig.toString().replace('\\', '/') 
+		       + "\"");
+
+	mod_jk.println("JkLogFile \"" 
+		       + jkLog.toString().replace('\\', '/') 
+		       + "\"");
+	mod_jk.println();
+
+	if( jkDebug != null ) {
+	    mod_jk.println("JkLogLevel " + jkDebug);
+	    mod_jk.println();
+	}
+	return true;
+    }
+
+    protected void generateVhostHead(Host host, PrintWriter mod_jk) {
+
+        mod_jk.println();
+        String vhostip = host.getName();
+	String vhost = vhostip;
+	int ppos = vhost.indexOf(":");
+	if(ppos >= 0)
+	    vhost = vhost.substring(0,ppos);
+
+        mod_jk.println("<VirtualHost "+ vhostip + ">");
+        mod_jk.println("    ServerName " + vhost );
+        String [] aliases=host.findAliases();
+        if( aliases.length > 0 ) {
+            mod_jk.print("    ServerAlias " );
+            for( int ii=0; ii < aliases.length ; ii++) {
+                mod_jk.print( aliases[ii] + " " );
+            }
+            mod_jk.println();
+        }
+        indent="    ";
+    }
+
+    protected void generateVhostTail(Host host, PrintWriter mod_jk) {
+        mod_jk.println("</VirtualHost>");
+        indent="";
+    }
+    
+    protected void generateSSLConfig(PrintWriter mod_jk) {
+	if( ! sslExtract ) {
+	    mod_jk.println("JkExtractSSL Off");        
+	}
+	if( ! "HTTPS".equalsIgnoreCase( sslHttpsIndicator ) ) {
+	    mod_jk.println("JkHTTPSIndicator " + sslHttpsIndicator);        
+	}
+	if( ! "SSL_SESSION_ID".equalsIgnoreCase( sslSessionIndicator )) {
+	    mod_jk.println("JkSESSIONIndicator " + sslSessionIndicator);
+	}
+	if( ! "SSL_CIPHER".equalsIgnoreCase( sslCipherIndicator )) {
+	    mod_jk.println("JkCIPHERIndicator " + sslCipherIndicator);
+	}
+	if( ! "SSL_CLIENT_CERT".equalsIgnoreCase( sslCertsIndicator )) {
+	    mod_jk.println("JkCERTSIndicator " + sslCertsIndicator);
+	}
+
+	mod_jk.println();
+    }
+
+    // -------------------- Forward all mode --------------------
+    String indent="";
+    
+    /** Forward all requests for a context to tomcat.
+	The default.
+     */
+    protected void generateStupidMappings(Context context,
+					   PrintWriter mod_jk )
+    {
+	String ctxPath  = context.getPath();
+	if(ctxPath == null)
+	    return;
+
+	String nPath=("".equals(ctxPath)) ? "/" : ctxPath;
+	
+        mod_jk.println();
+	mod_jk.println(indent + "JkMount " +  nPath + " " + jkWorker );
+	if( "".equals(ctxPath) ) {
+	    mod_jk.println(indent + "JkMount " +  nPath + "* " + jkWorker );
+            if ( context.getParent() instanceof Host ) {
+                mod_jk.println(indent + "DocumentRoot \"" +
+                            getApacheDocBase(context) + "\"");
+            } else {
+                mod_jk.println(indent +
+                        "# To avoid Apache serving root welcome files from htdocs, update DocumentRoot");
+                mod_jk.println(indent +
+                        "# to point to: \"" + getApacheDocBase(context) + "\"");
+            }
+
+	} else {
+	    mod_jk.println(indent + "JkMount " +  nPath + "/* " + jkWorker );
+	}
+    }    
+
+    
+    private void generateNameVirtualHost( PrintWriter mod_jk, String ip ) {
+        if( !NamedVirtualHosts.containsKey(ip) ) {
+            mod_jk.println("NameVirtualHost " + ip + "");
+            NamedVirtualHosts.put(ip,ip);
+        }
+    }
+    
+    // -------------------- Apache serves static mode --------------------
+    // This is not going to work for all apps. We fall back to stupid mode.
+    
+    protected void generateContextMappings(Context context, PrintWriter mod_jk )
+    {
+	String ctxPath  = context.getPath();
+	Host vhost = getHost(context);
+
+        if( noRoot &&  "".equals(ctxPath) ) {
+            log("Ignoring root context in non-forward-all mode  ");
+            return;
+        }
+
+	mod_jk.println();
+	mod_jk.println(indent + "#################### " +
+		       ((vhost!=null ) ? vhost.getName() + ":" : "" ) +
+		       (("".equals(ctxPath)) ? "/" : ctxPath ) +
+		       " ####################" );
+        mod_jk.println();
+	// Dynamic /servet pages go to Tomcat
+ 
+	generateStaticMappings( context, mod_jk );
+
+	// InvokerInterceptor - it doesn't have a container,
+	// but it's implemented using a special module.
+	
+	// XXX we need to better collect all mappings
+
+	if(context.getLoginConfig() != null) {
+	    String loginPage = context.getLoginConfig().getLoginPage();
+	    if(loginPage != null) {
+		int lpos = loginPage.lastIndexOf("/");
+		String jscurl = loginPage.substring(0,lpos+1) + "j_security_check";
+		addMapping( ctxPath, jscurl, mod_jk);
+	    }
+	}
+	String [] servletMaps = context.findServletMappings();
+	for(int ii=0; ii < servletMaps.length; ii++) {
+	      addMapping( ctxPath, servletMaps[ii] , mod_jk );
+	}
+    }
+
+    /** Add an Apache extension mapping.
+     */
+    protected boolean addExtensionMapping( String ctxPath, String ext,
+					 PrintWriter mod_jk )
+    {
+        if( debug > 0 )
+            log( "Adding extension map for " + ctxPath + "/*." + ext );
+	mod_jk.println(indent + "JkMount " + ctxPath + "/*." + ext
+		       + " " + jkWorker);
+	return true;
+    }
+    
+    
+    /** Add a fulling specified Appache mapping.
+     */
+    protected boolean addMapping( String fullPath, PrintWriter mod_jk ) {
+        if( debug > 0 )
+            log( "Adding map for " + fullPath );
+	mod_jk.println(indent + "JkMount " + fullPath + "  " + jkWorker );
+	return true;
+    }
+    /** Add a partially specified Appache mapping.
+     */
+    protected boolean addMapping( String ctxP, String ext, PrintWriter mod_jk ) {
+        if( debug > 0 )
+            log( "Adding map for " + ext );
+	if(! ext.startsWith("/") )
+	    ext = "/" + ext;
+	if(ext.length() > 1)
+	    mod_jk.println(indent + "JkMount " + ctxP + ext+ "  " + jkWorker );
+	return true;
+    }
+
+    private void generateWelcomeFiles(Context context, PrintWriter mod_jk ) {
+	String wf[]=context.findWelcomeFiles();
+	if( wf==null || wf.length == 0 )
+	    return;
+	mod_jk.print(indent + "    DirectoryIndex ");
+	for( int i=0; i<wf.length ; i++ ) {
+	    mod_jk.print( wf[i] + " " );
+	}
+	mod_jk.println();
+    }
+
+    /** Mappings for static content. XXX need to add welcome files,
+     *  mime mappings ( all will be handled by Mime and Static modules of
+     *  apache ).
+     */
+    private void generateStaticMappings(Context context, PrintWriter mod_jk ) {
+	String ctxPath  = context.getPath();
+
+	// Calculate the absolute path of the document base
+	String docBase = getApacheDocBase(context);
+
+        if( !"".equals(ctxPath) ) {
+            // Static files will be served by Apache
+            mod_jk.println(indent + "# Static files ");		    
+            mod_jk.println(indent + "Alias " + ctxPath + " \"" + docBase + "\"");
+            mod_jk.println();
+        } else {
+            if ( getHost(context) != null ) {
+                mod_jk.println(indent + "DocumentRoot \"" +
+                            getApacheDocBase(context) + "\"");
+            } else {
+                // For root context, ask user to update DocumentRoot setting.
+                // Using "Alias / " interferes with the Alias for other contexts.
+                mod_jk.println(indent +
+                        "# Be sure to update DocumentRoot");
+                mod_jk.println(indent +
+                        "# to point to: \"" + docBase + "\"");
+            }
+        }
+	mod_jk.println(indent + "<Directory \"" + docBase + "\">");
+	mod_jk.println(indent + "    Options Indexes FollowSymLinks");
+
+	generateWelcomeFiles(context, mod_jk);
+
+	// XXX XXX Here goes the Mime types and welcome files !!!!!!!!
+	mod_jk.println(indent + "</Directory>");
+	mod_jk.println();            
+	
+
+	// Deny serving any files from WEB-INF
+	mod_jk.println();            
+	mod_jk.println(indent +
+		       "# Deny direct access to WEB-INF and META-INF");
+	mod_jk.println(indent + "#");                        
+	mod_jk.println(indent + "<Location \"" + ctxPath + "/WEB-INF/*\">");
+	mod_jk.println(indent + "    AllowOverride None");
+	mod_jk.println(indent + "    deny from all");
+	mod_jk.println(indent + "</Location>");
+	// Deny serving any files from META-INF
+	mod_jk.println();            
+	mod_jk.println(indent + "<Location \"" + ctxPath + "/META-INF/*\">");
+	mod_jk.println(indent + "    AllowOverride None");
+	mod_jk.println(indent + "    deny from all");
+	mod_jk.println(indent + "</Location>");
+	if (File.separatorChar == '\\') {
+	    mod_jk.println(indent + "#");		    
+	    mod_jk.println(indent +
+			   "# Use Directory too. On Windows, Location doesn't"
+			   + " work unless case matches");
+	    mod_jk.println(indent + "#");                        
+	    mod_jk.println(indent +
+			   "<Directory \"" + docBase + "/WEB-INF/\">");
+	    mod_jk.println(indent + "    AllowOverride None");
+	    mod_jk.println(indent + "    deny from all");
+	    mod_jk.println(indent + "</Directory>");
+	    mod_jk.println();
+	    mod_jk.println(indent +
+			   "<Directory \"" + docBase + "/META-INF/\">");
+	    mod_jk.println(indent + "    AllowOverride None");
+	    mod_jk.println(indent + "    deny from all");
+	    mod_jk.println(indent + "</Directory>");
+	}
+	mod_jk.println();
+    }    
+
+    // -------------------- Utils --------------------
+
+    private String getApacheDocBase(Context context)
+    {
+	// Calculate the absolute path of the document base
+	String docBase = getAbsoluteDocBase(context);
+	if (File.separatorChar == '\\') {
+	    // use separator preferred by Apache
+	    docBase = docBase.replace('\\','/');
+	}
+        return docBase;
+    }
+
+    private String getVirtualHostAddress(String vhost, String vhostip) {
+        if( vhostip == null ) {
+            if ( vhost != null && vhost.length() > 0 && Character.isDigit(vhost.charAt(0)) )
+                vhostip=vhost;
+            else
+                vhostip="*";
+        }
+        return vhostip;
+    }
+
+}
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/config/BaseJkConfig.java b/connectors/jk/java/org/apache/ajp/tomcat4/config/BaseJkConfig.java
new file mode 100644
index 0000000..4afc9e0
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/config/BaseJkConfig.java
@@ -0,0 +1,485 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp.tomcat4.config;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Server;
+
+
+/**
+    Base class for automatic jk based configurations based on
+    the Tomcat server.xml settings and the war contexts
+    initialized during startup.
+    <p>
+    This config interceptor is enabled by inserting a Config
+    element in the <b>&lt;ContextManager&gt;</b> tag body inside
+    the server.xml file like so:
+    <pre>
+    * < ContextManager ... >
+    *   ...
+    *   <<b>???Config</b> <i>options</i> />
+    *   ...
+    * < /ContextManager >
+    </pre>
+    where <i>options</i> can include any of the following attributes:
+    <ul>
+     <li><b>configHome</b> - default parent directory for the following paths.
+                             If not set, this defaults to TOMCAT_HOME. Ignored
+                             whenever any of the following paths is absolute.
+                             </li>
+     <li><b>workersConfig</b> - path to workers.properties file used by 
+                                jk connector. If not set, defaults to
+                                "conf/jk/workers.properties".</li>
+     <li><b>jkLog</b> - path to log file to be used by jk connector.</li>
+     <li><b>jkDebug</b> - Loglevel setting.  May be debug, info, error, or emerg.
+                          If not set, defaults to emerg.</li>
+     <li><b>jkWorker</b> The desired worker.  Must be set to one of the workers
+                         defined in the workers.properties file. "ajp12", "ajp13"
+                         or "inprocess" are the workers found in the default
+                         workers.properties file. If not specified, defaults
+                         to "ajp13" if an Ajp13Interceptor is in use, otherwise
+                         it defaults to "ajp12".</li>
+     <li><b>forwardAll</b> - If true, forward all requests to Tomcat. This helps
+                             insure that all the behavior configured in the web.xml
+                             file functions correctly.  If false, let Apache serve
+                             static resources. The default is true.
+                             Warning: When false, some configuration in
+                             the web.xml may not be duplicated in Apache.
+                             Review the mod_jk conf file to see what
+                             configuration is actually being set in Apache.</li>
+     <li><b>noRoot</b> - If true, the root context is not mapped to
+                         Tomcat.  If false and forwardAll is true, all requests
+                         to the root context are mapped to Tomcat. If false and
+                         forwardAll is false, only JSP and servlets requests to
+                         the root context are mapped to Tomcat. When false,
+                         to correctly serve Tomcat's root context you may also
+                         need to modify the web server to point it's home
+                         directory to Tomcat's root context directory.
+                         Otherwise some content, such as the root index.html,
+                         may be served by the web server before the connector
+                         gets a chance to claim the request and pass it to Tomcat.
+                         The default is true.</li>
+    </ul>
+    <p>
+    @author Costin Manolache
+    @author Larry Isaacs
+    @author Bill Barker
+	@version $Revision$
+ */
+public class BaseJkConfig  implements LifecycleListener {
+    protected int debug=0;
+    protected File configHome = null;
+    protected File workersConfig = null;
+
+    protected File jkLog = null;
+    protected String jkDebug="emerg";
+    protected String jkWorker = "ajp13";
+
+    protected boolean noRoot=true;
+    protected boolean forwardAll=true;
+
+    protected String tomcatHome;
+    protected boolean regenerate=false;
+    protected boolean append=false;
+
+    // -------------------- Tomcat callbacks --------------------
+
+
+    // Auto-config should be able to react to dynamic config changes,
+    // and regenerate the config.
+
+    /** Generate the configuration - only when the server is
+     *  completely initialized ( before starting )
+     */
+    public void lifecycleEvent(LifecycleEvent evt)
+    {
+	if(Lifecycle.START_EVENT.equals(evt.getType())) {
+	   execute( evt );
+	}
+    }
+
+    /** Generate configuration files.  Override with method to generate
+        web server specific configuration.
+     */
+    public void execute(LifecycleEvent evt)
+    {
+	initProperties();
+	PrintWriter mod_jk = null;
+	try {
+	    mod_jk = getWriter();
+	} catch(IOException iex) {
+	    log("Unable to open config file");
+	    return;
+	}
+	Lifecycle who = evt.getLifecycle();
+	if( who instanceof Server ) {
+	    executeServer((Server)who, mod_jk);
+	} else if ( who instanceof Host ) {
+	    executeHost((Host)who, mod_jk);
+	} else if( who instanceof Context ) {
+	    executeContext((Context)who, mod_jk);
+	}
+	mod_jk.close();
+    }
+    /** Generate configuration files.  Override with method to generate
+        web server specific configuration.
+     */
+    public void executeServer(Server svr, PrintWriter mod_jk) {
+	if(! append ) {
+	    if( ! generateJkHead(mod_jk) )
+		return;
+	    generateSSLConfig(mod_jk);
+	    generateJkTail(mod_jk);
+	}
+    }
+
+    /** Generate SSL options
+     */
+    protected void generateSSLConfig(PrintWriter mod_jk)
+    {
+    }
+    /** Generate general options
+     */
+    protected boolean generateJkHead(PrintWriter mod_jk)
+    {
+	return true;
+    }
+    /** Generate general options
+     */
+    protected void generateJkTail(PrintWriter mod_jk)
+    {
+    }
+    /** Generate Virtual Host start
+     */
+    protected void generateVhostHead(Host host, PrintWriter mod_jk) {
+    }
+    /** Generate Virtual Host end
+     */
+    protected void generateVhostTail(Host host, PrintWriter mod_jk) {
+    }
+    /** Generate configuration files.  Override with method to generate
+        web server specific configuration.
+     */
+    protected void executeEngine(Engine egn, PrintWriter mod_jk) {
+	Container [] children = egn.findChildren();
+	for(int ii=0; ii < children.length; ii++) {
+	    if( children[ii] instanceof Host ) {
+		executeHost((Host)children[ii], mod_jk);
+	    } else if( children[ii] instanceof Context ) {
+		executeContext((Context)children[ii], mod_jk);
+	    }
+	}
+    }
+    /** Generate configuration files.  Override with method to generate
+        web server specific configuration.
+     */
+    protected void executeHost(Host hst, PrintWriter mod_jk) {
+	generateVhostHead(hst, mod_jk);
+	Container [] children = hst.findChildren();
+	for(int ii=0; ii < children.length; ii++) {
+	    if(children[ii] instanceof Context) {
+		executeContext((Context)children[ii],mod_jk);
+	    }
+	}
+	generateVhostTail(hst, mod_jk);
+    }
+    /**
+        executes the ApacheConfig interceptor. This method generates apache
+        configuration files for use with  mod_jk.
+        <p>
+        @param context a Context object.
+	@param mod_jk Writer for output.
+    */
+    public void executeContext(Context context, PrintWriter mod_jk){
+
+	if(context.getPath().length() > 0 || ! noRoot ) {
+	    String docRoot = context.getServletContext().getRealPath("/");
+	    if( forwardAll || docRoot == null)
+		generateStupidMappings( context, mod_jk );
+	    else
+		generateContextMappings( context, mod_jk);
+	}
+    }
+    protected void generateStupidMappings(Context context, PrintWriter mod_jk){
+    }
+    protected void generateContextMappings(Context context, PrintWriter mod_jk){
+    }
+    /** Get the output Writer.  Override with method to generate
+        web server specific configuration.
+     */
+    protected PrintWriter getWriter() throws IOException {
+	return null;
+    }
+    /** Get the host associated with this Container (if any).
+     */
+    protected Host getHost(Container child) {
+	while(child != null && ! (child instanceof Host) ) {
+	    child = child.getParent();
+	}
+	return (Host)child;
+    }
+
+    //-------------------- Properties --------------------
+
+    /** Append to config file.
+     *  Set to <code>true</code> if the config information should be 
+     *  appended.
+     */
+    public void setAppend(boolean apnd) {
+	append = apnd;
+    }
+    /** If false, we'll try to generate a config that will
+     *  let apache serve static files.
+     *  The default is true, forward all requests in a context
+     *  to tomcat.
+     */
+    public void setForwardAll( boolean b ) {
+        forwardAll=b;
+    }
+
+    /** Special option - do not generate mappings for the ROOT
+        context. The default is true, and will not generate the mappings,
+        not redirecting all pages to tomcat (since /* matches everything).
+        This means that the web server's root remains intact but isn't
+        completely servlet/JSP enabled. If the ROOT webapp can be configured
+        with the web server serving static files, there's no problem setting
+        this option to false. If not, then setting it true means the web
+        server will be out of picture for all requests.
+    */
+    public void setNoRoot( boolean b ) {
+        noRoot=b;
+    }
+    
+    /**
+        set a path to the parent directory of the
+        conf folder.  That is, the parent directory
+        within which path setters would be resolved against,
+        if relative.  For example if ConfigHome is set to "/home/tomcat"
+        and regConfig is set to "conf/mod_jk.conf" then the resulting 
+        path used would be: 
+        "/home/tomcat/conf/mod_jk.conf".</p>
+        <p>
+        However, if the path is set to an absolute path,
+        this attribute is ignored.
+        <p>
+        If not set, execute() will set this to TOMCAT_HOME.
+        <p>
+        @param dir - path to a directory
+    */
+    public void setConfigHome(String dir){
+        if( dir==null ) return;
+        File f=new File(dir);
+        if(!f.isDirectory()){
+            throw new IllegalArgumentException(
+                "BaseConfig.setConfigHome(): "+
+                "Configuration Home must be a directory! : "+dir);
+        }
+        configHome = f;
+    }
+
+    /**
+        set a path to the workers.properties file.
+        @param path String path to workers.properties file
+    */
+    public void setWorkersConfig(String path){
+        workersConfig= (path==null?null:new File(path));
+    }
+
+    /**
+        set the path to the log file
+        @param path String path to a file
+    */
+    public void setJkLog(String path){
+        jkLog= ( path==null?null:new File(path));
+    }
+
+    /** Set the verbosity level
+        ( use debug, error, etc. ) If not set, no log is written.
+     */
+    public void setJkDebug( String level ) {
+        jkDebug=level;
+    }
+
+    /**
+        Set the AJP worker.
+        @param worker The worker name
+     */
+    public void setJkWorker(String worker){
+        jkWorker = worker;
+    }
+
+    // -------------------- Initialize/guess defaults --------------------
+
+    /** Initialize defaults for properties that are not set
+        explicitely
+    */
+    protected void initProperties() {
+        tomcatHome = System.getProperty("catalina.home");
+        File tomcatDir = new File(tomcatHome);
+        if(configHome==null){
+            configHome=tomcatDir;
+        }
+    }
+
+    // -------------------- Config Utils  --------------------
+
+
+    /** Add an extension mapping. Override with method to generate
+        web server specific configuration
+     */
+    protected boolean addExtensionMapping( String ctxPath, String ext,
+					 PrintWriter pw )
+    {
+	return true;
+    }
+    
+    
+    /** Add a fulling specified mapping.  Override with method to generate
+        web server specific configuration
+     */
+    protected boolean addMapping( String fullPath, PrintWriter pw ) {
+	return true;
+    }
+
+    // -------------------- General Utils --------------------
+
+    protected String getAbsoluteDocBase(Context context)
+    {
+	// Calculate the absolute path of the document base
+	String docBase = context.getServletContext().getRealPath("/");
+	docBase = docBase.substring(0,docBase.length()-1);
+	if (!isAbsolute(docBase)){
+	    docBase = tomcatHome + "/" + docBase;
+	}
+	docBase = patch(docBase);
+        return docBase;
+    }
+
+    // ------------------ Grabbed from FileUtil -----------------
+    public static File getConfigFile( File base, File configDir, String defaultF )
+    {
+        if( base==null )
+            base=new File( defaultF );
+        if( ! base.isAbsolute() ) {
+            if( configDir != null )
+                base=new File( configDir, base.getPath());
+            else
+                base=new File( base.getAbsolutePath()); //??
+        }
+        File parent=new File(base.getParent());
+        if(!parent.exists()){
+            if(!parent.mkdirs()){
+                throw new RuntimeException(
+                    "Unable to create path to config file :"+
+                    base.getAbsolutePath());
+            }
+        }
+        return base;
+    }
+    public static String patch(String path) {
+        String patchPath = path;
+
+        // Move drive spec to the front of the path
+        if (patchPath.length() >= 3 &&
+            patchPath.charAt(0) == '/'  &&
+            Character.isLetter(patchPath.charAt(1)) &&
+            patchPath.charAt(2) == ':') {
+            patchPath=patchPath.substring(1,3)+"/"+patchPath.substring(3);
+        }
+
+        // Eliminate consecutive slashes after the drive spec
+	if (patchPath.length() >= 2 &&
+            Character.isLetter(patchPath.charAt(0)) &&
+            patchPath.charAt(1) == ':') {
+            char[] ca = patchPath.replace('/', '\\').toCharArray();
+            char c;
+            StringBuffer sb = new StringBuffer();
+
+            for (int i = 0; i < ca.length; i++) {
+                if ((ca[i] != '\\') ||
+                    (ca[i] == '\\' &&
+                        i > 0 &&
+                        ca[i - 1] != '\\')) {
+                    if (i == 0 &&
+                        Character.isLetter(ca[i]) &&
+                        i < ca.length - 1 &&
+                        ca[i + 1] == ':') {
+                        c = Character.toUpperCase(ca[i]);
+                    } else {
+                        c = ca[i];
+                    }
+
+                    sb.append(c);
+                }
+            }
+
+            patchPath = sb.toString();
+        }
+
+	// fix path on NetWare - all '/' become '\\' and remove duplicate '\\'
+	if (System.getProperty("os.name").startsWith("NetWare") &&
+	    path.length() >=3 &&
+	    path.indexOf(':') > 0) {
+	    char[] ca = patchPath.replace('/', '\\').toCharArray();
+	    StringBuffer sb = new StringBuffer();
+
+	    for (int i = 0; i < ca.length; i++) {
+		if ((ca[i] != '\\') ||
+		    (ca[i] == '\\' && i > 0 && ca[i - 1] != '\\')) {
+		    sb.append(ca[i]);
+		}
+	    }
+	    patchPath = sb.toString();
+	}
+
+        return patchPath;
+    }
+
+    public static boolean isAbsolute( String path ) {
+	// normal file
+	if( path.startsWith("/" ) ) return true;
+
+	if( path.startsWith(File.separator ) ) return true;
+
+	// win c:
+	if (path.length() >= 3 &&
+            Character.isLetter(path.charAt(0)) &&
+            path.charAt(1) == ':')
+	    return true;
+
+	// NetWare volume:
+	if (System.getProperty("os.name").startsWith("NetWare") &&
+	    path.length() >=3 &&
+	    path.indexOf(':') > 0)
+	    return true;
+
+	return false;
+    }
+    protected void log(String msg) {
+	System.err.println(msg);
+    }
+}
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/config/IISConfig.java b/connectors/jk/java/org/apache/ajp/tomcat4/config/IISConfig.java
new file mode 100644
index 0000000..03a29b9
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/config/IISConfig.java
@@ -0,0 +1,303 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp.tomcat4.config;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Date;
+
+import org.apache.catalina.Context;
+
+
+/**
+    Generates automatic IIS isapi_redirect configurations based on
+    the Tomcat server.xml settings and the war contexts
+    initialized during startup.
+    <p>
+    This config interceptor is enabled by inserting an IISConfig
+    element in the <b>&lt;ContextManager&gt;</b> tag body inside
+    the server.xml file like so:
+    <pre>
+    * < ContextManager ... >
+    *   ...
+    *   <<b>IISConfig</b> <i>options</i> />
+    *   ...
+    * < /ContextManager >
+    </pre>
+    where <i>options</i> can include any of the following attributes:
+    <ul>
+     <li><b>configHome</b> - default parent directory for the following paths.
+                            If not set, this defaults to TOMCAT_HOME. Ignored
+                            whenever any of the following paths is absolute.
+                             </li>
+     <li><b>regConfig</b> - path to use for writing IIS isapi_redirect registry
+                            file. If not set, defaults to
+                            "conf/auto/iis_redirect.reg".</li>
+     <li><b>workersConfig</b> - path to workers.properties file used by 
+                                isapi_redirect. If not set, defaults to
+                                "conf/jk/workers.properties".</li>
+     <li><b>uriConfig</b> - path to use for writing IIS isapi_redirect uriworkermap
+                            file. If not set, defaults to
+                            "conf/auto/uriworkermap.properties".</li>
+     <li><b>jkLog</b> - path to log file to be used by isapi_redirect.</li>
+     <li><b>jkDebug</b> - Loglevel setting.  May be debug, info, error, or emerg.
+                          If not set, defaults to emerg.</li>
+     <li><b>jkWorker</b> The desired worker.  Must be set to one of the workers
+                         defined in the workers.properties file. "ajp12", "ajp13"
+                         or "inprocess" are the workers found in the default
+                         workers.properties file. If not specified, defaults
+                         to "ajp13" if an Ajp13Interceptor is in use, otherwise
+                         it defaults to "ajp12".</li>
+     <li><b>forwardAll</b> - If true, forward all requests to Tomcat. This helps
+                             insure that all the behavior configured in the web.xml
+                             file functions correctly.  If false, let IIS serve
+                             static resources assuming it has been configured
+                             to do so. The default is true.
+                             Warning: When false, some configuration in
+                             the web.xml may not be duplicated in IIS.
+                             Review the uriworkermap file to see what
+                             configuration is actually being set in IIS.</li>
+     <li><b>noRoot</b> - If true, the root context is not mapped to
+                         Tomcat.  If false and forwardAll is true, all requests
+                         to the root context are mapped to Tomcat. If false and
+                         forwardAll is false, only JSP and servlets requests to
+                         the root context are mapped to Tomcat. When false,
+                         to correctly serve Tomcat's root context you must also
+                         modify the Home Directory setting in IIS
+                         to point to Tomcat's root context directory.
+                         Otherwise some content, such as the root index.html,
+                         will be served by IIS before isapi_redirect gets a chance
+                         to claim the request and pass it to Tomcat.
+                         The default is true.</li>
+    </ul>
+  <p>
+    @author Costin Manolache
+    @author Larry Isaacs
+    @author Gal Shachor
+    @author Bill Barker
+ */
+public class IISConfig extends BaseJkConfig  { 
+
+    public static final String WORKERS_CONFIG = "/conf/jk/workers.properties";
+    public static final String URI_WORKERS_MAP_CONFIG = "/conf/auto/uriworkermap.properties";
+    public static final String ISAPI_LOG_LOCATION = "/logs/iis_redirect.log";
+    public static final String ISAPI_REG_FILE = "/conf/auto/iis_redirect.reg";    
+
+    private File regConfig = null;
+    private File uriConfig = null;
+
+    public IISConfig() 
+    {
+    }
+
+    //-------------------- Properties --------------------
+    
+    /**
+        set the path to the output file for the auto-generated
+        isapi_redirect registry file.  If this path is relative
+        then getRegConfig() will resolve it absolutely against
+        the getConfigHome() path.
+        <p>
+        @param path String path to a file
+    */
+    public void setRegConfig(String path){
+	regConfig= (path==null)?null:new File(path);
+    }
+
+    /**
+        set a path to the uriworkermap.properties file.
+        @param path String path to uriworkermap.properties file
+    */
+    public void setUriConfig(String path){
+        uriConfig= (path==null?null:new File(path));
+    }
+
+    // -------------------- Initialize/guess defaults --------------------
+
+    /** Initialize defaults for properties that are not set
+	explicitely
+    */
+    protected void initProperties() {
+        super.initProperties();
+
+	regConfig=getConfigFile( regConfig, configHome, ISAPI_REG_FILE);
+	workersConfig=getConfigFile( workersConfig, configHome, WORKERS_CONFIG);
+	uriConfig=getConfigFile( uriConfig, configHome, URI_WORKERS_MAP_CONFIG);
+	jkLog=getConfigFile( jkLog, configHome, ISAPI_LOG_LOCATION);
+    }
+
+    // -------------------- Generate config --------------------
+
+    protected PrintWriter getWriter() throws IOException {
+	String abUriConfig = uriConfig.getAbsolutePath();
+	return new PrintWriter(new FileWriter(abUriConfig,append));        
+    }
+    protected boolean generateJkHead(PrintWriter mod_jk) {
+	try {
+	    PrintWriter regfile = new PrintWriter(new FileWriter(regConfig));
+	    log("Generating IIS registry file = "+regConfig );
+	    generateRegistrySettings(regfile);
+	    regfile.close();
+	} catch(IOException iex) {
+	    log("Unable to generate registry file " +regConfig);
+	    return false;
+	}
+	log("Generating IIS URI worker map file = "+uriConfig );
+	generateUriWorkerHeader(mod_jk);            
+	return true;
+    }
+
+    // -------------------- Config sections  --------------------
+
+    /** Writes the registry settings required by the IIS connector
+     */
+    private void generateRegistrySettings(PrintWriter regfile)
+    {
+        regfile.println("REGEDIT4");
+        regfile.println();
+        regfile.println("[HKEY_LOCAL_MACHINE\\SOFTWARE\\Apache Software Foundation\\Jakarta Isapi Redirector\\1.0]");
+        regfile.println("\"extension_uri\"=\"/jakarta/isapi_redirect.dll\"");
+        regfile.println("\"log_file\"=\"" + dubleSlash(jkLog.toString()) +"\"");
+        regfile.println("\"log_level\"=\"" + jkDebug + "\"");
+        regfile.println("\"worker_file\"=\"" + dubleSlash(workersConfig.toString()) +"\"");
+        regfile.println("\"worker_mount_file\"=\"" + dubleSlash(uriConfig.toString()) +"\"");
+    }
+
+    /** Writes the header information to the uriworkermap file
+     */
+    private void generateUriWorkerHeader(PrintWriter uri_worker)
+    {
+        uri_worker.println("###################################################################");		    
+        uri_worker.println("# Auto generated configuration. Dated: " +  new Date());
+        uri_worker.println("###################################################################");		    
+        uri_worker.println();
+
+        uri_worker.println("#");        
+        uri_worker.println("# Default worker to be used through our mappings");
+        uri_worker.println("#");        
+        uri_worker.println("default.worker=" + jkWorker);        
+        uri_worker.println();
+    }
+
+    /** Forward all requests for a context to tomcat.
+	The default.
+     */
+    protected void generateStupidMappings(Context context, PrintWriter uri_worker )
+    {
+        String ctxPath  = context.getPath();
+	String nPath=("".equals(ctxPath)) ? "/" : ctxPath;
+
+        if( noRoot &&  "".equals(ctxPath) ) {
+            log("Ignoring root context in forward-all mode  ");
+            return;
+        } 
+
+        // map all requests for this context to Tomcat
+        uri_worker.println(nPath +"=$(default.worker)");
+        if( "".equals(ctxPath) ) {
+            uri_worker.println(nPath +"*=$(default.worker)");
+            uri_worker.println(
+                    "# Note: To correctly serve the Tomcat's root context, IIS's Home Directory must");
+            uri_worker.println(
+                    "# must be set to: \"" + getAbsoluteDocBase(context) + "\"");
+        }
+        else
+            uri_worker.println(nPath +"/*=$(default.worker)");
+    }
+
+    protected void generateContextMappings(Context context, PrintWriter uri_worker )
+    {
+        String ctxPath  = context.getPath();
+	String nPath=("".equals(ctxPath)) ? "/" : ctxPath;
+
+        if( noRoot &&  "".equals(ctxPath) ) {
+            log("Ignoring root context in forward-all mode  ");
+            return;
+        } 
+
+        // Static files will be served by IIS
+        uri_worker.println();
+        uri_worker.println("#########################################################");		    
+        uri_worker.println("# Auto configuration for the " + nPath + " context.");
+        uri_worker.println("#########################################################");		    
+        uri_worker.println();
+
+        // Static mappings are not set in uriworkermap, but must be set with IIS admin.
+
+	// InvokerInterceptor - it doesn't have a container,
+	// but it's implemented using a special module.
+
+	// XXX we need to better collect all mappings
+
+	if(context.getLoginConfig() != null) {
+	    String loginPage = context.getLoginConfig().getLoginPage();
+	    if(loginPage != null) {
+		int lpos = loginPage.lastIndexOf("/");
+		String jscurl = loginPage.substring(0,lpos+1) + "j_security_check";
+		addMapping( ctxPath, jscurl, uri_worker);
+	    }
+	}
+		String [] servletMaps=context.findServletMappings();
+	for( int ii=0; ii < servletMaps.length ; ii++) {
+	    addMapping( ctxPath , servletMaps[ii] , uri_worker );
+	}
+    }
+
+    /** Add an IIS extension mapping.
+     */
+    protected boolean addMapping( String ctxPath, String ext,
+					 PrintWriter uri_worker )
+    {
+        if( debug > 0 )
+            log( "Adding extension map for " + ctxPath + "/*." + ext );
+	if(! ext.startsWith("/") )
+	    ext = "/" + ext;
+	if(ext.length() > 1)
+	    uri_worker.println(ctxPath + "/*." + ext + "=$(default.worker)");
+        return true;
+    }
+
+    /** Add a fulling specified IIS mapping.
+     */
+    protected boolean addMapping( String fullPath, PrintWriter uri_worker ) {
+        if( debug > 0 )
+            log( "Adding map for " + fullPath );
+        uri_worker.println(fullPath + "=$(default.worker)" );
+        return true;
+    }
+
+    // -------------------- Utils --------------------
+
+    private String dubleSlash(String in) 
+    {
+        StringBuffer sb = new StringBuffer();
+        
+        for(int i = 0 ; i < in.length() ; i++) {
+            char ch = in.charAt(i);
+            if('\\' == ch) {
+                sb.append("\\\\");
+            } else {
+                sb.append(ch);
+            }
+        }
+        
+        return sb.toString();
+    }
+
+}
diff --git a/connectors/jk/java/org/apache/ajp/tomcat4/config/NSConfig.java b/connectors/jk/java/org/apache/ajp/tomcat4/config/NSConfig.java
new file mode 100644
index 0000000..a776f4f
--- /dev/null
+++ b/connectors/jk/java/org/apache/ajp/tomcat4/config/NSConfig.java
@@ -0,0 +1,326 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp.tomcat4.config;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Date;
+
+import org.apache.catalina.Context;
+
+
+/**
+    Generates automatic Netscape nsapi_redirect configurations based on
+    the Tomcat server.xml settings and the war contexts
+    initialized during startup.
+    <p>
+    This config interceptor is enabled by inserting an NSConfig
+    element in the <b>&lt;ContextManager&gt;</b> tag body inside
+    the server.xml file like so:
+    <pre>
+    * < ContextManager ... >
+    *   ...
+    *   <<b>NSConfig</b> <i>options</i> />
+    *   ...
+    * < /ContextManager >
+    </pre>
+    where <i>options</i> can include any of the following attributes:
+    <ul>
+     <li><b>configHome</b> - default parent directory for the following paths.
+                            If not set, this defaults to TOMCAT_HOME. Ignored
+                            whenever any of the following paths is absolute.
+                             </li>
+     <li><b>objConfig</b> - path to use for writing Netscape obj.conf
+                            file. If not set, defaults to
+                            "conf/auto/obj.conf".</li>
+     <li><b>objectName</b> - Name of the Object to execute the requests.
+                             Defaults to "servlet".</li>
+     <li><b>workersConfig</b> - path to workers.properties file used by 
+                                nsapi_redirect. If not set, defaults to
+                                "conf/jk/workers.properties".</li>
+     <li><b>nsapiJk</b> - path to Netscape mod_jk plugin file.  If not set,
+                        defaults to "bin/nsapi_redirect.dll" on windows,
+                        "bin/nsapi_rd.nlm" on netware, and
+                        "bin/nsapi_redirector.so" everywhere else.</li>
+     <li><b>jkLog</b> - path to log file to be used by nsapi_redirect.</li>
+     <li><b>jkDebug</b> - Loglevel setting.  May be debug, info, error, or emerg.
+                          If not set, defaults to emerg.</li>
+     <li><b>jkWorker</b> The desired worker.  Must be set to one of the workers
+                         defined in the workers.properties file. "ajp12", "ajp13"
+                         or "inprocess" are the workers found in the default
+                         workers.properties file. If not specified, defaults
+                         to "ajp13" if an Ajp13Interceptor is in use, otherwise
+                         it defaults to "ajp12".</li>
+     <li><b>forwardAll</b> - If true, forward all requests to Tomcat. This helps
+                             insure that all the behavior configured in the web.xml
+                             file functions correctly.  If false, let Netscape serve
+                             static resources assuming it has been configured
+                             to do so. The default is true.
+                             Warning: When false, some configuration in
+                             the web.xml may not be duplicated in Netscape.
+                             Review the uriworkermap file to see what
+                             configuration is actually being set in Netscape.</li>
+     <li><b>noRoot</b> - If true, the root context is not mapped to
+                         Tomcat.  If false and forwardAll is true, all requests
+                         to the root context are mapped to Tomcat. If false and
+                         forwardAll is false, only JSP and servlets requests to
+                         the root context are mapped to Tomcat. When false,
+                         to correctly serve Tomcat's root context you must also
+                         modify the Home Directory setting in Netscape
+                         to point to Tomcat's root context directory.
+                         Otherwise some content, such as the root index.html,
+                         will be served by Netscape before nsapi_redirect gets a chance
+                         to claim the request and pass it to Tomcat.
+                         The default is true.</li>
+    </ul>
+  <p>
+    @author Costin Manolache
+    @author Larry Isaacs
+    @author Gal Shachor
+    @author Bill Barker
+ */
+public class NSConfig  extends BaseJkConfig { 
+
+    public static final String WORKERS_CONFIG = "/conf/jk/workers.properties";
+    public static final String NS_CONFIG = "/conf/auto/obj.conf";
+    public static final String NSAPI_LOG_LOCATION = "/logs/nsapi_redirect.log";
+    /** default location of nsapi plug-in. */
+    public static final String NSAPI_REDIRECTOR;
+    
+    //set up some defaults based on OS type
+    static{
+        String os = System.getProperty("os.name").toLowerCase();
+        if(os.indexOf("windows")>=0){
+           NSAPI_REDIRECTOR = "bin/nsapi_redirect.dll";
+        }else if(os.indexOf("netware")>=0){
+           NSAPI_REDIRECTOR = "bin/nsapi_rd.nlm";
+        }else{
+           NSAPI_REDIRECTOR = "bin/nsapi_redirector.so";
+        }
+    }
+
+    private File objConfig = null;
+    private File nsapiJk = null;
+    private String objectName = "servlet";
+
+    public NSConfig() 
+    {
+    }
+
+    //-------------------- Properties --------------------
+    
+    /**
+        set the path to the output file for the auto-generated
+        isapi_redirect registry file.  If this path is relative
+        then getRegConfig() will resolve it absolutely against
+        the getConfigHome() path.
+        <p>
+        @param path String path to a file
+    */
+    public void setObjConfig(String path) {
+	objConfig= (path==null)?null:new File(path);
+    }
+
+    /**
+        set the path to the nsapi plugin module
+        @param path String path to a file
+    */
+    public void setNsapiJk(String path) {
+        nsapiJk=( path==null?null:new File(path));
+    }
+
+    /**
+        Set the name for the Object that implements the
+        jk_service call.
+        @param name Name of the obj.conf Object
+    */
+    public void setObjectName(String name) {
+        objectName = name;
+    }
+
+    // -------------------- Initialize/guess defaults --------------------
+
+    /** Initialize defaults for properties that are not set
+	explicitely
+    */
+    protected void initProperties() {
+        super.initProperties();
+
+	objConfig=getConfigFile( objConfig, configHome, NS_CONFIG);
+	workersConfig=getConfigFile( workersConfig, configHome, WORKERS_CONFIG);
+
+	if( nsapiJk == null )
+	    nsapiJk=new File(NSAPI_REDIRECTOR);
+	else
+	    nsapiJk =getConfigFile( nsapiJk, configHome, NSAPI_REDIRECTOR );
+	jkLog=getConfigFile( jkLog, configHome, NSAPI_LOG_LOCATION);
+    }
+
+    // -------------------- Generate config --------------------
+    protected PrintWriter getWriter() throws IOException {
+	String abObjConfig = objConfig.getAbsolutePath();
+	return new PrintWriter(new FileWriter(abObjConfig,append));
+    }
+    protected boolean generateJkHead(PrintWriter mod_jk) {
+	log("Generating netscape web server config = "+objConfig );
+	
+	generateNsapiHead( mod_jk );
+	
+	mod_jk.println("<Object name=default>");
+	return true;
+    }
+
+    private void generateNsapiHead(PrintWriter objfile)
+    {
+        objfile.println("###################################################################");		    
+        objfile.println("# Auto generated configuration. Dated: " +  new Date());
+        objfile.println("###################################################################");		    
+        objfile.println();
+
+        objfile.println("#");        
+        objfile.println("# You will need to merge the content of this file with your ");
+        objfile.println("# regular obj.conf and then restart (=stop + start) your Netscape server. ");
+        objfile.println("#");        
+        objfile.println();
+            
+        objfile.println("#");                    
+        objfile.println("# Loading the redirector into your server");
+        objfile.println("#");        
+        objfile.println();            
+        objfile.println("Init fn=\"load-modules\" funcs=\"jk_init,jk_service\" shlib=\"<put full path to the redirector here>\"");
+        objfile.println("Init fn=\"jk_init\" worker_file=\"" + 
+                        workersConfig.toString().replace('\\', '/') +  
+                        "\" log_level=\"" + jkDebug + "\" log_file=\"" + 
+                        jkLog.toString().replace('\\', '/') + 
+                        "\"");
+        objfile.println();
+    }
+
+    protected void generateJkTail(PrintWriter objfile)
+    {
+        objfile.println();
+        objfile.println("#######################################################");		    
+        objfile.println("# Protecting the WEB-INF and META-INF directories.");
+        objfile.println("#######################################################");		    
+        objfile.println("PathCheck fn=\"deny-existence\" path=\"*/WEB-INF/*\""); 
+        objfile.println("PathCheck fn=\"deny-existence\" path=\"*/META-INF/*\""); 
+        objfile.println();
+
+        objfile.println("</Object>");            
+        objfile.println();
+
+        objfile.println("#######################################################");		    
+        objfile.println("# New object to execute your servlet requests.");
+        objfile.println("#######################################################");		    
+        objfile.println("<Object name=" + objectName + ">");
+        objfile.println("ObjectType fn=force-type type=text/html");
+        objfile.println("Service fn=\"jk_service\" worker=\""+ jkWorker + "\" path=\"/*\"");
+        objfile.println("</Object>");
+        objfile.println();
+    }
+
+    // -------------------- Forward all mode --------------------
+    
+    /** Forward all requests for a context to tomcat.
+	The default.
+     */
+    protected void generateStupidMappings(Context context, PrintWriter objfile )
+    {
+        String ctxPath  = context.getPath();
+	String nPath=("".equals(ctxPath)) ? "/" : ctxPath;
+
+        if( noRoot &&  "".equals(ctxPath) ) {
+            log("Ignoring root context in forward-all mode  ");
+            return;
+        } 
+	objfile.println("<Object name=" + context.getName() + ">");
+
+        objfile.println("NameTrans fn=\"assign-name\" from=\"" + ctxPath + "\" name=\"" + objectName + "\""); 
+        objfile.println("NameTrans fn=\"assign-name\" from=\"" + ctxPath + "/*\" name=\"" + objectName + "\""); 
+	objfile.println("</Object>");
+    }
+
+
+    // -------------------- Netscape serves static mode --------------------
+    // This is not going to work for all apps. We fall back to stupid mode.
+    
+    protected void generateContextMappings(Context context, PrintWriter objfile )
+    {
+        String ctxPath  = context.getPath();
+	String nPath=("".equals(ctxPath)) ? "/" : ctxPath;
+
+        if( noRoot &&  "".equals(ctxPath) ) {
+            log("Ignoring root context in non-forward-all mode  ");
+            return;
+        } 
+	objfile.println("<Object name=" + context.getName() + ">");
+        // Static files will be served by Netscape
+        objfile.println("#########################################################");		    
+        objfile.println("# Auto configuration for the " + nPath + " context starts.");
+        objfile.println("#########################################################");		    
+        objfile.println();
+
+        // XXX Need to determine what if/how static mappings are done
+
+	// InvokerInterceptor - it doesn't have a container,
+	// but it's implemented using a special module.
+	
+	// XXX we need to better collect all mappings
+	if(context.getLoginConfig() != null) {
+	    String loginPage = context.getLoginConfig().getLoginPage();
+	    if(loginPage != null) {
+		int lpos = loginPage.lastIndexOf("/");
+		String jscurl = loginPage.substring(0,lpos+1) + "j_security_check";
+		addMapping( ctxPath, jscurl, objfile);
+	    }
+	}
+	
+	String [] servletMaps=context.findServletMappings();
+	for(int ii=0; ii < servletMaps.length; ii++) {
+	    addMapping( ctxPath , servletMaps[ii] , objfile );
+	}
+	objfile.println("</Object>");
+    }
+
+    /** Add a Netscape extension mapping.
+     */
+    protected boolean addMapping( String ctxPath, String ext,
+					 PrintWriter objfile )
+    {
+        if( debug > 0 )
+            log( "Adding extension map for " + ctxPath + "/*." + ext );
+	if(! ext.startsWith("/") )
+	    ext = "/" + ext;
+	if(ext.length() > 1)
+	    objfile.println("NameTrans fn=\"assign-name\" from=\"" +
+                    ctxPath  + ext + "\" name=\"" + objectName + "\""); 
+	return true;
+    }
+
+    /** Add a fulling specified Netscape mapping.
+     */
+    protected boolean addMapping( String fullPath, PrintWriter objfile ) {
+        if( debug > 0 )
+            log( "Adding map for " + fullPath );
+        objfile.println("NameTrans fn=\"assign-name\" from=\"" +
+                        fullPath + "\" name=\"" + objectName + "\""); 
+	return true;
+    }
+
+}
diff --git a/connectors/jk/java/org/apache/jk/apr/AprImpl.java b/connectors/jk/java/org/apache/jk/apr/AprImpl.java
new file mode 100644
index 0000000..0f5c0ab
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/apr/AprImpl.java
@@ -0,0 +1,316 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+ 
+package org.apache.jk.apr;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Hashtable;
+import org.apache.jk.core.JkHandler;
+import org.apache.jk.core.MsgContext;
+import org.apache.jk.core.JkChannel;
+
+/** Implements the interface with the APR library. This is for internal-use
+ *  only. The goal is to use 'natural' mappings for user code - for example
+ *  java.net.Socket for unix-domain sockets, etc. 
+ * 
+ */
+public class AprImpl extends JkHandler { // This will be o.a.t.util.handler.TcHandler - lifecycle and config
+    static AprImpl aprSingleton=null;
+
+    String baseDir;
+    String aprHome;
+    String soExt="so";
+
+    static boolean ok=true;
+    boolean initialized=false;
+    // Handlers for native callbacks
+    Hashtable jkHandlers=new Hashtable();
+
+    // Name of the so used in inprocess mode 
+    String jniModeSo="inprocess";
+    // name of the so used by java. If not set we'll loadLibrary("jkjni" ),
+    // if set we load( nativeSo )
+    String nativeSo;
+    
+    public AprImpl() {
+        aprSingleton=this;
+    }
+    
+    // -------------------- Properties --------------------
+    
+    /** Native libraries are located based on base dir.
+     *  XXX Add platform, version, etc
+     */
+    public void setBaseDir(String s) {
+        baseDir=s;
+    }
+    
+    public void setSoExt(String s ) {
+        soExt=s;
+    }
+    
+    // XXX maybe install the jni lib in apr-home ?
+    public void setAprHome( String s ) {
+        aprHome=s;
+    }
+
+    /** Add a Handler for jni callbacks.
+     */
+    public void addJkHandler(String type, JkHandler cb) {
+        jkHandlers.put( type, cb );
+    }
+    
+    /** Name of the so used in inprocess mode
+     */
+    public void setJniModeSo(String jniModeSo ) {
+        this.jniModeSo=jniModeSo;
+    }
+
+    /** name of the so used by java. If not set we'll loadLibrary("jkjni" ),
+        if set we load( nativeSo )
+    */
+    public void setNativeSo( String nativeSo ) {
+        this.nativeSo=nativeSo;
+    }
+
+    /** Sets the System.out stream */
+    
+    public static void setOut( String filename ) {
+        try{ 
+            if( filename !=null ){
+                System.setOut( new PrintStream(new FileOutputStream(filename )));
+            }
+        }catch (Throwable th){
+        }
+    }
+    /** Sets the System.err stream */
+    
+    public static void setErr( String filename ) {
+        try{ 
+            if( filename !=null ){
+                System.setErr( new PrintStream(new FileOutputStream(filename )));
+            }                                                 
+        }catch (Throwable th){
+        }
+    }
+
+    // -------------------- Apr generic utils --------------------
+    /** Initialize APR
+     */
+    public native int initialize();
+
+    public native int terminate();
+
+    /* -------------------- Access to the jk_env_t -------------------- */
+
+    /* The jk_env_t provide temporary storage ( pool ), logging, common services
+     */
+    
+    /* Return a jk_env_t, used to keep the execution context ( temp pool, etc )
+     */
+    public native long getJkEnv();
+
+    /** Clean the temp pool, put back the env in the pool
+     */
+    public native void releaseJkEnv(long xEnv);
+
+    /* -------------------- Interface to the jk_bean object -------------------- */
+    /* Each jk component is 'wrapped' as a bean, with a specified lifecycle
+     *
+     */
+    
+    /** Get a native component
+     *  @return 0 if the component is not found.
+     */
+    public native long getJkHandler(long xEnv, String compName );
+
+    public native long createJkHandler(long xEnv, String compName );
+
+    public native int jkSetAttribute( long xEnv, long componentP, String name, String val );
+
+    public native String jkGetAttribute( long xEnv, long componentP, String name );
+    
+    public native int jkInit( long xEnv, long componentP );
+
+    public native int jkDestroy( long xEnv, long componentP );
+    
+    /** Send the packet to the C side. On return it contains the response
+     *  or indication there is no response. Asymetrical because we can't
+     *  do things like continuations.
+     */
+    public static native int jkInvoke(long xEnv, long componentP, long endpointP,
+                                      int code, byte data[], int off, int len, int raw);
+
+    /** Recycle an endpoint after use.
+     */
+    public native void jkRecycle(long xEnv, long endpointP);
+
+    // -------------------- Called from C --------------------
+    // XXX Check security, add guard or other protection
+    // It's better to do it the other way - on init 'push' AprImpl into
+    // the native library, and have native code call instance methods.
+    
+    public static Object createJavaContext(String type, long cContext) {
+        // XXX will be an instance method, fields accessible directly
+        AprImpl apr=aprSingleton;
+        JkChannel jkH=(JkChannel)apr.jkHandlers.get( type );
+        if( jkH==null ) return null;
+
+        MsgContext ep=jkH.createMsgContext();
+
+        ep.setSource( jkH );
+        
+        ep.setJniContext( cContext );
+        return ep;
+    }
+
+    /** Return a buffer associated with the ctx.
+     */
+    public static byte[] getBuffer( Object ctx, int id ) {
+        return ((MsgContext)ctx).getBuffer(  id );
+    }
+
+    public static int jniInvoke( long jContext, Object ctx ) {
+        try {
+            MsgContext ep=(MsgContext)ctx;
+            ep.setJniEnv(  jContext );
+            ep.setType( 0 );
+            return ((MsgContext)ctx).execute();
+        } catch( Throwable ex ) {
+            ex.printStackTrace();
+            return -1;
+        }
+    }
+
+    // -------------------- Initialization -------------------- 
+
+    public void init() throws IOException {
+        try {
+            initialized=true;
+            loadNative();
+
+            initialize();
+            jkSetAttribute(0, 0, "channel:jni", "starting");
+            
+            log.info("JK2: Initialized apr" );
+            
+        } catch( Throwable t ) {
+            throw new IOException( t.toString() );
+        }
+        ok=true;
+    }
+
+    public boolean isLoaded() {
+        if( ! initialized ) {
+            try {
+                init();
+            } catch( Throwable t ) {
+                log.info("Apr not loaded: " + t);
+            }
+        }
+        return ok;
+    }
+
+    static boolean jniMode=false;
+
+    
+    public static void jniMode() {
+        jniMode=true;
+    }
+
+    /** This method of loading the libs doesn't require setting
+     *   LD_LIBRARY_PATH. Assuming a 'right' binary distribution,
+     *   or a correct build all files will be in their right place.
+     *
+     *  The burden is on our code to deal with platform specific
+     *  extensions and to keep the paths consistent - not easy, but
+     *  worth it if it avoids one extra step for the user.
+     *
+     *  Of course, this can change to System.load() and putting the
+     *  libs in LD_LIBRARY_PATH.
+     */
+    public void loadNative() throws Throwable {
+        if( aprHome==null )
+            aprHome=baseDir;
+
+        // XXX Update for windows
+        if( jniMode ) {
+            /* In JNI mode we use mod_jk for the native functions.
+               This seems the cleanest solution that works with multiple
+               VMs.
+            */
+            if (jniModeSo.equals("inprocess")) {
+                ok=true;
+                return;                                
+            }
+            try {
+                log.info("Loading " + jniModeSo);
+                if( jniModeSo!= null ) System.load( jniModeSo );
+            } catch( Throwable ex ) {
+                // ignore
+                //ex.printStackTrace();
+                return;
+            }
+            ok=true;
+            return;
+        }
+        
+            /*
+              jkjni _must_ be linked with apr and crypt -
+              this seem the only ( decent ) way to support JDK1.4 and
+              JDK1.3 at the same time
+              try {
+                  System.loadLibrary( "crypt" );
+              } catch( Throwable ex ) {
+                  // ignore
+                  ex.printStackTrace();
+              }
+              try {
+                  System.loadLibrary( "apr" );
+              } catch( Throwable ex ) {
+                  System.out.println("can't load apr, that's fine");
+                  ex.printStackTrace();
+              }
+            */
+        try {
+            if( nativeSo == null ) {
+                // This will load libjkjni.so or jkjni.dll in LD_LIBRARY_PATH
+                log.debug("Loading jkjni from " + System.getProperty("java.library.path"));
+                System.loadLibrary( "jkjni" );
+            } else {
+                System.load( nativeSo );
+            }
+        } catch( Throwable ex ) {
+            ok=false;
+            //ex.printStackTrace();
+            throw ex;
+        }
+    } 
+
+    public void loadNative(String libPath) {
+        try {
+            System.load( libPath );
+        } catch( Throwable ex ) {
+            ok=false;
+            if( log.isDebugEnabled() ) 
+                log.debug( "Error loading native library ", ex);
+        }
+    }
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( AprImpl.class );
+}
diff --git a/connectors/jk/java/org/apache/jk/apr/TomcatStarter.java b/connectors/jk/java/org/apache/jk/apr/TomcatStarter.java
new file mode 100644
index 0000000..c9e2a6b
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/apr/TomcatStarter.java
@@ -0,0 +1,93 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+ 
+package org.apache.jk.apr;
+
+import java.lang.reflect.Method;
+
+// Hack for Catalina 4.1 who hungs the calling thread.
+// Also avoids delays in apache initialization ( tomcat can take a while )
+
+/** 
+ * Start some tomcat.
+ * 
+ */
+public class TomcatStarter implements Runnable {
+    Class c;
+    String args[];
+    AprImpl apr = new AprImpl();
+    
+    public static String mainClasses[]={ "org.apache.tomcat.startup.Main",
+                                         "org.apache.catalina.startup.BootstrapService",
+                                         "org.apache.catalina.startup.Bootstrap"};
+
+    // If someone has time - we can also guess the classpath and do other
+    // fancy guessings.
+    
+    public static void main( String args[] ) {
+        System.err.println("TomcatStarter: main()");
+        int nClasses = 0;
+        
+        try {
+            AprImpl.jniMode();            
+            // Find the class
+            Class c=null;
+            for( int i=0; i<mainClasses.length; i++ ) {
+                try {
+                    System.err.println("Try  " + mainClasses[i]);
+                    c=Class.forName( mainClasses[i] );
+                } catch( ClassNotFoundException ex  ) {
+                    continue;
+                }
+                if( c!= null ) {
+                    ++nClasses;
+                    Thread startThread=new Thread( new TomcatStarter(c, args));
+                    c=null;
+                    startThread.start();
+                    break;
+                }
+            }
+            if (nClasses==0)
+                System.err.println("No class found  ");
+
+        } catch (Throwable t ) {
+            t.printStackTrace(System.err);
+        }
+    }
+
+    public TomcatStarter( Class c, String args[] ) {
+        this.c=c;
+        this.args=args;
+    }
+    
+    public void run() {
+        System.err.println("Starting " + c.getName());
+        try {
+            Class argClass=args.getClass();
+            Method m=c.getMethod( "main", new Class[] {argClass} );
+            m.invoke( c, new Object[] { args } );
+            System.out.println("TomcatStarter: Done");
+            if (apr.isLoaded())
+                apr.jkSetAttribute(0, 0, "channel:jni", "done");
+            if (args[0].equals("stop")) {
+                  Thread.sleep(5000);
+                  Runtime.getRuntime().exit(0);
+            }
+        } catch( Throwable t ) {
+            t.printStackTrace(System.err);
+        }
+    }
+}
diff --git a/connectors/jk/java/org/apache/jk/common/ChannelJni.java b/connectors/jk/java/org/apache/jk/common/ChannelJni.java
new file mode 100644
index 0000000..691ef22
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/ChannelJni.java
@@ -0,0 +1,190 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.common;
+
+import java.io.IOException;
+
+import org.apache.jk.core.JkHandler;
+import org.apache.jk.core.Msg;
+import org.apache.jk.core.MsgContext;
+import org.apache.jk.core.JkChannel;
+
+import org.apache.coyote.Request;
+/** Pass messages using jni 
+ *
+ * @author Costin Manolache
+ */
+public class ChannelJni extends JniHandler implements JkChannel {
+    int receivedNote=1;
+
+    public ChannelJni() {
+        // we use static for now, it's easier on the C side.
+        // Easy to change after we get everything working
+    }
+
+    public void init() throws IOException {
+        super.initNative("channel.jni:jni");
+
+        if( apr==null ) return;
+        
+        // We'll be called from C. This deals with that.
+        apr.addJkHandler( "channelJni", this );        
+        log.info("JK2: listening on channel.jni:jni" );
+        
+        if( next==null ) {
+            if( nextName!=null ) 
+                setNext( wEnv.getHandler( nextName ) );
+            if( next==null )
+                next=wEnv.getHandler( "dispatch" );
+            if( next==null )
+                next=wEnv.getHandler( "request" );
+            if( log.isDebugEnabled() )
+                log.debug("Setting default next " + next.getClass().getName());
+        }
+    }
+
+    /** Receives does nothing - send will put the response
+     *  in the same buffer
+     */
+    public int receive( Msg msg, MsgContext ep )
+        throws IOException
+    {
+        Msg sentResponse=(Msg)ep.getNote( receivedNote );
+        ep.setNote( receivedNote, null );
+
+        if( sentResponse == null ) {
+            if( log.isDebugEnabled() )
+                log.debug("No send() prior to receive(), no data buffer");
+            // No sent() was done prior to receive.
+            msg.reset();
+            return 0;
+        }
+        
+        sentResponse.processHeader();
+
+        if( log.isTraceEnabled() )
+            sentResponse.dump("received response ");
+
+        if( msg != sentResponse ) {
+            log.error( "Error, in JNI mode the msg used for receive() must be identical with the one used for send()");
+        }
+        
+        return 0;
+    }
+
+    /** Send the packet. XXX This will modify msg !!!
+     *  We could use 2 packets, or sendAndReceive().
+     *    
+     */
+    public int send( Msg msg, MsgContext ep )
+        throws IOException
+    {
+        ep.setNote( receivedNote, null );
+        if( log.isDebugEnabled() ) log.debug("ChannelJni.send: "  +  msg );
+
+        int rc=super.nativeDispatch( msg, ep, JK_HANDLE_JNI_DISPATCH, 0);
+
+        // nativeDispatch will put the response in the same buffer.
+        // Next receive() will just get it from there. Very tricky to do
+        // things in one thread instead of 2.
+        ep.setNote( receivedNote, msg );
+        
+        return rc;
+    }
+
+    public int flush(Msg msg, MsgContext ep) throws IOException {
+        ep.setNote( receivedNote, null );
+        return OK;
+    }
+
+    public boolean isSameAddress(MsgContext ep) {
+        return true;
+    }
+
+    public void registerRequest(Request req, MsgContext ep, int count) {
+        // Not supported.
+    }
+
+    public String getChannelName() {
+        return getName();
+    }
+    /** Receive a packet from the C side. This is called from the C
+     *  code using invocation, but only for the first packet - to avoid
+     *  recursivity and thread problems.
+     *
+     *  This may look strange, but seems the best solution for the
+     *  problem ( the problem is that we don't have 'continuation' ).
+     *
+     *  sendPacket will move the thread execution on the C side, and
+     *  return when another packet is available. For packets that
+     *  are one way it'll return after it is processed too ( having
+     *  2 threads is far more expensive ).
+     *
+     *  Again, the goal is to be efficient and behave like all other
+     *  Channels ( so the rest of the code can be shared ). Playing with
+     *  java objects on C is extremely difficult to optimize and do
+     *  right ( IMHO ), so we'll try to keep it simple - byte[] passing,
+     *  the conversion done in java ( after we know the encoding and
+     *  if anyone asks for it - same lazy behavior as in 3.3 ).
+     */
+    public  int invoke(Msg msg, MsgContext ep )  throws IOException {
+        if( apr==null ) return -1;
+        
+        long xEnv=ep.getJniEnv();
+        long cEndpointP=ep.getJniContext();
+
+        int type=ep.getType();
+        if( log.isDebugEnabled() ) log.debug("ChannelJni.invoke: "  + ep + " " + type);
+
+        switch( type ) {
+        case JkHandler.HANDLE_RECEIVE_PACKET:
+            return receive( msg, ep );
+        case JkHandler.HANDLE_SEND_PACKET:
+            return send( msg, ep );
+        case JkHandler.HANDLE_FLUSH:
+            return flush(msg, ep);
+        }
+
+        // Reset receivedNote. It'll be visible only after a SEND and before a receive.
+        ep.setNote( receivedNote, null );
+
+        // Default is FORWARD - called from C 
+        try {
+            // first, we need to get an endpoint. It should be
+            // per/thread - and probably stored by the C side.
+            if( log.isDebugEnabled() ) log.debug("Received request " + xEnv);
+            
+            // The endpoint will store the message pt.
+            msg.processHeader();
+
+            if( log.isTraceEnabled() ) msg.dump("Incoming msg ");
+
+            int status= next.invoke(  msg, ep );
+            
+            if( log.isDebugEnabled() ) log.debug("after processCallbacks " + status);
+            
+            return status;
+        } catch( Exception ex ) {
+            ex.printStackTrace();
+        }
+        return 0;
+    }    
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( ChannelJni.class );
+
+}
diff --git a/connectors/jk/java/org/apache/jk/common/ChannelShm.java b/connectors/jk/java/org/apache/jk/common/ChannelShm.java
new file mode 100644
index 0000000..ceaaf9a
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/ChannelShm.java
@@ -0,0 +1,32 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.common;
+
+import org.apache.jk.core.JkHandler;
+
+
+
+/** Channel using shm.
+ *
+ * @author Costin Manolache
+ */
+public class ChannelShm extends JkHandler {
+
+    // Not implemented yet.
+    
+    
+}
diff --git a/connectors/jk/java/org/apache/jk/common/ChannelSocket.java b/connectors/jk/java/org/apache/jk/common/ChannelSocket.java
new file mode 100644
index 0000000..55db456
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/ChannelSocket.java
@@ -0,0 +1,868 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.common;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanNotificationInfo;
+import javax.management.Notification;
+import javax.management.NotificationBroadcaster;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+
+import org.apache.commons.modeler.Registry;
+import org.apache.jk.core.JkHandler;
+import org.apache.jk.core.Msg;
+import org.apache.jk.core.MsgContext;
+import org.apache.jk.core.JkChannel;
+import org.apache.jk.core.WorkerEnv;
+import org.apache.coyote.Request;
+import org.apache.coyote.RequestGroupInfo;
+import org.apache.coyote.RequestInfo;
+import org.apache.tomcat.util.threads.ThreadPool;
+import org.apache.tomcat.util.threads.ThreadPoolRunnable;
+
+
+/* XXX Make the 'message type' pluggable
+ */
+
+/* A lot of the 'original' behavior is hardcoded - this uses Ajp13 wire protocol,
+   TCP, Ajp14 API etc.
+   As we add other protocols/transports/APIs this will change, the current goal
+   is to get the same level of functionality as in the original jk connector.
+*/
+
+/**
+ *  Jk2 can use multiple protocols/transports.
+ *  Various container adapters should load this object ( as a bean ),
+ *  set configurations and use it. Note that the connector will handle
+ *  all incoming protocols - it's not specific to ajp1x. The protocol
+ *  is abstracted by MsgContext/Message/Channel.
+ */
+
+
+/** Accept ( and send ) TCP messages.
+ *
+ * @author Costin Manolache
+ * @jmx:mbean name="jk2:service=ChannelSocket"
+ *            description="Accept socket connections"
+ * @jmx:notification name="org.apache.coyote.INVOKE
+ * @jmx:notification-handler name="org.apache.jk.JK_SEND_PACKET
+ * @jmx:notification-handler name="org.apache.jk.JK_RECEIVE_PACKET
+ * @jmx:notification-handler name="org.apache.jk.JK_FLUSH
+ */
+public class ChannelSocket extends JkHandler
+    implements NotificationBroadcaster, JkChannel {
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( ChannelSocket.class );
+
+    int startPort=8009;
+    int maxPort=8019; // 0 for backward compat.
+    int port=startPort;
+    InetAddress inet;
+    int serverTimeout;
+    boolean tcpNoDelay=true; // nodelay to true by default
+    int linger=100;
+    int socketTimeout;
+
+    long requestCount=0;
+    
+    /* Turning this to true will reduce the latency with about 20%.
+       But it requires changes in tomcat to make sure client-requested
+       flush() is honored ( on my test, I got 367->433 RPS and
+       52->35ms average time with a simple servlet )
+    */
+    static final boolean BUFFER_WRITE=false;
+    
+    ThreadPool tp=ThreadPool.createThreadPool(true);
+
+    /* ==================== Tcp socket options ==================== */
+
+    /**
+     * @jmx:managed-constructor description="default constructor"
+     */
+    public ChannelSocket() {
+        // This should be integrated with the  domain setup
+    }
+    
+    public ThreadPool getThreadPool() {
+        return tp;
+    }
+
+    public long getRequestCount() {
+        return requestCount;
+    }
+    
+    /** Set the port for the ajp13 channel.
+     *  To support seemless load balancing and jni, we treat this
+     *  as the 'base' port - we'll try up until we find one that is not
+     *  used. We'll also provide the 'difference' to the main coyote
+     *  handler - that will be our 'sessionID' and the position in
+     *  the scoreboard and the suffix for the unix domain socket.
+     *
+     * @jmx:managed-attribute description="Port to listen" access="READ_WRITE"
+     */
+    public void setPort( int port ) {
+        this.startPort=port;
+        this.port=port;
+        this.maxPort=port+10;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setAddress(InetAddress inet) {
+        this.inet=inet;
+    }
+
+    /**
+     * @jmx:managed-attribute description="Bind on a specified address" access="READ_WRITE"
+     */
+    public void setAddress(String inet) {
+        try {
+            this.inet= InetAddress.getByName( inet );
+        } catch( Exception ex ) {
+            log.error("Error parsing "+inet,ex);
+        }
+    }
+
+    public String getAddress() {
+        if( inet!=null)
+            return inet.toString();
+        return "/0.0.0.0";
+    }
+
+    /**
+     * Sets the timeout in ms of the server sockets created by this
+     * server. This method allows the developer to make servers
+     * more or less responsive to having their server sockets
+     * shut down.
+     *
+     * <p>By default this value is 1000ms.
+     */
+    public void setServerTimeout(int timeout) {
+	this.serverTimeout = timeout;
+    }
+    public int getServerTimeout() {
+        return serverTimeout;
+    }
+
+    public void setTcpNoDelay( boolean b ) {
+	tcpNoDelay=b;
+    }
+
+    public boolean getTcpNoDelay() {
+        return tcpNoDelay;
+    }
+    
+    public void setSoLinger( int i ) {
+	linger=i;
+    }
+
+    public int getSoLinger() {
+        return linger;
+    }
+    
+    public void setSoTimeout( int i ) {
+	socketTimeout=i;
+    }
+
+    public int getSoTimeout() {
+	return socketTimeout;
+    }
+
+    public void setMaxPort( int i ) {
+        maxPort=i;
+    }
+
+    public int getMaxPort() {
+        return maxPort;
+    }
+
+    /** At startup we'll look for the first free port in the range.
+        The difference between this port and the beggining of the range
+        is the 'id'.
+        This is usefull for lb cases ( less config ).
+    */
+    public int getInstanceId() {
+        return port-startPort;
+    }
+
+    /** If set to false, the thread pool will be created in
+     *  non-daemon mode, and will prevent main from exiting
+     */
+    public void setDaemon( boolean b ) {
+        tp.setDaemon( b );
+    }
+
+    public boolean getDaemon() {
+        return tp.getDaemon();
+    }
+
+
+    public void setMaxThreads( int i ) {
+        if( log.isDebugEnabled()) log.debug("Setting maxThreads " + i);
+        tp.setMaxThreads(i);
+    }
+    
+	public void setMinSpareThreads( int i ) {
+        if( log.isDebugEnabled()) log.debug("Setting minSpareThreads " + i);
+            tp.setMinSpareThreads(i);
+	}
+
+	public void setMaxSpareThreads( int i ) {
+        if( log.isDebugEnabled()) log.debug("Setting maxSpareThreads " + i);
+            tp.setMaxSpareThreads(i);
+	}
+
+    public int getMaxThreads() {
+        return tp.getMaxThreads();   
+    }
+    
+	public int getMinSpareThreads() {
+        return tp.getMinSpareThreads();   
+	}
+
+	public int getMaxSpareThreads() {
+        return tp.getMaxSpareThreads();   
+	}
+
+    public void setBacklog(int i) {
+    }
+    
+    
+    /* ==================== ==================== */
+    ServerSocket sSocket;
+    final int socketNote=1;
+    final int isNote=2;
+    final int osNote=3;
+    final int notifNote=4;
+    boolean paused = false;
+
+    public void pause() throws Exception {
+        synchronized(this) {
+            paused = true;
+            unLockSocket();
+        }
+    }
+
+    public void resume() throws Exception {
+        synchronized(this) {
+            paused = false;
+            notify();
+        }
+    }
+
+
+    public void accept( MsgContext ep ) throws IOException {
+        if( sSocket==null ) return;
+        synchronized(this) {
+            while(paused) {
+                try{ 
+                    wait();
+                } catch(InterruptedException ie) {
+                    //Ignore, since can't happen
+                }
+            }
+        }
+        Socket s=sSocket.accept();
+        ep.setNote( socketNote, s );
+        if(log.isDebugEnabled() )
+            log.debug("Accepted socket " + s );
+        if( linger > 0 )
+            s.setSoLinger( true, linger);
+        if( socketTimeout > 0 ) 
+            s.setSoTimeout( socketTimeout );
+        
+        s.setTcpNoDelay( tcpNoDelay ); // set socket tcpnodelay state
+        
+        requestCount++;
+
+        InputStream is=new BufferedInputStream(s.getInputStream());
+        OutputStream os;
+        if( BUFFER_WRITE )
+            os = new BufferedOutputStream( s.getOutputStream());
+        else
+            os = s.getOutputStream();
+        ep.setNote( isNote, is );
+        ep.setNote( osNote, os );
+        ep.setControl( tp );
+    }
+
+    public void resetCounters() {
+        requestCount=0;
+    }
+
+    /** Called after you change some fields at runtime using jmx.
+        Experimental for now.
+    */
+    public void reinit() throws IOException {
+        destroy();
+        init();
+    }
+
+    /**
+     * @jmx:managed-operation
+     */
+    public void init() throws IOException {
+        // Find a port.
+        if (startPort == 0) {
+            port = 0;
+            log.info("JK2: ajp13 disabling channelSocket");
+            running = true;
+            return;
+        }
+        if (maxPort < startPort)
+            maxPort = startPort;
+        for( int i=startPort; i<=maxPort; i++ ) {
+            try {
+                if( inet == null ) {
+                    sSocket = new ServerSocket( i, 0 );
+                } else {
+                    sSocket=new ServerSocket( i, 0, inet );
+                }
+                port=i;
+                break;
+            } catch( IOException ex ) {
+                log.info("Port busy " + i + " " + ex.toString());
+                continue;
+            }
+        }
+
+        if( sSocket==null ) {
+            log.error("Can't find free port " + startPort + " " + maxPort );
+            return;
+        }
+        log.info("JK2: ajp13 listening on " + getAddress() + ":" + port );
+
+        // If this is not the base port and we are the 'main' channleSocket and
+        // SHM didn't already set the localId - we'll set the instance id
+        if( "channelSocket".equals( name ) &&
+            port != startPort &&
+            (wEnv.getLocalId()==0) ) {
+            wEnv.setLocalId(  port - startPort );
+        }
+        if( serverTimeout > 0 )
+            sSocket.setSoTimeout( serverTimeout );
+
+        // XXX Reverse it -> this is a notification generator !!
+        if( next==null && wEnv!=null ) {
+            if( nextName!=null )
+                setNext( wEnv.getHandler( nextName ) );
+            if( next==null )
+                next=wEnv.getHandler( "dispatch" );
+            if( next==null )
+                next=wEnv.getHandler( "request" );
+        }
+        JMXRequestNote =wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "requestNote");
+        running = true;
+
+        // Run a thread that will accept connections.
+        // XXX Try to find a thread first - not sure how...
+        if( this.domain != null ) {
+            try {
+                tpOName=new ObjectName(domain + ":type=ThreadPool,name=" + 
+                                       getChannelName());
+
+                Registry.getRegistry(null, null)
+                    .registerComponent(tp, tpOName, null);
+
+                rgOName = new ObjectName
+                    (domain+":type=GlobalRequestProcessor,name=" + getChannelName());
+                Registry.getRegistry(null, null)
+                    .registerComponent(global, rgOName, null);
+            } catch (Exception e) {
+                log.error("Can't register threadpool" );
+            }
+        }
+
+        tp.start();
+        SocketAcceptor acceptAjp=new SocketAcceptor(  this );
+        tp.runIt( acceptAjp);
+
+    }
+
+    ObjectName tpOName;
+    ObjectName rgOName;
+    RequestGroupInfo global=new RequestGroupInfo();
+    int JMXRequestNote;
+
+    public void start() throws IOException{
+        if( sSocket==null )
+            init();
+    }
+
+    public void stop() throws IOException {
+        destroy();
+    }
+
+    public void registerRequest(Request req, MsgContext ep, int count) {
+        if(this.domain != null) {
+            try {
+                RequestInfo rp=req.getRequestProcessor();
+                rp.setGlobalProcessor(global);
+                ObjectName roname = new ObjectName
+                    (getDomain() + ":type=RequestProcessor,worker="+
+                     getChannelName()+",name=JkRequest" +count);
+                ep.setNote(JMXRequestNote, roname);
+                        
+                Registry.getRegistry().registerComponent( rp, roname, null);
+            } catch( Exception ex ) {
+                log.warn("Error registering request");
+            }
+        }
+    }
+
+    public void open(MsgContext ep) throws IOException {
+    }
+
+    
+    public void close(MsgContext ep) throws IOException {
+        Socket s=(Socket)ep.getNote( socketNote );
+        s.close();
+    }
+
+    private void unLockSocket() throws IOException {
+        // Need to create a connection to unlock the accept();
+        Socket s;
+        InetAddress ladr = inet;
+
+        if (ladr == null || "0.0.0.0".equals(ladr.getHostAddress())) {
+            ladr = InetAddress.getLocalHost();
+        }
+        s=new Socket(ladr, port );
+        // setting soLinger to a small value will help shutdown the
+        // connection quicker
+        s.setSoLinger(true, 0);
+
+	s.close();
+    }
+
+    public void destroy() throws IOException {
+        running = false;
+        try {
+            /* If we disabled the channel return */
+            if (port == 0)
+                return;
+            tp.shutdown();
+
+	    if(!paused) {
+		unLockSocket();
+	    }
+
+            sSocket.close(); // XXX?
+            
+            if( tpOName != null )  {
+                Registry.getRegistry().unregisterComponent(tpOName);
+            }
+            if( rgOName != null ) {
+                Registry.getRegistry().unregisterComponent(rgOName);
+            }
+        } catch(Exception e) {
+            log.info("Error shutting down the channel " + port + " " +
+                    e.toString());
+            if( log.isDebugEnabled() ) log.debug("Trace", e);
+        }
+    }
+
+    public int send( Msg msg, MsgContext ep)
+        throws IOException
+    {
+        msg.end(); // Write the packet header
+        byte buf[]=msg.getBuffer();
+        int len=msg.getLen();
+        
+        if(log.isTraceEnabled() )
+            log.trace("send() " + len + " " + buf[4] );
+
+        OutputStream os=(OutputStream)ep.getNote( osNote );
+        os.write( buf, 0, len );
+        return len;
+    }
+
+    public int flush( Msg msg, MsgContext ep)
+        throws IOException
+    {
+        if( BUFFER_WRITE ) {
+            OutputStream os=(OutputStream)ep.getNote( osNote );
+            os.flush();
+        }
+        return 0;
+    }
+
+    public int receive( Msg msg, MsgContext ep )
+        throws IOException
+    {
+        if (log.isDebugEnabled()) {
+            log.debug("receive() ");
+        }
+
+        byte buf[]=msg.getBuffer();
+        int hlen=msg.getHeaderLength();
+        
+	// XXX If the length in the packet header doesn't agree with the
+	// actual number of bytes read, it should probably return an error
+	// value.  Also, callers of this method never use the length
+	// returned -- should probably return true/false instead.
+
+        int rd = this.read(ep, buf, 0, hlen );
+        
+        if(rd < 0) {
+            // Most likely normal apache restart.
+            // log.warn("Wrong message " + rd );
+            return rd;
+        }
+
+        msg.processHeader();
+
+        /* After processing the header we know the body
+           length
+        */
+        int blen=msg.getLen();
+        
+	// XXX check if enough space - it's assert()-ed !!!
+        
+ 	int total_read = 0;
+        
+        total_read = this.read(ep, buf, hlen, blen);
+        
+        if ((total_read <= 0) && (blen > 0)) {
+            log.warn("can't read body, waited #" + blen);
+            return  -1;
+        }
+        
+        if (total_read != blen) {
+             log.warn( "incomplete read, waited #" + blen +
+                        " got only " + total_read);
+            return -2;
+        }
+        
+	return total_read;
+    }
+    
+    /**
+     * Read N bytes from the InputStream, and ensure we got them all
+     * Under heavy load we could experience many fragmented packets
+     * just read Unix Network Programming to recall that a call to
+     * read didn't ensure you got all the data you want
+     *
+     * from read() Linux manual
+     *
+     * On success, the number of bytes read is returned (zero indicates end
+     * of file),and the file position is advanced by this number.
+     * It is not an error if this number is smaller than the number of bytes
+     * requested; this may happen for example because fewer bytes
+     * are actually available right now (maybe because we were close to
+     * end-of-file, or because we are reading from a pipe, or  from  a
+     * terminal),  or  because  read()  was interrupted by a signal.
+     * On error, -1 is returned, and errno is set appropriately. In this
+     * case it is left unspecified whether the file position (if any) changes.
+     *
+     **/
+    public int read( MsgContext ep, byte[] b, int offset, int len)
+        throws IOException
+    {
+        InputStream is=(InputStream)ep.getNote( isNote );
+        int pos = 0;
+        int got;
+
+        while(pos < len) {
+            try {
+                got = is.read(b, pos + offset, len - pos);
+            } catch(SocketException sex) {
+                if(pos > 0) {
+                    log.info("Error reading data after "+pos+"bytes",sex);
+                } else {
+                    log.debug("Error reading data", sex);
+                }
+                got = -1;
+            }
+            if (log.isTraceEnabled()) {
+                log.trace("read() " + b + " " + (b==null ? 0: b.length) + " " +
+                          offset + " " + len + " = " + got );
+            }
+
+            // connection just closed by remote. 
+            if (got <= 0) {
+                // This happens periodically, as apache restarts
+                // periodically.
+                // It should be more gracefull ! - another feature for Ajp14
+                // log.warn( "server has closed the current connection (-1)" );
+                return -3;
+            }
+
+            pos += got;
+        }
+        return pos;
+    }
+    
+    protected boolean running=true;
+    
+    /** Accept incoming connections, dispatch to the thread pool
+     */
+    void acceptConnections() {
+        if( log.isDebugEnabled() )
+            log.debug("Accepting ajp connections on " + port);
+        while( running ) {
+	    try{
+                MsgContext ep=new MsgContext();
+                ep.setSource(this);
+                ep.setWorkerEnv( wEnv );
+                this.accept(ep);
+
+                if( !running ) break;
+                
+                // Since this is a long-running connection, we don't care
+                // about the small GC
+                SocketConnection ajpConn=
+                    new SocketConnection(this, ep);
+                tp.runIt( ajpConn );
+	    }catch(Exception ex) {
+                if (running)
+                    log.warn("Exception executing accept" ,ex);
+	    }
+        }
+    }
+
+    /** Process a single ajp connection.
+     */
+    void processConnection(MsgContext ep) {
+        try {
+            MsgAjp recv=new MsgAjp();
+            while( running ) {
+                if(paused) { // Drop the connection on pause
+                    break;
+                }
+                int status= this.receive( recv, ep );
+                if( status <= 0 ) {
+                    if( status==-3)
+                        log.debug( "server has been restarted or reset this connection" );
+                    else 
+                        log.warn("Closing ajp connection " + status );
+                    break;
+                }
+                ep.setLong( MsgContext.TIMER_RECEIVED, System.currentTimeMillis());
+                
+                ep.setType( 0 );
+                // Will call next
+                status= this.invoke( recv, ep );
+                if( status!= JkHandler.OK ) {
+                    log.warn("processCallbacks status " + status );
+                    break;
+                }
+            }
+        } catch( Exception ex ) {
+            if( ex.getMessage().indexOf( "Connection reset" ) >= 0)
+                log.debug( "Server has been restarted or reset this connection");
+            else if (ex.getMessage().indexOf( "Read timed out" ) >=0 )
+                log.info( "connection timeout reached");            
+            else
+                log.error( "Error, processing connection", ex);
+        }
+        finally {
+	    	/*
+	    	 * Whatever happened to this connection (remote closed it, timeout, read error)
+	    	 * the socket SHOULD be closed, or we may be in situation where the webserver
+	    	 * will continue to think the socket is still open and will forward request
+	    	 * to tomcat without receiving ever a reply
+	    	 */
+            try {
+                this.close( ep );
+            }
+            catch( Exception e) {
+                log.error( "Error, closing connection", e);
+            }
+            try{
+                Request req = (Request)ep.getRequest();
+                if( req != null ) {
+                    ObjectName roname = (ObjectName)ep.getNote(JMXRequestNote);
+                    Registry.getRegistry().unregisterComponent(roname);
+                    req.getRequestProcessor().setGlobalProcessor(null);
+                }
+            } catch( Exception ee) {
+                log.error( "Error, releasing connection",ee);
+            }
+        }
+    }
+
+    // XXX This should become handleNotification
+    public int invoke( Msg msg, MsgContext ep ) throws IOException {
+        int type=ep.getType();
+
+        switch( type ) {
+        case JkHandler.HANDLE_RECEIVE_PACKET:
+            if( log.isDebugEnabled()) log.debug("RECEIVE_PACKET ?? ");
+            return receive( msg, ep );
+        case JkHandler.HANDLE_SEND_PACKET:
+            return send( msg, ep );
+        case JkHandler.HANDLE_FLUSH:
+            return flush( msg, ep );
+        }
+
+        if( log.isDebugEnabled() )
+            log.debug("Call next " + type + " " + next);
+
+        // Send notification
+        if( nSupport!=null ) {
+            Notification notif=(Notification)ep.getNote(notifNote);
+            if( notif==null ) {
+                notif=new Notification("channelSocket.message", ep, requestCount );
+                ep.setNote( notifNote, notif);
+            }
+            nSupport.sendNotification(notif);
+        }
+
+        if( next != null ) {
+            return next.invoke( msg, ep );
+        } else {
+            log.info("No next ");
+        }
+
+        return OK;
+    }
+    
+    public boolean isSameAddress(MsgContext ep) {
+        Socket s=(Socket)ep.getNote( socketNote );
+        return isSameAddress( s.getLocalAddress(), s.getInetAddress());
+    }
+    
+    public String getChannelName() {
+        String encodedAddr = "";
+        if (inet != null && !"0.0.0.0".equals(inet.getHostAddress())) {
+            encodedAddr = getAddress();
+            if (encodedAddr.startsWith("/"))
+                encodedAddr = encodedAddr.substring(1);
+	    encodedAddr = URLEncoder.encode(encodedAddr) + "-";
+        }
+        return ("jk-" + encodedAddr + port);
+    }
+    
+    /**
+     * Return <code>true</code> if the specified client and server addresses
+     * are the same.  This method works around a bug in the IBM 1.1.8 JVM on
+     * Linux, where the address bytes are returned reversed in some
+     * circumstances.
+     *
+     * @param server The server's InetAddress
+     * @param client The client's InetAddress
+     */
+    public static boolean isSameAddress(InetAddress server, InetAddress client)
+    {
+	// Compare the byte array versions of the two addresses
+	byte serverAddr[] = server.getAddress();
+	byte clientAddr[] = client.getAddress();
+	if (serverAddr.length != clientAddr.length)
+	    return (false);
+	boolean match = true;
+	for (int i = 0; i < serverAddr.length; i++) {
+	    if (serverAddr[i] != clientAddr[i]) {
+		match = false;
+		break;
+	    }
+	}
+	if (match)
+	    return (true);
+
+	// Compare the reversed form of the two addresses
+	for (int i = 0; i < serverAddr.length; i++) {
+	    if (serverAddr[i] != clientAddr[(serverAddr.length-1)-i])
+		return (false);
+	}
+	return (true);
+    }
+
+    public void sendNewMessageNotification(Notification notification) {
+        if( nSupport!= null )
+            nSupport.sendNotification(notification);
+    }
+
+    private NotificationBroadcasterSupport nSupport= null;
+
+    public void addNotificationListener(NotificationListener listener,
+                                        NotificationFilter filter,
+                                        Object handback)
+            throws IllegalArgumentException
+    {
+        if( nSupport==null ) nSupport=new NotificationBroadcasterSupport();
+        nSupport.addNotificationListener(listener, filter, handback);
+    }
+
+    public void removeNotificationListener(NotificationListener listener)
+            throws ListenerNotFoundException
+    {
+        if( nSupport!=null)
+            nSupport.removeNotificationListener(listener);
+    }
+
+    MBeanNotificationInfo notifInfo[]=new MBeanNotificationInfo[0];
+
+    public void setNotificationInfo( MBeanNotificationInfo info[]) {
+        this.notifInfo=info;
+    }
+
+    public MBeanNotificationInfo[] getNotificationInfo() {
+        return notifInfo;
+    }
+}
+
+class SocketAcceptor implements ThreadPoolRunnable {
+    ChannelSocket wajp;
+    
+    SocketAcceptor(ChannelSocket wajp ) {
+        this.wajp=wajp;
+    }
+
+    public Object[] getInitData() {
+        return null;
+    }
+
+    public void runIt(Object thD[]) {
+        wajp.acceptConnections();
+    }
+}
+
+class SocketConnection implements ThreadPoolRunnable {
+    ChannelSocket wajp;
+    MsgContext ep;
+
+    SocketConnection(ChannelSocket wajp, MsgContext ep) {
+        this.wajp=wajp;
+        this.ep=ep;
+    }
+
+
+    public Object[] getInitData() {
+        return null;
+    }
+    
+    public void runIt(Object perTh[]) {
+        wajp.processConnection(ep);
+    }
+}
diff --git a/connectors/jk/java/org/apache/jk/common/ChannelUn.java b/connectors/jk/java/org/apache/jk/common/ChannelUn.java
new file mode 100644
index 0000000..3d1a9e8
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/ChannelUn.java
@@ -0,0 +1,392 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.common;
+
+import java.net.URLEncoder;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import javax.management.ObjectName;
+
+import org.apache.commons.modeler.Registry;
+import org.apache.jk.core.JkHandler;
+import org.apache.jk.core.Msg;
+import org.apache.jk.core.MsgContext;
+import org.apache.jk.core.JkChannel;
+import org.apache.jk.core.WorkerEnv;
+import org.apache.coyote.Request;
+import org.apache.coyote.RequestGroupInfo;
+import org.apache.coyote.RequestInfo;
+import org.apache.tomcat.util.threads.ThreadPool;
+import org.apache.tomcat.util.threads.ThreadPoolRunnable;
+
+
+/** Pass messages using unix domain sockets.
+ *
+ * @author Costin Manolache
+ */
+public class ChannelUn extends JniHandler implements JkChannel {
+    static final int CH_OPEN=4;
+    static final int CH_CLOSE=5;
+    static final int CH_READ=6;
+    static final int CH_WRITE=7;
+
+    String file;
+    ThreadPool tp = ThreadPool.createThreadPool(true);
+
+    /* ==================== Tcp socket options ==================== */
+
+    public ThreadPool getThreadPool() {
+        return tp;
+    }
+    
+    public void setFile( String f ) {
+        file=f;
+    }
+    
+    public String getFile() {
+        return file;
+    }
+    
+    /* ==================== ==================== */
+    int socketNote=1;
+    int isNote=2;
+    int osNote=3;
+    
+    int localId=0;
+    
+    public void init() throws IOException {
+        if( file==null ) {
+            log.debug("No file, disabling unix channel");
+            return;
+            //throw new IOException( "No file for the unix socket channel");
+        }
+        if( wEnv!=null && wEnv.getLocalId() != 0 ) {
+            localId=wEnv.getLocalId();
+        }
+
+        if( localId != 0 ) {
+            file=file+ localId;
+        }
+        File socketFile=new File( file );
+        if( !socketFile.isAbsolute() ) {
+            String home=wEnv.getJkHome();
+            if( home==null ) {
+                log.debug("No jkhome");
+            } else {
+                File homef=new File( home );
+                socketFile=new File( homef, file );
+                log.debug( "Making the file absolute " +socketFile);
+            }
+        }
+        
+        if( ! socketFile.exists() ) {
+            try {
+                FileOutputStream fos=new FileOutputStream(socketFile);
+                fos.write( 1 );
+                fos.close();
+            } catch( Throwable t ) {
+                log.error("Attempting to create the file failed, disabling channel" 
+                        + socketFile);
+                return;
+            }
+        }
+        // The socket file cannot be removed ...
+        if (!socketFile.delete()) {
+            log.error( "Can't remove socket file " + socketFile);
+            return;
+        }
+        
+
+        super.initNative( "channel.un:" + file );
+
+        if( apr==null || ! apr.isLoaded() ) {
+            log.debug("Apr is not available, disabling unix channel ");
+            apr=null;
+            return;
+        }
+        
+        // Set properties and call init.
+        setNativeAttribute( "file", file );
+        // unixListenSocket=apr.unSocketListen( file, 10 );
+
+        setNativeAttribute( "listen", "10" );
+        // setNativeAttribute( "debug", "10" );
+
+        // Initialize the thread pool and execution chain
+        if( next==null && wEnv!=null ) {
+            if( nextName!=null ) 
+                setNext( wEnv.getHandler( nextName ) );
+            if( next==null )
+                next=wEnv.getHandler( "dispatch" );
+            if( next==null )
+                next=wEnv.getHandler( "request" );
+        }
+
+        super.initJkComponent();
+        JMXRequestNote =wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "requestNote");        
+        // Run a thread that will accept connections.
+        if( this.domain != null ) {
+            try {
+                tpOName=new ObjectName(domain + ":type=ThreadPool,name=" + 
+				       getChannelName());
+
+                Registry.getRegistry(null, null)
+		    .registerComponent(tp, tpOName, null);
+
+		rgOName = new ObjectName
+		    (domain+":type=GlobalRequestProcessor,name=" + getChannelName());
+		Registry.getRegistry(null, null)
+		    .registerComponent(global, rgOName, null);
+            } catch (Exception e) {
+                log.error("Can't register threadpool" );
+            }
+        }
+        tp.start();
+        AprAcceptor acceptAjp=new AprAcceptor(  this );
+        tp.runIt( acceptAjp);
+        log.info("JK: listening on unix socket: " + file );
+        
+    }
+
+    ObjectName tpOName;
+    ObjectName rgOName;
+    RequestGroupInfo global=new RequestGroupInfo();
+    int count = 0;
+    int JMXRequestNote;
+
+    public void start() throws IOException {
+    }
+
+    public void destroy() throws IOException {
+        if( apr==null ) return;
+        try {
+            if( tp != null )
+                tp.shutdown();
+            
+            //apr.unSocketClose( unixListenSocket,3);
+            super.destroyJkComponent();
+
+            if(tpOName != null) {
+		Registry.getRegistry().unregisterComponent(tpOName);
+	    }
+	    if(rgOName != null) {
+		Registry.getRegistry().unregisterComponent(rgOName);
+	    }
+        } catch(Exception e) {
+            log.error("Error in destroy",e);
+        }
+    }
+
+    public void registerRequest(Request req, MsgContext ep, int count) {
+	if(this.domain != null) {
+	    try {
+
+		RequestInfo rp=req.getRequestProcessor();
+		rp.setGlobalProcessor(global);
+		ObjectName roname = new ObjectName
+		    (getDomain() + ":type=RequestProcessor,worker="+
+		     getChannelName()+",name=JkRequest" +count);
+		ep.setNote(JMXRequestNote, roname);
+                        
+		Registry.getRegistry().registerComponent( rp, roname, null);
+	    } catch( Exception ex ) {
+		log.warn("Error registering request");
+	    }
+	}
+    }
+
+
+    /** Open a connection - since we're listening that will block in
+        accept
+    */
+    public int open(MsgContext ep) throws IOException {
+        // Will associate a jk_endpoint with ep and call open() on it.
+        // jk_channel_un will accept a connection and set the socket info
+        // in the endpoint. MsgContext will represent an active connection.
+        return super.nativeDispatch( ep.getMsg(0), ep, CH_OPEN, 1 );
+    }
+    
+    public void close(MsgContext ep) throws IOException {
+        super.nativeDispatch( ep.getMsg(0), ep, CH_CLOSE, 1 );
+    }
+
+    public int send( Msg msg, MsgContext ep)
+        throws IOException
+    {
+        return super.nativeDispatch( msg, ep, CH_WRITE, 0 );
+    }
+
+    public int receive( Msg msg, MsgContext ep )
+        throws IOException
+    {
+        int rc=super.nativeDispatch( msg, ep, CH_READ, 1 );
+
+        if( rc!=0 ) {
+            log.error("receive error:   " + rc, new Throwable());
+            return -1;
+        }
+        
+        msg.processHeader();
+        
+        if (log.isDebugEnabled())
+             log.debug("receive:  total read = " + msg.getLen());
+
+	return msg.getLen();
+    }
+
+    public int flush( Msg msg, MsgContext ep) throws IOException {
+	return OK;
+    }
+
+    public boolean isSameAddress( MsgContext ep ) {
+	return false; // Not supporting shutdown on this channel.
+    }
+
+    boolean running=true;
+    
+    /** Accept incoming connections, dispatch to the thread pool
+     */
+    void acceptConnections() {
+        if( apr==null ) return;
+
+        if( log.isDebugEnabled() )
+            log.debug("Accepting ajp connections on " + file);
+        
+        while( running ) {
+            try {
+                MsgContext ep=this.createMsgContext();
+
+                // blocking - opening a server connection.
+                int status=this.open(ep);
+                if( status != 0 && status != 2 ) {
+                    log.error( "Error acceptin connection on " + file );
+                    break;
+                }
+
+                //    if( log.isDebugEnabled() )
+                //     log.debug("Accepted ajp connections ");
+        
+                AprConnection ajpConn= new AprConnection(this, ep);
+                tp.runIt( ajpConn );
+            } catch( Exception ex ) {
+                ex.printStackTrace();
+            }
+        }
+    }
+
+    /** Process a single ajp connection.
+     */
+    void processConnection(MsgContext ep) {
+        if( log.isDebugEnabled() )
+            log.debug( "New ajp connection ");
+        try {
+            MsgAjp recv=new MsgAjp();
+            while( running ) {
+                int res=this.receive( recv, ep );
+                if( res<0 ) {
+                    // EOS
+                    break;
+                }
+                ep.setType(0);
+                log.debug( "Process msg ");
+                int status=next.invoke( recv, ep );
+            }
+            if( log.isDebugEnabled() )
+                log.debug( "Closing un channel");
+            try{
+                Request req = (Request)ep.getRequest();
+                if( req != null ) {
+                    ObjectName roname = (ObjectName)ep.getNote(JMXRequestNote);
+                    Registry.getRegistry().unregisterComponent(roname);
+                    req.getRequestProcessor().setGlobalProcessor(null);
+                }
+            } catch( Exception ee) {
+                log.error( "Error, releasing connection",ee);
+            }
+            this.close( ep );
+        } catch( Exception ex ) {
+            ex.printStackTrace();
+        }
+    }
+
+    public int invoke( Msg msg, MsgContext ep ) throws IOException {
+        int type=ep.getType();
+
+        switch( type ) {
+        case JkHandler.HANDLE_RECEIVE_PACKET:
+            return receive( msg, ep );
+        case JkHandler.HANDLE_SEND_PACKET:
+            return send( msg, ep );
+        case JkHandler.HANDLE_FLUSH:
+            return flush( msg, ep );
+        }
+
+        // return next.invoke( msg, ep );
+        return OK;
+    }
+
+    public String getChannelName() {
+        String encodedAddr = "";
+        String address = file;
+        if (address != null) {
+            encodedAddr = "" + address;
+            if (encodedAddr.startsWith("/"))
+                encodedAddr = encodedAddr.substring(1);
+            encodedAddr = URLEncoder.encode(encodedAddr) ;
+        }
+        return ("jk-" + encodedAddr);
+    }
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( ChannelUn.class );
+}
+
+class AprAcceptor implements ThreadPoolRunnable {
+    ChannelUn wajp;
+    
+    AprAcceptor(ChannelUn wajp ) {
+        this.wajp=wajp;
+    }
+
+    public Object[] getInitData() {
+        return null;
+    }
+
+    public void runIt(Object thD[]) {
+        wajp.acceptConnections();
+    }
+}
+
+class AprConnection implements ThreadPoolRunnable {
+    ChannelUn wajp;
+    MsgContext ep;
+
+    AprConnection(ChannelUn wajp, MsgContext ep) {
+        this.wajp=wajp;
+        this.ep=ep;
+    }
+
+
+    public Object[] getInitData() {
+        return null;
+    }
+    
+    public void runIt(Object perTh[]) {
+        wajp.processConnection(ep);
+    }
+}
diff --git a/connectors/jk/java/org/apache/jk/common/HandlerDispatch.java b/connectors/jk/java/org/apache/jk/common/HandlerDispatch.java
new file mode 100644
index 0000000..d40b14c
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/HandlerDispatch.java
@@ -0,0 +1,100 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.common;
+
+import java.io.IOException;
+
+import org.apache.jk.core.JkHandler;
+import org.apache.jk.core.Msg;
+import org.apache.jk.core.MsgContext;
+
+
+
+
+/**
+ * Dispatch based on the message type. ( XXX make it more generic,
+ * now it's specific to ajp13 ).
+ * 
+ * @author Costin Manolache
+ */
+public class HandlerDispatch extends JkHandler
+{
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( HandlerDispatch.class );
+
+    public HandlerDispatch() 
+    {
+    }
+
+    public void init() {
+    }
+
+    JkHandler handlers[]=new JkHandler[MAX_HANDLERS];
+    String handlerNames[]=new String[MAX_HANDLERS];
+    
+    static final int MAX_HANDLERS=32;    
+    static final int RESERVED=16;  // reserved names, backward compat
+    int currentId=RESERVED;
+
+    public int registerMessageType( int id, String name, JkHandler h,
+                                    String sig[] )
+    {
+        if( log.isDebugEnabled() )
+            log.debug( "Register message " + id + " " + h.getName() +
+                 " " + h.getClass().getName());
+	if( id < 0 ) {
+	    // try to find it by name
+	    for( int i=0; i< handlerNames.length; i++ ) {
+                if( handlerNames[i]==null ) continue;
+                if( name.equals( handlerNames[i] ) )
+                    return i;
+            }
+	    handlers[currentId]=h;
+            handlerNames[currentId]=name;
+	    currentId++;
+	    return currentId;
+	}
+	handlers[id]=h;
+        handlerNames[currentId]=name;
+	return id;
+    }
+
+    
+    // -------------------- Incoming message --------------------
+
+    public int invoke(Msg msg, MsgContext ep ) 
+        throws IOException
+    {
+        int type=msg.peekByte();
+        ep.setType( type );
+        
+        if( type > handlers.length ||
+            handlers[type]==null ) {
+	    if( log.isDebugEnabled() )
+                log.debug( "Invalid handler " + type );
+	    return ERROR;
+	}
+
+        if( log.isDebugEnabled() )
+            log.debug( "Received " + type + " " + handlers[type].getName());
+        
+	JkHandler handler=handlers[type];
+        
+        return handler.invoke( msg, ep );
+    }
+
+ }
diff --git a/connectors/jk/java/org/apache/jk/common/HandlerRequest.java b/connectors/jk/java/org/apache/jk/common/HandlerRequest.java
new file mode 100644
index 0000000..34cc36b
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/HandlerRequest.java
@@ -0,0 +1,751 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.common;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.CharConversionException;
+import java.net.InetAddress;
+import java.util.Properties;
+import javax.management.ObjectName;
+
+import org.apache.commons.modeler.Registry;
+import org.apache.coyote.Request;
+import org.apache.coyote.RequestGroupInfo;
+import org.apache.coyote.RequestInfo;
+import org.apache.coyote.Response;
+import org.apache.coyote.Constants;
+import org.apache.jk.core.JkHandler;
+import org.apache.jk.core.Msg;
+import org.apache.jk.core.MsgContext;
+import org.apache.jk.core.WorkerEnv;
+import org.apache.jk.core.JkChannel;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.HexUtils;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.threads.ThreadWithAttributes;
+
+/**
+ * Handle messages related with basic request information.
+ *
+ * This object can handle the following incoming messages:
+ * - "FORWARD_REQUEST" input message ( sent when a request is passed from the
+ *   web server )
+ * - "RECEIVE_BODY_CHUNK" input ( sent by container to pass more body, in
+ *   response to GET_BODY_CHUNK )
+ *
+ * It can handle the following outgoing messages:
+ * - SEND_HEADERS. Pass the status code and headers.
+ * - SEND_BODY_CHUNK. Send a chunk of body
+ * - GET_BODY_CHUNK. Request a chunk of body data
+ * - END_RESPONSE. Notify the end of a request processing.
+ *
+ * @author Henri Gomez [hgomez@apache.org]
+ * @author Dan Milstein [danmil@shore.net]
+ * @author Keith Wannamaker [Keith@Wannamaker.org]
+ * @author Costin Manolache
+ */
+public class HandlerRequest extends JkHandler
+{
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( HandlerRequest.class );
+
+    // XXX Will move to a registry system.
+    
+    // Prefix codes for message types from server to container
+    public static final byte JK_AJP13_FORWARD_REQUEST   = 2;
+        public static final byte JK_AJP13_SHUTDOWN          = 7;
+        public static final byte JK_AJP13_PING_REQUEST      = 8;
+        public static final byte JK_AJP13_CPING_REQUEST     = 10;
+
+    // Prefix codes for message types from container to server
+    public static final byte JK_AJP13_SEND_BODY_CHUNK   = 3;
+    public static final byte JK_AJP13_SEND_HEADERS      = 4;
+    public static final byte JK_AJP13_END_RESPONSE      = 5;
+        public static final byte JK_AJP13_GET_BODY_CHUNK    = 6;
+        public static final byte JK_AJP13_CPONG_REPLY       = 9;
+    
+    // Integer codes for common response header strings
+    public static final int SC_RESP_CONTENT_TYPE        = 0xA001;
+    public static final int SC_RESP_CONTENT_LANGUAGE    = 0xA002;
+    public static final int SC_RESP_CONTENT_LENGTH      = 0xA003;
+    public static final int SC_RESP_DATE                = 0xA004;
+    public static final int SC_RESP_LAST_MODIFIED       = 0xA005;
+    public static final int SC_RESP_LOCATION            = 0xA006;
+    public static final int SC_RESP_SET_COOKIE          = 0xA007;
+    public static final int SC_RESP_SET_COOKIE2         = 0xA008;
+    public static final int SC_RESP_SERVLET_ENGINE      = 0xA009;
+    public static final int SC_RESP_STATUS              = 0xA00A;
+    public static final int SC_RESP_WWW_AUTHENTICATE    = 0xA00B;
+        
+    // Integer codes for common (optional) request attribute names
+    public static final byte SC_A_CONTEXT       = 1;  // XXX Unused
+    public static final byte SC_A_SERVLET_PATH  = 2;  // XXX Unused
+    public static final byte SC_A_REMOTE_USER   = 3;
+    public static final byte SC_A_AUTH_TYPE     = 4;
+    public static final byte SC_A_QUERY_STRING  = 5;
+    public static final byte SC_A_JVM_ROUTE     = 6;
+    public static final byte SC_A_SSL_CERT      = 7;
+    public static final byte SC_A_SSL_CIPHER    = 8;
+    public static final byte SC_A_SSL_SESSION   = 9;
+    public static final byte SC_A_SSL_KEYSIZE   = 11;
+    public static final byte SC_A_SECRET        = 12;
+    public static final byte SC_A_STORED_METHOD = 13;
+
+    // Used for attributes which are not in the list above
+    public static final byte SC_A_REQ_ATTRIBUTE = 10; 
+
+    // Terminates list of attributes
+    public static final byte SC_A_ARE_DONE      = (byte)0xFF;
+    
+    // Translates integer codes to names of HTTP methods
+    public static final String []methodTransArray = {
+        "OPTIONS",
+        "GET",
+        "HEAD",
+        "POST",
+        "PUT",
+        "DELETE",
+        "TRACE",
+        "PROPFIND",
+        "PROPPATCH",
+        "MKCOL",
+        "COPY",
+        "MOVE",
+        "LOCK",
+        "UNLOCK",
+        "ACL",
+        "REPORT",
+        "VERSION-CONTROL",
+        "CHECKIN",
+        "CHECKOUT",
+        "UNCHECKOUT",
+        "SEARCH",
+        "MKWORKSPACE",
+        "UPDATE",
+        "LABEL",
+        "MERGE",
+        "BASELINE-CONTROL",
+        "MKACTIVITY"
+    };
+    public static final int SC_M_JK_STORED = (byte) 0xFF;
+    
+    // id's for common request headers
+    public static final int SC_REQ_ACCEPT          = 1;
+    public static final int SC_REQ_ACCEPT_CHARSET  = 2;
+    public static final int SC_REQ_ACCEPT_ENCODING = 3;
+    public static final int SC_REQ_ACCEPT_LANGUAGE = 4;
+    public static final int SC_REQ_AUTHORIZATION   = 5;
+    public static final int SC_REQ_CONNECTION      = 6;
+    public static final int SC_REQ_CONTENT_TYPE    = 7;
+    public static final int SC_REQ_CONTENT_LENGTH  = 8;
+    public static final int SC_REQ_COOKIE          = 9;
+    public static final int SC_REQ_COOKIE2         = 10;
+    public static final int SC_REQ_HOST            = 11;
+    public static final int SC_REQ_PRAGMA          = 12;
+    public static final int SC_REQ_REFERER         = 13;
+    public static final int SC_REQ_USER_AGENT      = 14;
+    // AJP14 new header
+    public static final byte SC_A_SSL_KEY_SIZE  = 11; // XXX ??? 
+
+    // Translates integer codes to request header names    
+    public static final String []headerTransArray = {
+        "accept",
+        "accept-charset",
+        "accept-encoding",
+        "accept-language",
+        "authorization",
+        "connection",
+        "content-type",
+        "content-length",
+        "cookie",
+        "cookie2",
+        "host",
+        "pragma",
+        "referer",
+        "user-agent"
+    };
+
+    /*
+     * Note for Host parsing.
+     */
+    public static final int HOSTBUFFER = 10;
+
+    HandlerDispatch dispatch;
+    String ajpidDir="conf";
+    
+
+    public HandlerRequest() 
+    {
+    }
+
+    public void init() {
+        dispatch=(HandlerDispatch)wEnv.getHandler( "dispatch" );
+        if( dispatch != null ) {
+            // register incoming message handlers
+            dispatch.registerMessageType( JK_AJP13_FORWARD_REQUEST,
+                                          "JK_AJP13_FORWARD_REQUEST",
+                                          this, null); // 2
+            
+            dispatch.registerMessageType( JK_AJP13_SHUTDOWN,
+                                          "JK_AJP13_SHUTDOWN",
+                                          this, null); // 7
+            
+            dispatch.registerMessageType( JK_AJP13_CPING_REQUEST,
+                                          "JK_AJP13_CPING_REQUEST",
+                                           this, null); // 10
+            dispatch.registerMessageType( HANDLE_THREAD_END,
+                                         "HANDLE_THREAD_END",
+                                         this, null);
+            // register outgoing messages handler
+            dispatch.registerMessageType( JK_AJP13_SEND_BODY_CHUNK, // 3
+                                          "JK_AJP13_SEND_BODY_CHUNK",
+                                          this,null );
+        }
+
+        bodyNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "jkInputStream" );
+        tmpBufNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "tmpBuf" );
+        secretNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "secret" );
+
+        if( next==null )
+            next=wEnv.getHandler( "container" );
+        if( log.isDebugEnabled() )
+            log.debug( "Container handler " + next + " " + next.getName() +
+                       " " + next.getClass().getName());
+
+        // should happen on start()
+        generateAjp13Id();
+    }
+
+    public void setSecret( String s ) {
+        requiredSecret=s;
+    }
+
+    public void setUseSecret( boolean b ) {
+        requiredSecret=Double.toString(Math.random());
+    }
+
+    public void setDecodedUri( boolean b ) {
+        decoded=b;
+    }
+
+    public boolean isTomcatAuthentication() {
+        return tomcatAuthentication;
+    }
+
+    public void setTomcatAuthentication(boolean newTomcatAuthentication) {
+        tomcatAuthentication = newTomcatAuthentication;
+    }
+    
+    public void setAjpidDir( String path ) {
+        if( "".equals( path ) ) path=null;
+        ajpidDir=path;
+    }
+
+    /**
+     * Set the flag to tell if we JMX register requests.
+     */
+    public void setRegisterRequests(boolean srr) {
+        registerRequests = srr;
+    }
+
+    /**
+     * Get the flag to tell if we JMX register requests.
+     */
+    public boolean getRegisterRequests() {
+        return registerRequests;
+    }
+
+    // -------------------- Ajp13.id --------------------
+
+    private void generateAjp13Id() {
+        int portInt=8009; // tcpCon.getPort();
+        InetAddress address=null; // tcpCon.getAddress();
+
+        if( requiredSecret == null )
+            return;
+        
+        File f1=new File( wEnv.getJkHome() );
+        File f2=new File( f1, "conf" );
+        
+        if( ! f2.exists() ) {
+            log.error( "No conf dir for ajp13.id " + f2 );
+            return;
+        }
+        
+        File sf=new File( f2, "ajp13.id");
+        
+        if( log.isDebugEnabled())
+            log.debug( "Using stop file: "+sf);
+
+        try {
+            Properties props=new Properties();
+
+            props.put( "port", Integer.toString( portInt ));
+            if( address!=null ) {
+                props.put( "address", address.getHostAddress() );
+            }
+            if( requiredSecret !=null ) {
+                props.put( "secret", requiredSecret );
+            }
+
+            FileOutputStream stopF=new FileOutputStream( sf );
+            props.save( stopF, "Automatically generated, don't edit" );
+        } catch( IOException ex ) {
+            log.debug( "Can't create stop file: "+sf );
+            ex.printStackTrace();
+        }
+    }
+    
+    // -------------------- Incoming message --------------------
+    String requiredSecret=null;
+    int bodyNote;
+    int tmpBufNote;
+    int secretNote;
+
+    boolean decoded=true;
+    boolean tomcatAuthentication=true;
+    boolean registerRequests=true;
+    
+    public int invoke(Msg msg, MsgContext ep ) 
+        throws IOException
+    {
+        int type=msg.getByte();
+        ThreadWithAttributes twa = null;
+        if (Thread.currentThread() instanceof ThreadWithAttributes) {
+            twa = (ThreadWithAttributes) Thread.currentThread();
+        }
+        Object control=ep.getControl();
+
+        MessageBytes tmpMB=(MessageBytes)ep.getNote( tmpBufNote );
+        if( tmpMB==null ) {
+            tmpMB=new MessageBytes();
+            ep.setNote( tmpBufNote, tmpMB);
+        }
+        if( log.isDebugEnabled() )
+            log.debug( "Handling " + type );
+        
+        switch( type ) {
+        case JK_AJP13_FORWARD_REQUEST:
+            try {
+                if (twa != null) {
+                    twa.setCurrentStage(control, "JkDecode");
+                }
+                decodeRequest( msg, ep, tmpMB );
+                if (twa != null) {
+                    twa.setCurrentStage(control, "JkService");
+                    twa.setParam(control,
+                                 ((Request)ep.getRequest()).unparsedURI());
+                }
+            } catch( Exception ex ) {
+                log.error( "Error decoding request ", ex );
+                msg.dump( "Incomming message");
+                return ERROR;
+            }
+
+            if( requiredSecret != null ) {
+                String epSecret=(String)ep.getNote( secretNote );
+                if( epSecret==null || ! requiredSecret.equals( epSecret ) )
+                    return ERROR;
+            }
+            /* XXX it should be computed from request, by workerEnv */
+            if(log.isDebugEnabled() )
+                log.debug("Calling next " + next.getName() + " " +
+                  next.getClass().getName());
+
+            int err= next.invoke( msg, ep );
+            if (twa != null) {
+                twa.setCurrentStage(control, "JkDone");
+            }
+
+            if( log.isDebugEnabled() )
+                log.debug( "Invoke returned " + err );
+            return err;
+        case JK_AJP13_SHUTDOWN:
+            String epSecret=null;
+            if( msg.getLen() > 3 ) {
+                // we have a secret
+                msg.getBytes( tmpMB );
+                epSecret=tmpMB.toString();
+            }
+            
+            if( requiredSecret != null &&
+                requiredSecret.equals( epSecret ) ) {
+                if( log.isDebugEnabled() )
+                    log.debug("Received wrong secret, no shutdown ");
+                return ERROR;
+            }
+
+            // XXX add isSameAddress check
+            JkChannel ch=ep.getSource();
+	    if( !ch.isSameAddress(ep) ) {
+		log.error("Shutdown request not from 'same address' ");
+		return ERROR;
+	    }
+
+            // forward to the default handler - it'll do the shutdown
+            next.invoke( msg, ep );
+
+            log.info("Exiting");
+            System.exit(0);
+            
+            return OK;
+
+            // We got a PING REQUEST, quickly respond with a PONG
+        case JK_AJP13_CPING_REQUEST:
+            msg.reset();
+            msg.appendByte(JK_AJP13_CPONG_REPLY);
+            ep.setType( JkHandler.HANDLE_SEND_PACKET );
+            ep.getSource().send( msg, ep );
+	    return OK;
+
+        case HANDLE_THREAD_END:
+            return OK;
+
+        default:
+            log.info("Unknown message " + type);
+        }
+
+        return OK;
+    }
+
+    static int count = 0;
+
+    private int decodeRequest( Msg msg, MsgContext ep, MessageBytes tmpMB )
+        throws IOException
+    {
+        // FORWARD_REQUEST handler
+        Request req=(Request)ep.getRequest();
+        if( req==null ) {
+            req=new Request();
+            Response res=new Response();
+            req.setResponse(res);
+            ep.setRequest( req );
+            if( registerRequests ) {
+		ep.getSource().registerRequest(req, ep, count++);
+            }
+        }
+
+	RequestInfo rp = req.getRequestProcessor();
+	rp.setStage(Constants.STAGE_PARSE);
+        MessageBytes tmpMB2 = (MessageBytes)req.getNote(WorkerEnv.SSL_CERT_NOTE);
+        if(tmpMB2 != null) {
+            tmpMB2.recycle();
+        }
+        req.setStartTime(System.currentTimeMillis());
+        JkInputStream jkBody=(JkInputStream)ep.getNote( bodyNote );
+        if( jkBody==null ) {
+            jkBody=new JkInputStream();
+            jkBody.setMsgContext( ep );
+
+            ep.setNote( bodyNote, jkBody );
+        }
+
+        jkBody.recycle();
+        
+        // Translate the HTTP method code to a String.
+        byte methodCode = msg.getByte();
+        if (methodCode != SC_M_JK_STORED) {
+            String mName=methodTransArray[(int)methodCode - 1];
+            req.method().setString(mName);
+        }
+
+        msg.getBytes(req.protocol()); 
+        msg.getBytes(req.requestURI());
+
+        msg.getBytes(req.remoteAddr());
+        msg.getBytes(req.remoteHost());
+        msg.getBytes(req.localName());
+        req.setLocalPort(msg.getInt());
+
+        boolean isSSL = msg.getByte() != 0;
+        if( isSSL ) {
+            // XXX req.setSecure( true );
+            req.scheme().setString("https");
+        }
+
+        decodeHeaders( ep, msg, req, tmpMB );
+
+        decodeAttributes( ep, msg, req, tmpMB );
+
+	rp.setStage(Constants.STAGE_PREPARE);
+        MessageBytes valueMB = req.getMimeHeaders().getValue("host");
+        parseHost(valueMB, req);
+        // set cookies on request now that we have all headers
+        req.getCookies().setHeaders(req.getMimeHeaders());
+
+        // Check to see if there should be a body packet coming along
+        // immediately after
+        int cl=req.getContentLength();
+        if(cl > 0) {
+            jkBody.setContentLength( cl );
+            jkBody.receive();
+        }
+    
+        if (log.isTraceEnabled()) {
+            log.trace(req.toString());
+         }
+
+        return OK;
+    }
+        
+    private int decodeAttributes( MsgContext ep, Msg msg, Request req,
+                                  MessageBytes tmpMB) {
+        boolean moreAttr=true;
+
+        while( moreAttr ) {
+            byte attributeCode=msg.getByte();
+            if( attributeCode == SC_A_ARE_DONE )
+                return 200;
+
+            /* Special case ( XXX in future API make it separate type !)
+             */
+            if( attributeCode == SC_A_SSL_KEY_SIZE ) {
+                // Bug 1326: it's an Integer.
+                req.setAttribute(SSLSupport.KEY_SIZE_KEY,
+                                 new Integer( msg.getInt()));
+               //Integer.toString(msg.getInt()));
+            }
+
+            if( attributeCode == SC_A_REQ_ATTRIBUTE ) {
+                // 2 strings ???...
+                msg.getBytes( tmpMB );
+                String n=tmpMB.toString();
+                msg.getBytes( tmpMB );
+                String v=tmpMB.toString();
+                req.setAttribute(n, v );
+            }
+
+
+            // 1 string attributes
+            switch(attributeCode) {
+            case SC_A_CONTEXT      :
+                msg.getBytes( tmpMB );
+                // nothing
+                break;
+                
+            case SC_A_SERVLET_PATH :
+                msg.getBytes( tmpMB );
+                // nothing 
+                break;
+                
+            case SC_A_REMOTE_USER  :
+                if( tomcatAuthentication ) {
+                    // ignore server
+                    msg.getBytes( tmpMB );
+                } else {
+                    msg.getBytes(req.getRemoteUser());
+                }
+                break;
+                
+            case SC_A_AUTH_TYPE    :
+                if( tomcatAuthentication ) {
+                    // ignore server
+                    msg.getBytes( tmpMB );
+                } else {
+                    msg.getBytes(req.getAuthType());
+                }
+                break;
+                
+            case SC_A_QUERY_STRING :
+                msg.getBytes(req.queryString());
+                break;
+                
+            case SC_A_JVM_ROUTE    :
+                msg.getBytes(req.instanceId());
+                break;
+                
+            case SC_A_SSL_CERT     :
+                req.scheme().setString( "https" );
+                // Transform the string into certificate.
+                MessageBytes tmpMB2 = (MessageBytes)req.getNote(WorkerEnv.SSL_CERT_NOTE);
+                if(tmpMB2 == null) {
+                    tmpMB2 = new MessageBytes();
+                    req.setNote(WorkerEnv.SSL_CERT_NOTE, tmpMB2);
+                }
+                // SSL certificate extraction is costy, moved to JkCoyoteHandler
+                msg.getBytes(tmpMB2);
+                break;
+                
+            case SC_A_SSL_CIPHER   :
+                req.scheme().setString( "https" );
+                msg.getBytes(tmpMB);
+                req.setAttribute(SSLSupport.CIPHER_SUITE_KEY,
+                                 tmpMB.toString());
+                break;
+                
+            case SC_A_SSL_SESSION  :
+                req.scheme().setString( "https" );
+                msg.getBytes(tmpMB);
+                req.setAttribute(SSLSupport.SESSION_ID_KEY, 
+                                  tmpMB.toString());
+                break;
+                
+            case SC_A_SECRET  :
+                msg.getBytes(tmpMB);
+                String secret=tmpMB.toString();
+                log.info("Secret: " + secret );
+                // endpoint note
+                ep.setNote( secretNote, secret );
+                break;
+                
+            case SC_A_STORED_METHOD:
+                msg.getBytes(req.method()); 
+                break;
+                
+            default:
+                break; // ignore, we don't know about it - backward compat
+            }
+        }
+        return 200;
+    }
+    
+    private void decodeHeaders( MsgContext ep, Msg msg, Request req,
+                                MessageBytes tmpMB ) {
+        // Decode headers
+        MimeHeaders headers = req.getMimeHeaders();
+
+        int hCount = msg.getInt();
+        for(int i = 0 ; i < hCount ; i++) {
+            String hName = null;
+
+            // Header names are encoded as either an integer code starting
+            // with 0xA0, or as a normal string (in which case the first
+            // two bytes are the length).
+            int isc = msg.peekInt();
+            int hId = isc & 0xFF;
+
+            MessageBytes vMB=null;
+            isc &= 0xFF00;
+            if(0xA000 == isc) {
+                msg.getInt(); // To advance the read position
+                hName = headerTransArray[hId - 1];
+                vMB=headers.addValue( hName );
+            } else {
+                // reset hId -- if the header currently being read
+                // happens to be 7 or 8 bytes long, the code below
+                // will think it's the content-type header or the
+                // content-length header - SC_REQ_CONTENT_TYPE=7,
+                // SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
+                // behaviour.  see bug 5861 for more information.
+                hId = -1;
+                msg.getBytes( tmpMB );
+                ByteChunk bc=tmpMB.getByteChunk();
+                //hName=tmpMB.toString();
+                //                vMB=headers.addValue( hName );
+                vMB=headers.addValue( bc.getBuffer(),
+                                      bc.getStart(), bc.getLength() );
+            }
+
+            msg.getBytes(vMB);
+
+            if (hId == SC_REQ_CONTENT_LENGTH ||
+                tmpMB.equalsIgnoreCase("Content-Length")) {
+                // just read the content-length header, so set it
+                int contentLength = (vMB == null) ? -1 : vMB.getInt();
+                req.setContentLength(contentLength);
+            } else if (hId == SC_REQ_CONTENT_TYPE ||
+                       tmpMB.equalsIgnoreCase("Content-Type")) {
+                // just read the content-type header, so set it
+                ByteChunk bchunk = vMB.getByteChunk();
+                req.contentType().setBytes(bchunk.getBytes(),
+                                           bchunk.getOffset(),
+                                           bchunk.getLength());
+            }
+        }
+    }
+
+    /**
+     * Parse host.
+     */
+    private void parseHost(MessageBytes valueMB, Request request) 
+        throws IOException {
+
+        if (valueMB == null || valueMB.isNull()) {
+            // HTTP/1.0
+            // Default is what the socket tells us. Overriden if a host is 
+            // found/parsed
+            request.setServerPort(request.getLocalPort());
+            request.serverName().duplicate(request.localName());
+            return;
+        }
+
+        ByteChunk valueBC = valueMB.getByteChunk();
+        byte[] valueB = valueBC.getBytes();
+        int valueL = valueBC.getLength();
+        int valueS = valueBC.getStart();
+        int colonPos = -1;
+        CharChunk hostNameC = (CharChunk)request.getNote(HOSTBUFFER);
+        if(hostNameC == null) {
+            hostNameC = new CharChunk(valueL);
+            request.setNote(HOSTBUFFER, hostNameC);
+        }
+        hostNameC.recycle();
+
+        boolean ipv6 = (valueB[valueS] == '[');
+        boolean bracketClosed = false;
+        for (int i = 0; i < valueL; i++) {
+            char b = (char) valueB[i + valueS];
+            hostNameC.append(b);
+            if (b == ']') {
+                bracketClosed = true;
+            } else if (b == ':') {
+                if (!ipv6 || bracketClosed) {
+                    colonPos = i;
+                    break;
+                }
+            }
+        }
+
+        if (colonPos < 0) {
+            if (request.scheme().equalsIgnoreCase("https")) {
+                // 80 - Default HTTTP port
+                request.setServerPort(443);
+            } else {
+                // 443 - Default HTTPS port
+                request.setServerPort(80);
+            }
+            request.serverName().setChars(hostNameC.getChars(), 
+                                          hostNameC.getStart(), 
+                                          hostNameC.getLength());
+        } else {
+
+            request.serverName().setChars(hostNameC.getChars(), 
+                                          hostNameC.getStart(), colonPos);
+
+            int port = 0;
+            int mult = 1;
+            for (int i = valueL - 1; i > colonPos; i--) {
+                int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
+                if (charValue == -1) {
+                    // Invalid character
+                    throw new CharConversionException("Invalid char in port: " + valueB[i + valueS]); 
+                }
+                port = port + (charValue * mult);
+                mult = 10 * mult;
+            }
+            request.setServerPort(port);
+
+        }
+
+    }
+
+}
diff --git a/connectors/jk/java/org/apache/jk/common/JkInputStream.java b/connectors/jk/java/org/apache/jk/common/JkInputStream.java
new file mode 100644
index 0000000..8bdbaf7
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/JkInputStream.java
@@ -0,0 +1,327 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.common;
+
+import java.io.IOException;
+import java.io.InputStream;
+import org.apache.jk.core.JkHandler;
+import org.apache.jk.core.Msg;
+import org.apache.jk.core.MsgContext;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+
+/** Generic input stream impl on top of ajp
+ */
+public class JkInputStream extends InputStream {
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( JkInputStream.class );
+
+    public JkInputStream() {
+    }
+
+    public int available() throws IOException {
+        if( log.isDebugEnabled() )
+            log.debug( "available(): "  + blen + " " + pos );
+        return blen-pos;
+    }
+
+    public void close() throws IOException {
+        if( log.isDebugEnabled() )
+            log.debug( "cloae() " );
+        this.closed=true;
+    }
+
+    public void mark(int readLimit) {
+    }
+
+    public boolean markSupported() {
+        return false;
+    }
+
+    public void reset() throws IOException {
+        throw new IOException("reset() not supported");
+    }
+
+    public int read() throws IOException {
+        if( contentLength == -1 ) {
+            return doRead1();
+	}
+	if( available <= 0 ) {
+            if( log.isDebugEnabled() )
+                log.debug("doRead() nothing available" );
+            return -1;
+        }
+	available--;
+
+        return doRead1();
+    }
+    
+    public int read(byte[] b) throws IOException {
+        int rd=read( b, 0, b.length);
+        if( log.isDebugEnabled() )
+            log.debug("read(" + b + ")=" + rd + " / " + b.length);
+        return rd;
+    }
+    
+    public int read(byte[] b, int off, int len) throws IOException {
+      	int rd=-1;
+	if( contentLength == -1 ) {
+	    rd=doRead1(b,off,len);
+	    return rd;
+	}
+	if( available <= 0 ) {
+            if( log.isDebugEnabled() ) log.debug("doRead() nothing available" );
+	    return -1;
+        }
+        
+	rd=doRead1( b,off, len );
+	available -= rd;
+	if( log.isDebugEnabled() )
+            log.debug("Read: " + new String( b,off, len ));
+	return rd;
+    }
+
+    public long skip(long n) throws IOException {
+        if (n > Integer.MAX_VALUE) {
+            throw new IOException("can't skip than many:  " + n);
+        }
+        // XXX if n is big, split this in multiple reads
+        byte[] b = new byte[(int)n];
+        return read(b, 0, b.length);
+    }
+
+
+    // -------------------- Jk specific methods --------------------
+
+    Msg bodyMsg=new MsgAjp();
+    MsgContext mc;
+
+    // Total length of the body - maximum we can read
+    // If -1, we don't use any limit, and we don't count available
+    int contentLength;
+    // How much remains unread.
+    int available;
+
+    boolean closed=false;
+
+    // Ajp13 specific -  needs refactoring for the new model
+    public static final int MAX_PACKET_SIZE=8192;
+    public static final int H_SIZE=4;  // Size of basic packet header
+    public static final int  MAX_READ_SIZE = MAX_PACKET_SIZE - H_SIZE - 2;
+    public static final byte JK_AJP13_GET_BODY_CHUNK    = 6;
+
+    
+    // Holds incoming chunks of request body data
+    // XXX We do a copy that could be avoided !
+    byte []bodyBuff = new byte[9000];
+    int blen;  // Length of current chunk of body data in buffer
+    int pos;   // Current read position within that buffer
+
+    boolean end_of_stream=false; // true if we've received an empty packet
+    
+    private int doRead1() throws IOException {
+        if(pos >= blen) {
+            if( ! refillReadBuffer()) {
+		return -1;
+	    }
+        }
+        int i=bodyBuff[pos++] & 0xFF;
+        if( log.isDebugEnabled() ) log.debug("doRead1 " + (char)i );
+        return i;  // prevent sign extension of byte value
+    }
+
+    public int doRead1(byte[] b, int off, int len) throws IOException 
+    {
+	if(pos >= blen) {
+	    if( ! refillReadBuffer()) {
+		return -1;
+	    }
+	}
+
+	if(pos + len <= blen) { // Fear the off by one error
+	    // Sanity check b.length > off + len?
+	    System.arraycopy(bodyBuff, pos, b, off, len);
+	    if( log.isDebugEnabled() )
+		log.debug("doRead1: " + pos + " " + len + " " + blen);
+            if( log.isTraceEnabled() )
+                log.trace("Data: \n" + new String( b, off, len ));
+	    pos += len;
+	    return len;
+	}
+
+	// Not enough data (blen < pos + len) or chunked encoded
+	int toCopy = len;
+	while(toCopy > 0) {
+	    int bytesRemaining = blen - pos;
+	    if(bytesRemaining < 0) 
+		bytesRemaining = 0;
+	    int c = bytesRemaining < toCopy ? bytesRemaining : toCopy;
+
+	    System.arraycopy(bodyBuff, pos, b, off, c);
+	    if( log.isDebugEnabled() )
+		log.debug("doRead2: " + pos + " " + len + " " +
+                          blen + " " + c);
+            if( log.isTraceEnabled() )
+                log.trace("Data: \n" + new String( b, off, (len<blen-1)?len:blen-1 ));
+
+	    toCopy    -= c;
+
+	    off       += c;
+	    pos       += c; // In case we exactly consume the buffer
+
+	    if(toCopy > 0) 
+		if( ! refillReadBuffer()) { // Resets blen and pos
+		    break;
+		}
+	}
+
+	return len - toCopy;
+    }
+
+    /** Must be called after the request is parsed, before
+     *  any input
+     */
+    public void setContentLength( int i ) {
+        contentLength=i;
+        available=i;
+    }
+
+    /** Must be called when the stream is created
+     */
+    public void setMsgContext( MsgContext mc ) {
+        this.mc=mc;
+    }
+
+    /** Must be called before or after each request
+     */
+    public void recycle() {
+        available=0;
+        blen = 0;
+        pos = 0;
+        closed=false;
+        end_of_stream = false;
+        contentLength=-1;
+    }
+
+    /**
+     */
+    public int doRead(ByteChunk responseChunk ) throws IOException {
+        if( log.isDebugEnabled())
+            log.debug( "doRead " + pos + " " + blen + " " + available + " " + end_of_stream+
+                       " " + responseChunk.getOffset()+ " " + responseChunk.getLength());
+        if( end_of_stream ) {
+            return -1;
+        }
+        if( blen == pos ) {
+            if ( !refillReadBuffer() ){
+                return -1;
+            }
+        }
+        responseChunk.setBytes( bodyBuff, pos, blen );
+        pos=blen;
+        return blen;
+    }
+    
+    /** Receive a chunk of data. Called to implement the
+     *  'special' packet in ajp13 and to receive the data
+     *  after we send a GET_BODY packet
+     */
+    public boolean receive() throws IOException
+    {
+        mc.setType( JkHandler.HANDLE_RECEIVE_PACKET );
+        bodyMsg.reset();
+        int err = mc.getSource().receive(bodyMsg, mc);
+        if( log.isDebugEnabled() )
+            log.info( "Receiving: getting request body chunk " + err + " " + bodyMsg.getLen() );
+        
+        if(err < 0) {
+	    throw new IOException();
+	}
+
+        pos=0;
+        blen=0;
+
+        // No data received.
+	if( bodyMsg.getLen() == 0 ) { // just the header
+            // Don't mark 'end of stream' for the first chunk.
+            // end_of_stream = true;
+	    return false;
+	}
+    	blen = bodyMsg.peekInt();
+
+        if( blen == 0 ) {
+            return false;
+        }
+
+        if( blen > bodyBuff.length ) {
+            bodyMsg.dump("Body");
+        }
+        
+        if( log.isTraceEnabled() ) {
+            bodyMsg.dump("Body buffer");
+        }
+        
+    	int cpl=bodyMsg.getBytes(bodyBuff);
+
+        if( log.isDebugEnabled() )
+            log.debug( "Copy into body buffer2 " + bodyBuff + " " + cpl + " " + blen );
+
+        if( log.isTraceEnabled() )
+            log.trace( "Data:\n" + new String( bodyBuff, 0, cpl ));
+
+	return (blen > 0);
+    }
+    
+    /**
+     * Get more request body data from the web server and store it in the 
+     * internal buffer.
+     *
+     * @return true if there is more data, false if not.    
+     */
+    private boolean refillReadBuffer() throws IOException 
+    {
+	// If the server returns an empty packet, assume that that end of
+	// the stream has been reached (yuck -- fix protocol??).
+        if (end_of_stream) {
+            if( log.isDebugEnabled() ) log.debug("refillReadBuffer: end of stream " );
+            return false;
+        }
+
+	// Why not use outBuf??
+	bodyMsg.reset();
+	bodyMsg.appendByte(JK_AJP13_GET_BODY_CHUNK);
+	bodyMsg.appendInt(MAX_READ_SIZE);
+        
+	if( log.isDebugEnabled() )
+            log.debug("refillReadBuffer " + Thread.currentThread());
+
+        mc.setType( JkHandler.HANDLE_SEND_PACKET );
+	mc.getSource().send(bodyMsg, mc);
+
+        // In JNI mode, response will be in bodyMsg. In TCP mode, response need to be
+        // read
+
+        //bodyMsg.dump("refillReadBuffer ");
+        
+        boolean moreData=receive();
+        if( !moreData ) {
+            end_of_stream=true;
+        }
+        return moreData;
+    }
+
+}
diff --git a/connectors/jk/java/org/apache/jk/common/JkMX.java b/connectors/jk/java/org/apache/jk/common/JkMX.java
new file mode 100644
index 0000000..f0ec896
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/JkMX.java
@@ -0,0 +1,377 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.common;
+
+
+import org.apache.jk.core.JkHandler;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.Attribute;
+import javax.management.MBeanServerFactory;
+import java.io.IOException;
+
+/**
+ * Load the HTTP or RMI adapters for MX4J and JMXRI.
+ *
+ * Add "mx.enabled=true" in jk2.properties to enable it.
+ * You could also select http and/or jrmp protocol, 
+ * with mx.httpPort, mx.httpHost, mxjrmpPort and mx.jrmpPort.
+ * <p />
+ * If you run into an error message like
+ * "SystemId Unknown; Line #12; Column #81; Cannot add attribute name after
+ * child nodes or before an element is produced.  Attribute will be ignored."
+ * after setting mx.enabled to true, you probably need a newer version
+ * of Xalan.  See the RELEASE-NOTES document section on XML Parsers for
+ * more information.
+ *
+ */
+public class JkMX extends JkHandler
+{
+    MBeanServer mserver;
+    private boolean enabled=false;
+    private int httpport=-1;
+    private String httphost="localhost";
+    private String authmode="none";
+    private String authuser=null;
+    private String authpassword=null;
+    private int jrmpport=-1;
+    private String jrmphost="localhost";
+    private boolean useXSLTProcessor = true;
+
+    public JkMX() {
+    }
+
+    /* -------------------- Public methods -------------------- */
+
+    /** Enable the MX4J adapters (new way)
+     */
+    public void setEnabled(boolean b) {
+        enabled=b;
+    }
+        
+    public boolean getEnabled() {
+        return enabled;
+    }
+        
+    /** Enable the MX4J adapters (old way, compatible)
+     */
+    public void setPort(int i) {
+        enabled=(i != -1);
+    }
+        
+    public int getPort() {
+        return ((httpport != -1) ? httpport : jrmpport);
+    }
+
+    /** Enable the MX4J HTTP internal adapter
+     */ 
+    public void setHttpPort( int i ) {
+        httpport=i;
+    }
+
+    public int getHttpPort() {
+        return httpport;
+    }
+
+    public void setHttpHost(String host ) {
+        this.httphost=host;
+    }
+
+    public String getHttpHost() {
+        return httphost;
+    }
+
+    public void setAuthMode(String mode) {
+        authmode=mode;
+    }
+
+    public String getAuthMode() {
+        return authmode;
+    }
+
+    public void setAuthUser(String user) {
+        authuser=user;
+    }
+
+    public String getAuthUser() {
+        return authuser;
+    }
+
+    public void setAuthPassword(String password) {
+        authpassword=password;
+    }
+
+    public String getAuthPassword() {
+        return authpassword;
+    }
+
+    /** Enable the MX4J JRMP internal adapter
+     */
+    public void setJrmpPort( int i ) {
+        jrmpport=i;
+    }
+
+    public int getJrmpPort() {
+        return jrmpport;
+    }
+
+    public void setJrmpHost(String host ) {
+        this.jrmphost=host;
+    }
+
+    public String getJrmpHost() {
+        return jrmphost;
+    }
+
+    public boolean getUseXSLTProcessor() {
+        return useXSLTProcessor;
+    }
+
+    public void setUseXSLTProcessor(boolean uxsltp) {
+        useXSLTProcessor = uxsltp;
+    }        
+
+    /* ==================== Start/stop ==================== */
+    ObjectName httpServerName=null;
+    ObjectName jrmpServerName=null;
+
+    /** Initialize the worker. After this call the worker will be
+     *  ready to accept new requests.
+     */
+    public void loadAdapter() throws IOException {
+        boolean httpAdapterLoaded = false;
+        boolean jrmpAdapterLoaded = false;
+        
+        if ((httpport != -1) && classExists("mx4j.adaptor.http.HttpAdaptor")) {
+            try {
+                httpServerName = registerObject("mx4j.adaptor.http.HttpAdaptor",
+                                                "Http:name=HttpAdaptor");
+
+                        
+                if( httphost!=null )
+                    mserver.setAttribute(httpServerName, new Attribute("Host", httphost));
+                mserver.setAttribute(httpServerName, new Attribute("Port", new Integer(httpport)));
+
+                if( "none".equals(authmode) || "basic".equals(authmode) || "digest".equals(authmode) )
+                    mserver.setAttribute(httpServerName, new Attribute("AuthenticationMethod", authmode));
+
+                if( authuser!=null && authpassword!=null )
+                    mserver.invoke(httpServerName, "addAuthorization",
+                        new Object[] {
+                            authuser,
+                            authpassword},
+                        new String[] { "java.lang.String", "java.lang.String" });
+
+                if(useXSLTProcessor) {
+                    ObjectName processorName = registerObject("mx4j.adaptor.http.XSLTProcessor",
+                                                          "Http:name=XSLTProcessor");
+                    mserver.setAttribute(httpServerName, new Attribute("ProcessorName", processorName));
+                }
+
+                // starts the server
+                mserver.invoke(httpServerName, "start", null, null);
+
+                log.info( "Started MX4J console on host " + httphost + " at port " + httpport);
+                
+                httpAdapterLoaded = true;
+
+            } catch( Throwable t ) {
+                httpServerName=null;
+                log.error( "Can't load the MX4J http adapter ", t );
+            }
+        }
+
+        if ((httpport != -1) && (!httpAdapterLoaded) && classExists("mx4j.tools.adaptor.http.HttpAdaptor")) {
+            try {
+                httpServerName = registerObject("mx4j.tools.adaptor.http.HttpAdaptor",
+                                                "Http:name=HttpAdaptor");
+
+                        
+                if( httphost!=null )
+                    mserver.setAttribute(httpServerName, new Attribute("Host", httphost));
+                mserver.setAttribute(httpServerName, new Attribute("Port", new Integer(httpport)));
+
+                if( "none".equals(authmode) || "basic".equals(authmode) || "digest".equals(authmode) )
+                    mserver.setAttribute(httpServerName, new Attribute("AuthenticationMethod", authmode));
+
+                if( authuser!=null && authpassword!=null )
+                    mserver.invoke(httpServerName, "addAuthorization",
+                        new Object[] {
+                            authuser,
+                            authpassword},
+                        new String[] { "java.lang.String", "java.lang.String" });
+
+               if(useXSLTProcessor) {
+                    ObjectName processorName = registerObject("mx4j.tools.adaptor.http.XSLTProcessor",
+                                                          "Http:name=XSLTProcessor");
+                    mserver.setAttribute(httpServerName, new Attribute("ProcessorName", processorName));
+		}
+                // starts the server
+                mserver.invoke(httpServerName, "start", null, null);
+
+                log.info( "Started MX4J console on host " + httphost + " at port " + httpport);
+                
+                httpAdapterLoaded = true;
+
+            } catch( Throwable t ) {
+                httpServerName=null;
+                log.error( "Can't load the MX4J http adapter ", t );
+            }
+        }
+
+        if ((jrmpport != -1) && classExists("mx4j.tools.naming.NamingService")) {
+            try {
+                jrmpServerName = registerObject("mx4j.tools.naming.NamingService",
+                                                "Naming:name=rmiregistry");
+				mserver.setAttribute(jrmpServerName, new Attribute("Port", 
+				                                     new Integer(jrmpport)));
+                mserver.invoke(jrmpServerName, "start", null, null);
+                log.info( "Creating " + jrmpServerName );
+
+                // Create the JRMP adaptor
+                ObjectName adaptor = registerObject("mx4j.adaptor.rmi.jrmp.JRMPAdaptor",
+                                                    "Adaptor:protocol=jrmp");
+
+
+                mserver.setAttribute(adaptor, new Attribute("JNDIName", "jrmp"));
+
+                mserver.invoke( adaptor, "putNamingProperty",
+                        new Object[] {
+                            javax.naming.Context.INITIAL_CONTEXT_FACTORY,
+                            "com.sun.jndi.rmi.registry.RegistryContextFactory"},
+                        new String[] { "java.lang.Object", "java.lang.Object" });
+
+                String jrpmurl = "rmi://" + jrmphost + ":" + Integer.toString(jrmpport) ;
+                                        
+                mserver.invoke( adaptor, "putNamingProperty",
+                        new Object[] {
+                            javax.naming.Context.PROVIDER_URL,
+                            jrpmurl},
+                        new String[] { "java.lang.Object", "java.lang.Object" });
+
+                // Registers the JRMP adaptor in JNDI and starts it
+                mserver.invoke(adaptor, "start", null, null);
+                log.info( "Creating " + adaptor + " on host " + jrmphost + " at port " + jrmpport);
+
+                jrmpAdapterLoaded = true;
+
+            } catch( Exception ex ) {
+                jrmpServerName = null;
+                log.error( "MX4j RMI adapter not loaded: " + ex.toString());
+            }
+        }
+
+        if ((httpport != -1) && (! httpAdapterLoaded) && classExists("com.sun.jdmk.comm.HtmlAdaptorServer")) {
+            try {
+                httpServerName=registerObject("com.sun.jdmk.comm.HtmlAdaptorServer",
+                                              "Adaptor:name=html,port=" + httpport);
+                log.info("Registering the JMX_RI html adapter " + httpServerName + " at port " + httpport);
+
+                mserver.setAttribute(httpServerName,
+                                     new Attribute("Port", new Integer(httpport)));
+
+                mserver.invoke(httpServerName, "start", null, null);
+
+                httpAdapterLoaded = true;
+            } catch( Throwable t ) {
+                httpServerName = null;
+                log.error( "Can't load the JMX_RI http adapter " + t.toString()  );
+            }
+        }
+
+        if ((!httpAdapterLoaded) && (!jrmpAdapterLoaded))
+            log.warn( "No adaptors were loaded but mx.enabled was defined.");
+
+    }
+
+    public void destroy() {
+        try {
+            log.info("Stoping JMX ");
+
+            if( httpServerName!=null ) {
+                mserver.invoke(httpServerName, "stop", null, null);
+            }
+            if( jrmpServerName!=null ) {
+                mserver.invoke(jrmpServerName, "stop", null, null);
+            }
+        } catch( Throwable t ) {
+            log.error( "Destroy error" + t );
+        }
+    }
+
+    public void init() throws IOException {
+        try {
+            mserver = getMBeanServer();
+
+            if( enabled ) {
+                loadAdapter();
+            }
+
+            try {
+                registerObject("org.apache.log4j.jmx.HierarchyDynamicMBean" ,
+                               "log4j:hierarchy=default");
+                log.info("Registering the JMX hierarchy for Log4J ");
+            } catch( Throwable t ) {
+                log.info("Can't enable log4j mx: " +  t.toString());
+            }
+
+        } catch( Throwable t ) {
+            log.error( "Init error", t );
+        }
+    }
+
+    public void addHandlerCallback( JkHandler w ) {
+    }
+
+    MBeanServer getMBeanServer() {
+        MBeanServer server;
+        if( MBeanServerFactory.findMBeanServer(null).size() > 0 ) {
+            server=(MBeanServer)MBeanServerFactory.findMBeanServer(null).get(0);
+        } else {
+            server=MBeanServerFactory.createMBeanServer();
+        }
+        return (server);
+    }
+
+
+    private static boolean classExists(String className) {
+        try {
+            Thread.currentThread().getContextClassLoader().loadClass(className);
+            return true;
+        } catch(Throwable e) {
+            if (log.isInfoEnabled())
+                log.info( "className [" + className + "] does not exist");
+            return false;
+        }
+    }
+
+    private ObjectName registerObject(String className, String oName) 
+        throws Exception {
+        Class c = Class.forName(className);
+        Object o = c.newInstance();
+        ObjectName objN = new ObjectName(oName);
+        mserver.registerMBean(o, objN);
+        return objN;
+    }
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( JkMX.class );
+
+
+}
+
diff --git a/connectors/jk/java/org/apache/jk/common/JniHandler.java b/connectors/jk/java/org/apache/jk/common/JniHandler.java
new file mode 100644
index 0000000..f7be9d3
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/JniHandler.java
@@ -0,0 +1,318 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.common;
+
+import java.io.IOException;
+
+import javax.management.ObjectName;
+
+import org.apache.commons.modeler.Registry;
+import org.apache.jk.apr.AprImpl;
+import org.apache.jk.core.JkHandler;
+import org.apache.jk.core.Msg;
+import org.apache.jk.core.MsgContext;
+import org.apache.jk.core.JkChannel;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.C2BConverter;
+import org.apache.tomcat.util.buf.MessageBytes;
+
+
+/**
+ * Base class for components using native code ( libjkjni.so ).
+ * It allows to access the jk_env and wrap ( 'box' ? ) a native
+ * jk component, and call it's methods.
+ *
+ * Note that get/setAttribute are expensive ( Strings, etc ),
+ * invoke() is were all optimizations are done. We do recycle
+ * all memory on both C and java sides ( the only exception is
+ * when we attempt pinning but the VM doesn't support it ). The
+ * low level optimizations from ByteBuffer, etc are used to
+ * reduce the overhead of passing strings.
+ *
+ * @author Costin Manolache
+ */
+public class JniHandler extends JkHandler {
+    protected AprImpl apr;
+
+    // The native side handler
+    protected long nativeJkHandlerP;
+
+    protected String jkHome;
+
+    // Dispatch table codes. Hardcoded for now, will change when we have more handlers.
+    public static final int JK_HANDLE_JNI_DISPATCH=0x15;
+    public static final int JK_HANDLE_SHM_DISPATCH=0x16;
+
+
+    public static final int MSG_NOTE=0;
+    public static final int C2B_NOTE=1;
+    public static final int MB_NOTE=2;
+    private boolean paused = false;
+
+
+    public JniHandler() {
+    }
+
+    /**
+     */
+    public void setJkHome( String s ) {
+        jkHome=s;
+    }
+
+    public String getJkHome() {
+        return jkHome;
+    }
+
+    /** You must call initNative() inside the component init()
+     */
+    public void init() throws IOException {
+        // static field init, temp
+    }
+
+    protected void initNative(String nativeComponentName) {
+        apr=(AprImpl)wEnv.getHandler("apr");
+        if( apr==null ) {
+            // In most cases we can just load it automatically.
+            // that requires all libs to be installed in standard places
+            // ( LD_LIBRARY_PATH, /usr/lib
+            try {
+                apr=new AprImpl();
+                wEnv.addHandler("apr", apr);
+                apr.init();
+                if( oname != null ) {
+                    ObjectName aprname=new ObjectName(oname.getDomain() +
+                            ":type=JkHandler, name=apr");
+                    Registry.getRegistry().registerComponent(apr, aprname, null);
+                }
+            } catch( Throwable t ) {
+                log.debug("Can't load apr", t);
+                apr=null;
+            }
+        }
+        if( apr==null || ! apr.isLoaded() ) {
+            if( log.isDebugEnabled() )
+                log.debug("No apr, disabling jni proxy ");
+            apr=null;
+            return;
+        }
+
+        try {
+            long xEnv=apr.getJkEnv();
+            nativeJkHandlerP=apr.getJkHandler(xEnv, nativeComponentName );
+            
+            if( nativeJkHandlerP==0 ) {
+                log.debug("Component not found, creating it " + nativeComponentName );
+                nativeJkHandlerP=apr.createJkHandler(xEnv, nativeComponentName);
+            }
+            log.debug("Native proxy " + nativeJkHandlerP );
+            apr.releaseJkEnv(xEnv);
+        } catch( Throwable t ) {
+            apr=null;
+            log.info("Error calling apr ", t);
+        }
+   }
+
+    public void appendString( Msg msg, String s, C2BConverter charsetDecoder)
+        throws IOException
+    {
+        ByteChunk bc=charsetDecoder.getByteChunk();
+        charsetDecoder.recycle();
+        charsetDecoder.convert( s );
+        charsetDecoder.flushBuffer();
+        msg.appendByteChunk( bc );
+    }
+
+    public void pause() throws Exception {
+        synchronized(this) {
+            paused = true;
+        }
+    }
+
+    public void resume() throws Exception {
+        synchronized(this) {
+            paused = false;
+            notifyAll();
+        }
+    }
+
+
+    /** Create a msg context to be used with the shm channel
+     */
+    public MsgContext createMsgContext() {
+        if( nativeJkHandlerP==0 || apr==null  )
+            return null;
+
+        synchronized(this) {
+            try{ 
+                while(paused) {
+                    wait();
+                }
+            }catch(InterruptedException ie) {
+                // Ignore, since it can't happen
+            }
+        }
+
+        try {
+            MsgContext msgCtx=new MsgContext();
+            MsgAjp msg=new MsgAjp();
+
+            msgCtx.setSource( (JkChannel)this );
+            msgCtx.setWorkerEnv( wEnv );
+
+            msgCtx.setNext( this );
+
+            msgCtx.setMsg( MSG_NOTE, msg); // XXX Use noteId
+
+            C2BConverter c2b=new C2BConverter(  "iso-8859-1" );
+            msgCtx.setNote( C2B_NOTE, c2b );
+
+            MessageBytes tmpMB=new MessageBytes();
+            msgCtx.setNote( MB_NOTE, tmpMB );
+            return msgCtx;
+        } catch( Exception ex ) {
+            log.error("Can't create endpoint", ex);
+            return null;
+        }
+    }
+
+    public void setNativeAttribute(String name, String val) throws IOException {
+        if( apr==null ) return;
+
+        if( nativeJkHandlerP == 0 ) {
+            log.error( "Unitialized component " + name+ " " + val );
+            return;
+        }
+
+        long xEnv=apr.getJkEnv();
+
+        apr.jkSetAttribute( xEnv, nativeJkHandlerP, name, val );
+
+        apr.releaseJkEnv( xEnv );
+    }
+
+    public void initJkComponent() throws IOException {
+        if( apr==null ) return;
+
+        if( nativeJkHandlerP == 0 ) {
+            log.error( "Unitialized component " );
+            return;
+        }
+
+        long xEnv=apr.getJkEnv();
+
+        apr.jkInit( xEnv, nativeJkHandlerP );
+
+        apr.releaseJkEnv( xEnv );
+    }
+
+    public void destroyJkComponent() throws IOException {
+        if( apr==null ) return;
+
+        if( nativeJkHandlerP == 0 ) {
+            log.error( "Unitialized component " );
+            return;
+        }
+
+        long xEnv=apr.getJkEnv();
+
+        apr.jkDestroy( xEnv, nativeJkHandlerP );
+
+        apr.releaseJkEnv( xEnv );
+    }
+
+
+
+    protected void setNativeEndpoint(MsgContext msgCtx) {
+        long xEnv=apr.getJkEnv();
+        msgCtx.setJniEnv( xEnv );
+
+        long epP=apr.createJkHandler(xEnv, "endpoint");
+        log.debug("create ep " + epP );
+        if( epP == 0 ) return;
+        apr.jkInit( xEnv, epP );
+        msgCtx.setJniContext( epP );
+
+    }
+
+    protected void recycleNative(MsgContext ep) {
+        apr.jkRecycle(ep.getJniEnv(), ep.getJniContext());
+    }
+
+    /** send and get the response in the same buffer. This calls the
+    * method on the wrapped jk_bean object.
+     */
+    protected int nativeDispatch( Msg msg, MsgContext ep, int code, int raw )
+        throws IOException
+    {
+        if( log.isDebugEnabled() )
+            log.debug( "Sending packet " + code + " " + raw);
+
+        if( raw == 0 ) {
+            msg.end();
+
+            if( log.isTraceEnabled() ) msg.dump("OUT:" );
+        }
+
+        // Create ( or reuse ) the jk_endpoint ( the native pair of
+        // MsgContext )
+        long xEnv=ep.getJniEnv();
+        long nativeContext=ep.getJniContext();
+        if( nativeContext==0 || xEnv==0 ) {
+            setNativeEndpoint( ep );
+            xEnv=ep.getJniEnv();
+            nativeContext=ep.getJniContext();
+        }
+
+        if( xEnv==0 || nativeContext==0 || nativeJkHandlerP==0 ) {
+            log.error("invokeNative: Null pointer ");
+            return -1;
+        }
+
+        // Will process the message in the current thread.
+        // No wait needed to receive the response, if any
+        int status=apr.jkInvoke( xEnv,
+                                 nativeJkHandlerP,
+                                 nativeContext,
+                                 code, msg.getBuffer(), 0, msg.getLen(), raw );
+
+        if( status != 0 && status != 2 ) {
+            log.error( "nativeDispatch: error " + status, new Throwable() );
+        }
+
+        if( log.isDebugEnabled() ) log.debug( "Sending packet - done " + status);
+        return status;
+    }
+
+    /** Base implementation for invoke. Dispatch the action to the native
+    * code, where invoke() is called on the wrapped jk_bean.
+    */
+    public  int invoke(Msg msg, MsgContext ep )
+        throws IOException
+    {
+        long xEnv=ep.getJniEnv();
+        int type=ep.getType();
+
+        int status=nativeDispatch(msg, ep, type, 0 );
+
+        apr.jkRecycle(xEnv, ep.getJniContext());
+        apr.releaseJkEnv( xEnv );
+        return status;
+    }
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( JniHandler.class );
+}
diff --git a/connectors/jk/java/org/apache/jk/common/ModJkMX.java b/connectors/jk/java/org/apache/jk/common/ModJkMX.java
new file mode 100644
index 0000000..59cc8d9
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/ModJkMX.java
@@ -0,0 +1,451 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.common;
+
+import java.io.IOException;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.URLConnection;
+import java.net.URL;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import javax.management.MBeanServer;
+import javax.management.AttributeNotFoundException;
+import javax.management.MBeanException;
+import javax.management.ReflectionException;
+import javax.management.Attribute;
+import javax.management.ObjectName;
+
+import org.apache.jk.core.JkHandler;
+import org.apache.commons.modeler.Registry;
+import org.apache.commons.modeler.BaseModelMBean;
+import org.apache.commons.modeler.ManagedBean;
+import org.apache.commons.modeler.AttributeInfo;
+import org.apache.commons.modeler.OperationInfo;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A small mbean that will act as a proxy for mod_jk2.
+ *
+ * For efficiency, it'll get bulk results and cache them - you
+ * can force an update by calling the refreshAttributes and refreshMetadata
+ * operations on this mbean.
+ *
+ * TODO: implement the user/pass auth ( right now you must use IP based security )
+ * TODO: eventually support https
+ * TODO: support for metadata ( mbean-descriptors ) for description and type conversions
+ * TODO: filter out trivial components ( mutexes, etc )
+ *
+ * @author Costin Manolache
+ */
+public class ModJkMX extends JkHandler
+{
+    private static Log log = LogFactory.getLog(ModJkMX.class);
+
+    MBeanServer mserver;
+    String webServerHost="localhost";
+    int webServerPort=80;
+    String statusPath="/jkstatus";
+    String user;
+    String pass;
+    Registry reg;
+
+    HashMap mbeans=new HashMap();
+    long lastRefresh=0;
+    long updateInterval=5000; // 5 sec - it's min time between updates
+
+    public ModJkMX()
+    {
+    }
+
+    /* -------------------- Public methods -------------------- */
+
+    public String getWebServerHost() {
+        return webServerHost;
+    }
+
+    public void setWebServerHost(String webServerHost) {
+        this.webServerHost = webServerHost;
+    }
+
+    public int getWebServerPort() {
+        return webServerPort;
+    }
+
+    public void setWebServerPort(int webServerPort) {
+        this.webServerPort = webServerPort;
+    }
+
+    public long getUpdateInterval() {
+        return updateInterval;
+    }
+
+    public void setUpdateInterval(long updateInterval) {
+        this.updateInterval = updateInterval;
+    }
+
+    public String getUser() {
+        return user;
+    }
+
+    public void setUser(String user) {
+        this.user = user;
+    }
+
+    public String getPass() {
+        return pass;
+    }
+
+    public void setPass(String pass) {
+        this.pass = pass;
+    }
+
+    public String getStatusPath() {
+        return statusPath;
+    }
+
+    public void setStatusPath(String statusPath) {
+        this.statusPath = statusPath;
+    }
+    /* ==================== Start/stop ==================== */
+
+    public void destroy() {
+        try {
+            // We should keep track of loaded beans and call stop.
+            // Modeler should do it...
+            Iterator mbeansIt=mbeans.values().iterator();
+            MBeanServer mbserver = Registry.getRegistry().getMBeanServer();
+            while( mbeansIt.hasNext()) {
+                MBeanProxy proxy=(MBeanProxy)mbeansIt.next();
+                Object ooname = proxy.getObjectName();
+                if( ooname != null ) {
+                    ObjectName oname = null;
+                    if(ooname instanceof ObjectName) {
+                        oname = (ObjectName)ooname;
+                    } else if(ooname instanceof String) {
+                        oname = new ObjectName((String)ooname);
+                    }
+                    if( oname != null ) {
+                        mbserver.unregisterMBean(oname);
+                    }
+                }
+            }
+        } catch( Throwable t ) {
+            log.error( "Destroy error", t );
+        }
+    }
+
+    public void init() throws IOException {
+        try {
+            //if( log.isDebugEnabled() )
+            log.info("init " + webServerHost + " " + webServerPort);
+            reg=Registry.getRegistry();
+            refreshMetadata();
+            refreshAttributes();
+        } catch( Throwable t ) {
+            log.error( "Init error", t );
+        }
+    }
+
+    public void start() throws IOException {
+        if( reg==null)
+            init();
+    }
+
+    /** Refresh the proxies, if updateInterval passed
+     *
+     */
+    public void refresh()  {
+        long time=System.currentTimeMillis();
+        if( time - lastRefresh < updateInterval ) {
+            return;
+        }
+        lastRefresh=time;
+        refreshMetadata();
+        refreshAttributes();
+    }
+
+    public void refreshAttributes()  {
+        try {
+            int cnt=0;
+            // connect to apache, get a list of mbeans
+            BufferedReader is=getStream( "dmp=*");
+            if( is==null ) return;
+
+            String name=null;
+            String att=null;
+            String val=null;
+            while(true) {
+                String line=is.readLine();
+                if( line==null ) break;
+                line=line.trim();
+                if( "".equals(line) || line.startsWith("#") ) continue;
+
+                // for each mbean, create a proxy
+                if(log.isDebugEnabled())
+                    log.debug("Read " + line);
+
+                if(line.startsWith( "[")) {
+                    name=line.substring(1);
+                    if( name.endsWith("]")) {
+                        name=name.substring(0, name.length()-1);
+                    }
+                }
+                // Name/value pair
+                int idx=line.indexOf('=');
+                if( idx < 0 ) continue;
+                att=line.substring(0, idx );
+                val=line.substring(idx+1);
+
+                if( log.isDebugEnabled())
+                    log.debug("name: " + name + " att=" + att +
+                            " val=" + val);
+
+                MBeanProxy proxy=(MBeanProxy)mbeans.get(name);
+                if( proxy==null ) {
+                    log.info( "Unknown object " + name);
+                } else {
+                    proxy.update(att, val);
+                    cnt++;
+                }
+            }
+            log.info( "Refreshing attributes " + cnt);
+        } catch( Exception ex ) {
+            log.info("Error ", ex);
+        }
+    }
+
+    /** connect to apache, get a list of mbeans
+      */
+    BufferedReader getStream(String qry) throws Exception {
+        try {
+            String path=statusPath + "?" + qry;
+            URL url=new URL( "http", webServerHost, webServerPort, path);
+            URLConnection urlc=url.openConnection();
+            BufferedReader is=new BufferedReader(new InputStreamReader(urlc.getInputStream()));
+            return is;
+        } catch (IOException e) {
+            log.info( "Can't connect to jkstatus " + webServerHost + ":" + webServerPort
+            + " " + e.toString());
+            return null;
+        }
+    }
+
+    public void refreshMetadata() {
+        try {
+            int cnt=0;
+            int newCnt=0;
+            BufferedReader is=getStream("lst=*");
+            if( is==null ) return;
+            String name=null;
+            String type=null;
+            ArrayList getters=new ArrayList();
+            ArrayList setters=new ArrayList();
+            ArrayList methods=new ArrayList();
+            while(true) {
+                String line=is.readLine();
+                if( log.isDebugEnabled())
+                    log.debug("Read " + line);
+
+                // end of section
+                if( line == null || line.startsWith("[") ) {
+                    if( name != null ) {
+                        cnt++;
+                        if( mbeans.get( name ) ==null ) {
+                            // New component
+                            newCnt++;
+                            MBeanProxy mproxy=new MBeanProxy(this);
+                            mproxy.init( name, getters, setters, methods);
+                            mbeans.put( name, mproxy );
+                        }
+                        if( log.isDebugEnabled())
+                            log.debug("mbean name: " + name + " type=" + type);
+
+                        getters.clear();
+                        setters.clear();
+                        methods.clear();
+                    }
+                }
+                // end of data
+                if( line==null ) break;
+
+                line=line.trim();
+                if( "".equals( line ) || line.startsWith("#"))  continue;
+
+                // for each mbean, create a proxy
+
+                if(line.startsWith( "[") && line.endsWith("]")) {
+                    name=line.substring(1, line.length()-1);
+                }
+                if(line.startsWith( "T=")) {
+                    type=line.substring(2);
+                }
+                if( line.startsWith("G=")) {
+                    getters.add(line.substring(2));
+                }
+                if( line.startsWith("S=")) {
+                    setters.add(line.substring(2));
+                }
+                if( line.startsWith("M=")) {
+                    methods.add(line.substring(2));
+                }
+            }
+            log.info( "Refreshing metadata " + cnt + " " +  newCnt);
+        } catch( Exception ex ) {
+            log.info("Error ", ex);
+        }
+    }
+
+    /** Use the same metadata, except that we replace the attribute
+     * get/set methods.
+     */
+    static class MBeanProxy extends BaseModelMBean {
+        private static Log log = LogFactory.getLog(MBeanProxy.class);
+
+        String jkName;
+        List getAttNames;
+        List setAttNames;
+        HashMap atts=new HashMap();
+        ModJkMX jkmx;
+
+        public MBeanProxy(ModJkMX jkmx) throws Exception {
+            this.jkmx=jkmx;
+        }
+
+        void init( String name, List getters, List setters, List methods )
+            throws Exception
+        {
+            if(log.isDebugEnabled())
+                log.debug("Register " + name );
+            int col=name.indexOf( ':' );
+            this.jkName=name;
+            String type=name.substring(0, col );
+            String id=name.substring(col+1);
+            id=id.replace('*','%');
+            id=id.replace(':', '%');
+            if( id.length() == 0 ) {
+                id="default";
+            }
+            ManagedBean mbean= new ManagedBean();
+
+            AttributeInfo ai=new AttributeInfo();
+            ai.setName( "jkName" );
+            ai.setType( "java.lang.String");
+            ai.setWriteable(false);
+            mbean.addAttribute(ai);
+
+            for( int i=0; i<getters.size(); i++ ) {
+                String att=(String)getters.get(i);
+                // Register metadata
+                ai=new AttributeInfo();
+                ai.setName( att );
+                ai.setType( "java.lang.String");
+                if( ! setters.contains(att))
+                    ai.setWriteable(false);
+                mbean.addAttribute(ai);
+            }
+            for( int i=0; i<setters.size(); i++ ) {
+                String att=(String)setters.get(i);
+                if( getters.contains(att))
+                    continue;
+                // Register metadata
+                ai=new AttributeInfo();
+                ai.setName( att );
+                ai.setType( "java.lang.String");
+                ai.setReadable(false);
+                mbean.addAttribute(ai);
+            }
+            for( int i=0; i<methods.size(); i++ ) {
+                String att=(String)methods.get(i);
+                // Register metadata
+                OperationInfo oi=new OperationInfo();
+                oi.setName( att );
+                oi.setReturnType("void");
+                mbean.addOperation(oi);
+            }
+
+            this.setModelMBeanInfo(mbean.createMBeanInfo());
+
+            MBeanServer mserver=Registry.getRegistry().getMBeanServer();
+            oname=new ObjectName("apache:type=" + type + ",id=" + id);
+            mserver.registerMBean(this, oname);
+        }
+
+        private void update( String name, String val ) {
+            log.debug( "Updating " + jkName + " " + name + " " + val);
+            atts.put( name, val);
+        }
+
+        public Object getAttribute(String name)
+            throws AttributeNotFoundException, MBeanException,
+                ReflectionException {
+            if( "jkName".equals( name )) {
+                return jkName;
+            }
+            jkmx.refresh();
+            return atts.get(name);
+        }
+
+        public void setAttribute(Attribute attribute)
+            throws AttributeNotFoundException, MBeanException,
+            ReflectionException
+        {
+            try {
+                // we support only string values
+                String val=(String)attribute.getValue();
+                String name=attribute.getName();
+                BufferedReader is=jkmx.getStream("set=" + jkName + "|" +
+                        name + "|" + val);
+                if( is==null ) return;
+                String res=is.readLine();
+                if( log.isDebugEnabled())
+                    log.debug( "Setting " + jkName + " " + name + " result " + res);
+
+                jkmx.refreshMetadata();
+                jkmx.refreshAttributes();
+            } catch( Exception ex ) {
+                throw new MBeanException(ex);
+            }
+        }
+
+        public Object invoke(String name, Object params[], String signature[])
+            throws MBeanException, ReflectionException {
+            try {
+                // we support only string values
+                BufferedReader is=jkmx.getStream("inv=" + jkName + "|" +
+                        name );
+                if( is==null ) return null;
+                String res=is.readLine();
+                if( log.isDebugEnabled())
+                    log.debug( "Invoking " + jkName + " " + name + " result " + res);
+
+                jkmx.refreshMetadata();
+                jkmx.refreshAttributes();
+            } catch( Exception ex ) {
+                throw new MBeanException(ex);
+            }
+            return null;
+        }
+
+    }
+
+
+}
+
diff --git a/connectors/jk/java/org/apache/jk/common/MsgAjp.java b/connectors/jk/java/org/apache/jk/common/MsgAjp.java
new file mode 100644
index 0000000..9d0936e
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/MsgAjp.java
@@ -0,0 +1,327 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.common;
+
+import java.io.IOException;
+
+import org.apache.jk.core.Msg;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+
+/**
+ * A single packet for communication between the web server and the
+ * container.  Designed to be reused many times with no creation of
+ * garbage.  Understands the format of data types for these packets.
+ * Can be used (somewhat confusingly) for both incoming and outgoing
+ * packets.  
+ *
+ * See Ajp14/Ajp13Packet.java.
+ *
+ * @author Henri Gomez [hgomez@apache.org]
+ * @author Dan Milstein [danmil@shore.net]
+ * @author Keith Wannamaker [Keith@Wannamaker.org]
+ * @author Kevin Seguin
+ * @author Costin Manolache
+ */
+public class MsgAjp extends Msg {
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( MsgAjp.class );
+
+    // that's the original buffer size in ajp13 - otherwise we'll get interoperability problems.
+    private byte buf[]=new byte[8*1024]; 
+    // The current read or write position in the buffer
+    private int pos;    
+    /**
+     * This actually means different things depending on whether the
+     * packet is read or write.  For read, it's the length of the
+     * payload (excluding the header).  For write, it's the length of
+     * the packet as a whole (counting the header).  Oh, well.
+     */
+    private int len; 
+
+
+    
+    
+    /**
+     * Prepare this packet for accumulating a message from the container to
+     * the web server.  Set the write position to just after the header
+     * (but leave the length unwritten, because it is as yet unknown).
+     */
+    public void reset() {
+        len = 4;
+        pos = 4;
+    }
+	
+    /**
+     * For a packet to be sent to the web server, finish the process of
+     * accumulating data and write the length of the data payload into
+     * the header.  
+     */
+    public void end() {
+        len=pos;
+        int dLen=len-4;
+
+        buf[0] = (byte)0x41;
+        buf[1] = (byte)0x42;
+        buf[2]=  (byte)((dLen>>>8 ) & 0xFF );
+        buf[3] = (byte)(dLen & 0xFF);
+    }
+
+    public byte[] getBuffer() {
+        return buf;
+    }
+
+    public int getLen() {
+        return len;
+    }
+    
+    // ============ Data Writing Methods ===================
+
+    /**
+     * Add an int.
+     *
+     * @param val The integer to write.
+     */
+    public void appendInt( int val ) {
+        buf[pos++]   = (byte) ((val >>>  8) & 0xFF);
+        buf[pos++] = (byte) (val & 0xFF);
+    }
+
+    public void appendByte( int val ) {
+        buf[pos++] = (byte)val;
+    }
+	
+    public void appendLongInt( int val ) {
+        buf[pos++]   = (byte) ((val >>>  24) & 0xFF);
+        buf[pos++] = (byte) ((val >>>  16) & 0xFF);
+        buf[pos++] = (byte) ((val >>>   8) & 0xFF);
+        buf[pos++] = (byte) (val & 0xFF);
+    }
+
+    /**
+     * Write a String out at the current write position.  Strings are
+     * encoded with the length in two bytes first, then the string, and
+     * then a terminating \0 (which is <B>not</B> included in the
+     * encoded length).  The terminator is for the convenience of the C
+     * code, where it saves a round of copying.  A null string is
+     * encoded as a string with length 0.  
+     */
+    public void appendBytes(MessageBytes mb) throws IOException {
+        if(mb==null || mb.isNull() ) {
+            appendInt( 0);
+            appendByte(0);
+            return;
+        }
+
+        // XXX Convert !!
+        ByteChunk bc= mb.getByteChunk();
+        appendByteChunk(bc);
+    }
+
+    public void appendByteChunk(ByteChunk bc) throws IOException {
+        if(bc==null) {
+            log.error("appendByteChunk() null");
+            appendInt( 0);
+            appendByte(0);
+            return;
+        }
+
+        byte[] bytes = bc.getBytes();
+        int start=bc.getStart();
+        appendInt( bc.getLength() );
+        cpBytes(bytes, start, bc.getLength());
+        appendByte(0);
+    }
+
+    /** 
+     * Copy a chunk of bytes into the packet, starting at the current
+     * write position.  The chunk of bytes is encoded with the length
+     * in two bytes first, then the data itself, and finally a
+     * terminating \0 (which is <B>not</B> included in the encoded
+     * length).
+     *
+     * @param b The array from which to copy bytes.
+     * @param off The offset into the array at which to start copying
+     * @param numBytes The number of bytes to copy.  
+     */
+    public void appendBytes( byte b[], int off, int numBytes ) {
+        appendInt( numBytes );
+        cpBytes( b, off, numBytes );
+        appendByte(0);
+    }
+    
+    private void cpBytes( byte b[], int off, int numBytes ) {
+        if( pos + numBytes >= buf.length ) {
+            log.error("Buffer overflow: buffer.len=" + buf.length + " pos=" +
+                      pos + " data=" + numBytes );
+            dump("Overflow/coBytes");
+            log.error( "Overflow ", new Throwable());
+            return;
+        }
+        System.arraycopy( b, off, buf, pos, numBytes);
+        pos += numBytes;
+        // buf[pos + numBytes] = 0; // Terminating \0
+    }
+    
+
+    
+    // ============ Data Reading Methods ===================
+
+    /**
+     * Read an integer from packet, and advance the read position past
+     * it.  Integers are encoded as two unsigned bytes with the
+     * high-order byte first, and, as far as I can tell, in
+     * little-endian order within each byte.  
+     */
+    public int getInt() {
+        int b1 = buf[pos++] & 0xFF;  // No swap, Java order
+        int b2 = buf[pos++] & 0xFF;
+
+        return  (b1<<8) + b2;
+    }
+
+    public int peekInt() {
+        int b1 = buf[pos] & 0xFF;  // No swap, Java order
+        int b2 = buf[pos+1] & 0xFF;
+
+        return  (b1<<8) + b2;
+    }
+
+    public byte getByte() {
+        byte res = buf[pos++];
+        return res;
+    }
+
+    public byte peekByte() {
+        byte res = buf[pos];
+        return res;
+    }
+
+    public void getBytes(MessageBytes mb) {
+        int length = getInt();
+        if( (length == 0xFFFF) || (length == -1) ) {
+            mb.setString( null );
+            return;
+        }
+        mb.setBytes( buf, pos, length );
+        pos += length;
+        pos++; // Skip the terminating \0
+    }
+    
+    /**
+     * Copy a chunk of bytes from the packet into an array and advance
+     * the read position past the chunk.  See appendBytes() for details
+     * on the encoding.
+     *
+     * @return The number of bytes copied.
+     */
+    public int getBytes(byte dest[]) {
+        int length = getInt();
+        if( length > buf.length ) {
+            // XXX Should be if(pos + length > buff.legth)?
+            log.error("getBytes() buffer overflow " + length + " " + buf.length );
+        }
+	
+        if( (length == 0xFFFF) || (length == -1) ) {
+            log.info("Null string " + length);
+            return 0;
+        }
+
+        System.arraycopy( buf, pos,  dest, 0, length );
+        pos += length;
+        pos++; // Skip terminating \0  XXX I believe this is wrong but harmless
+        return length;
+    }
+
+    /**
+     * Read a 32 bits integer from packet, and advance the read position past
+     * it.  Integers are encoded as four unsigned bytes with the
+     * high-order byte first, and, as far as I can tell, in
+     * little-endian order within each byte.
+     */
+    public int getLongInt() {
+        int b1 = buf[pos++] & 0xFF;  // No swap, Java order
+        b1 <<= 8;
+        b1 |= (buf[pos++] & 0xFF);
+        b1 <<= 8;
+        b1 |= (buf[pos++] & 0xFF);
+        b1 <<=8;
+        b1 |= (buf[pos++] & 0xFF);
+        return  b1;
+    }
+
+    public int getHeaderLength() {
+        return 4;
+    }
+
+    public int processHeader() {
+        pos = 0;
+        int mark = getInt();
+        len      = getInt();
+	    
+        if( mark != 0x1234 && mark != 0x4142 ) {
+            // XXX Logging
+            log.error("BAD packet signature " + mark);
+            dump( "In: " );
+            return -1;
+        }
+
+        if( log.isDebugEnabled() ) 
+            log.debug( "Received " + len + " " + buf[0] );
+        return len;
+    }
+    
+    public void dump(String msg) {
+        log.debug( msg + ": " + buf + " " + pos +"/" + (len + 4));
+        int max=pos;
+        if( len + 4 > pos )
+            max=len+4;
+        if( max >1000 ) max=1000;
+        for( int j=0; j < max; j+=16 )
+            System.out.println( hexLine( buf, j, len ));
+	
+    }
+
+    /* -------------------- Utilities -------------------- */
+    // XXX Move to util package
+
+    public static String hexLine( byte buf[], int start, int len ) {
+        StringBuffer sb=new StringBuffer();
+        for( int i=start; i< start+16 ; i++ ) {
+            if( i < len + 4)
+                sb.append( hex( buf[i] ) + " ");
+            else 
+                sb.append( "   " );
+        }
+        sb.append(" | ");
+        for( int i=start; i < start+16 && i < len + 4; i++ ) {
+            if( ! Character.isISOControl( (char)buf[i] ))
+                sb.append( new Character((char)buf[i]) );
+            else
+                sb.append( "." );
+        }
+        return sb.toString();
+    }
+
+    private  static String hex( int x ) {
+        //	    if( x < 0) x=256 + x;
+        String h=Integer.toHexString( x );
+        if( h.length() == 1 ) h = "0" + h;
+        return h.substring( h.length() - 2 );
+    }
+
+}
diff --git a/connectors/jk/java/org/apache/jk/common/Shm.java b/connectors/jk/java/org/apache/jk/common/Shm.java
new file mode 100644
index 0000000..2c0ee71
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/Shm.java
@@ -0,0 +1,326 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.common;
+
+import java.io.IOException;
+import java.util.Vector;
+
+import org.apache.jk.apr.AprImpl;
+import org.apache.jk.core.Msg;
+import org.apache.jk.core.MsgContext;
+import org.apache.jk.core.WorkerEnv;
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.tomcat.util.buf.C2BConverter;
+
+/* The code is a bit confusing at this moment - the class is used as
+   a Bean, or ant Task, or CLI - i.e. you set properties and call execute.
+
+   That's different from the rest of jk handlers wich are stateless ( but
+   similar with Coyote-http ).
+*/
+
+
+/** Handle the shared memory objects.
+ *
+ * @author Costin Manolache
+ */
+public class Shm extends JniHandler {
+    String file="/tmp/shm.file";
+    int size;
+    String host="localhost";
+    int port=8009;
+    String unixSocket;
+
+    boolean help=false;
+    boolean unregister=false;
+    boolean reset=false;
+    String dumpFile=null;
+
+    Vector groups=new Vector();
+    
+    // Will be dynamic ( getMethodId() ) after things are stable 
+    static final int SHM_WRITE_SLOT=2;
+    static final int SHM_RESET=5;
+    static final int SHM_DUMP=6;
+    
+    public Shm() {
+    }
+
+    /** Scoreboard location
+     */
+    public void setFile( String f ) {
+        file=f;
+    }
+
+    /** Copy the scoreboard in a file for debugging
+     *  Will also log a lot of information about what's in the scoreboard.
+     */
+    public void setDump( String dumpFile ) {
+        this.dumpFile=dumpFile;
+    }
+    
+    /** Size. Used only if the scoreboard is to be created.
+     */
+    public void setSize( int size ) {
+        this.size=size;
+    }
+
+    /** Set this to get the scoreboard reset.
+     *  The shm segment will be destroyed and a new one created,
+     *  with the provided size.
+     *
+     *  Requires "file" and "size".
+     */
+    public void setReset(boolean b) {
+        reset=true;
+    }
+
+    /** Ajp13 host
+     */
+    public void setHost( String host ) {
+        this.host=host;
+    }
+
+    /** Mark this instance as belonging to a group
+     */
+    public void setGroup( String grp ) {
+        groups.addElement( grp );
+    }
+
+    /** Ajp13 port
+     */
+    public void setPort( int port ) {
+        this.port=port;
+    }
+
+    /** Unix socket where tomcat is listening.
+     *  Use it only if tomcat is on the same host, of course
+     */
+    public void setUnixSocket( String unixSocket  ) {
+        this.unixSocket=unixSocket;
+    }
+
+    /** Set this option to mark the tomcat instance as
+        'down', so apache will no longer forward messages to it.
+        Note that requests with a session will still try this
+        host first.
+
+        This can be used to implement gracefull shutdown.
+
+        Host and port are still required, since they are used
+        to identify tomcat.
+    */
+    public void setUnregister( boolean unregister  ) {
+        this.unregister=true;
+    }
+    
+    public void init() throws IOException {
+        super.initNative( "shm" );
+        if( apr==null ) return;
+        if( file==null ) {
+            log.error("No shm file, disabling shared memory");
+            apr=null;
+            return;
+        }
+
+        // Set properties and call init.
+        setNativeAttribute( "file", file );
+        if( size > 0 )
+            setNativeAttribute( "size", Integer.toString( size ) );
+        
+        initJkComponent();
+    }
+
+    public void resetScoreboard() throws IOException {
+        if( apr==null ) return;
+        MsgContext mCtx=createMsgContext();
+        Msg msg=(Msg)mCtx.getMsg(0);
+        msg.reset();
+
+        msg.appendByte( SHM_RESET );
+        
+        this.invoke( msg, mCtx );
+    }
+
+    public void dumpScoreboard(String fname) throws IOException {
+        if( apr==null ) return;
+        MsgContext mCtx=createMsgContext();
+        Msg msg=(Msg)mCtx.getMsg(0);
+        C2BConverter c2b=(C2BConverter)mCtx.getNote(C2B_NOTE);
+        msg.reset();
+
+        msg.appendByte( SHM_DUMP );
+
+        appendString( msg, fname, c2b);
+        
+        this.invoke( msg, mCtx );
+    }
+
+    /** Register a tomcat instance
+     *  XXX make it more flexible
+     */
+    public void registerTomcat(String host, int port, String unixDomain)
+        throws IOException
+    {
+        String instanceId=host+":" + port;
+
+        String slotName="TOMCAT:" + instanceId;
+        MsgContext mCtx=createMsgContext();
+        Msg msg=(Msg)mCtx.getMsg(0);
+        msg.reset();
+        C2BConverter c2b=(C2BConverter)mCtx.getNote(C2B_NOTE);
+        
+        msg.appendByte( SHM_WRITE_SLOT );
+        appendString( msg, slotName, c2b );
+
+        int channelCnt=1;
+        if( unixDomain != null ) channelCnt++;
+
+        // number of groups. 0 means the default lb.
+        msg.appendInt( groups.size() );
+        for( int i=0; i<groups.size(); i++ ) {
+            appendString( msg, (String)groups.elementAt( i ), c2b);
+            appendString( msg, instanceId, c2b);
+        }
+        
+        // number of channels for this instance
+        msg.appendInt( channelCnt );
+        
+        // The body:
+        appendString(msg, "channel.socket:" + host + ":" + port, c2b );
+        msg.appendInt( 1 );
+        appendString(msg, "tomcatId", c2b);
+        appendString(msg, instanceId, c2b);
+
+        if( unixDomain != null ) {
+            appendString(msg, "channel.apr:" + unixDomain, c2b );
+            msg.appendInt(1);
+            appendString(msg, "tomcatId", c2b);
+            appendString(msg, instanceId, c2b);
+        }
+
+        System.out.println("Register " + instanceId );
+        this.invoke( msg, mCtx );
+    }
+
+    public void unRegisterTomcat(String host, int port)
+        throws IOException
+    {
+        String slotName="TOMCAT:" + host + ":" + port;
+        MsgContext mCtx=createMsgContext();
+        Msg msg=(Msg)mCtx.getMsg(0);
+        msg.reset();
+        C2BConverter c2b=(C2BConverter)mCtx.getNote(C2B_NOTE);
+        
+        msg.appendByte( SHM_WRITE_SLOT );
+        appendString( msg, slotName, c2b );
+
+        // number of channels for this instance
+        msg.appendInt( 0 );
+        msg.appendInt( 0 );
+        
+        System.out.println("UnRegister " + slotName );
+        this.invoke( msg, mCtx );
+    }
+
+    public void destroy() throws IOException {
+        destroyJkComponent();
+    }
+
+    
+    public  int invoke(Msg msg, MsgContext ep )
+        throws IOException
+    {
+        if( apr==null ) return 0;
+        log.debug("ChannelShm.invoke: "  + ep );
+        super.nativeDispatch( msg, ep, JK_HANDLE_SHM_DISPATCH, 0 );
+        return 0;
+    }    
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( Shm.class );
+
+    
+    //-------------------- Main - use the shm functions from ant or CLI ------
+
+    /** Local initialization - for standalone use
+     */
+    public void initCli() throws IOException {
+        WorkerEnv wEnv=new WorkerEnv();
+        AprImpl apr=new AprImpl();
+        wEnv.addHandler( "apr", apr );
+        wEnv.addHandler( "shm", this );
+        apr.init();
+        if( ! apr.isLoaded() ) {
+            log.error( "No native support. " +
+                       "Make sure libapr.so and libjkjni.so are available in LD_LIBRARY_PATH");
+            return;
+        }
+    }
+    
+    public void execute() {
+        try {
+            if( help ) return;
+            initCli();
+            init();
+
+            if( reset ) {
+                resetScoreboard();
+            } else if( dumpFile!=null ) {
+                dumpScoreboard(dumpFile);
+            } else if( unregister ) {
+                unRegisterTomcat( host, port );
+            } else {
+                registerTomcat( host, port, unixSocket );
+            }
+        } catch (Exception ex ) {
+            log.error( "Error executing Shm", ex);
+        }
+    }
+
+    public void setHelp( boolean b ) {
+        System.out.println("Usage: ");
+        System.out.println("  Shm [OPTIONS]");
+        System.out.println();
+        System.out.println("  -file SHM_FILE");
+        System.out.println("  -group GROUP ( can be specified multiple times )");
+        System.out.println("  -host HOST");
+        System.out.println("  -port PORT");
+        System.out.println("  -unixSocket UNIX_FILE");
+        //        System.out.println("  -priority XXX");
+        //        System.out.println("  -lbFactor XXX");
+        help=true;
+        return;
+    }
+    
+    public static void main( String args[] ) {
+        try {
+            Shm shm=new Shm();
+
+            if( args.length == 0 ||
+                ( "-?".equals(args[0]) ) ) {
+                shm.setHelp( true );
+                return;
+            }
+
+            IntrospectionUtils.processArgs( shm, args);
+            shm.execute();
+        } catch( Exception ex ) {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/connectors/jk/java/org/apache/jk/common/Shm14.java b/connectors/jk/java/org/apache/jk/common/Shm14.java
new file mode 100644
index 0000000..e446d83
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/Shm14.java
@@ -0,0 +1,90 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.common;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+
+import org.apache.jk.core.Msg;
+import org.apache.jk.core.MsgContext;
+import org.apache.tomcat.util.IntrospectionUtils;
+
+/** Shm implementation using JDK1.4 nio.
+ *
+ *
+ * @author Costin Manolache
+ */
+public class Shm14 extends Shm {
+    // Not ready yet.
+
+    MappedByteBuffer bb;
+
+    public void init() {
+        try {
+            RandomAccessFile f=new RandomAccessFile( file, "rw" );
+            FileChannel fc=f.getChannel();
+            
+            bb=fc.map( FileChannel.MapMode.READ_WRITE, 0, f.length());
+        } catch( IOException ex ) {
+            ex.printStackTrace();
+        }
+    }
+
+    public void dumpScoreboard(String file) {
+        // We can only sync with our backing store.
+        bb.force();
+        // XXX we should copy the content to the file
+    }
+
+    public void resetScoreboard() throws IOException {
+        // XXX Need to write the head
+    }
+
+
+    public  int invoke(Msg msg, MsgContext ep )
+        throws IOException
+    {
+        System.err.println("ChannelShm14.invoke: "  + ep );
+
+        // 
+        
+        return 0;
+    }    
+
+    public void initCli() {
+    }
+
+    public static void main( String args[] ) {
+        try {
+            Shm14 shm=new Shm14();
+
+            if( args.length == 0 ||
+                ( "-?".equals(args[0]) ) ) {
+                shm.setHelp( true );
+                return;
+            }
+
+            IntrospectionUtils.processArgs( shm, args);
+            shm.execute();
+        } catch( Exception ex ) {
+            ex.printStackTrace();
+        }
+    }
+
+}
diff --git a/connectors/jk/java/org/apache/jk/common/WorkerDummy.java b/connectors/jk/java/org/apache/jk/common/WorkerDummy.java
new file mode 100644
index 0000000..cc7600b
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/common/WorkerDummy.java
@@ -0,0 +1,93 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.common;
+
+import java.io.IOException;
+
+import org.apache.jk.core.JkHandler;
+import org.apache.jk.core.Msg;
+import org.apache.jk.core.MsgContext;
+import org.apache.jk.core.WorkerEnv;
+import org.apache.tomcat.util.buf.MessageBytes;
+
+
+/** A dummy worker, will just send back a dummy response.
+ *  Used for testing and tunning.
+ */
+public class WorkerDummy extends JkHandler
+{
+    public WorkerDummy()
+    {
+        String msg="HelloWorld";
+        byte b[]=msg.getBytes();
+        body.setBytes(b, 0, b.length);
+    }
+
+    /* ==================== Start/stop ==================== */
+
+    /** Initialize the worker. After this call the worker will be
+     *  ready to accept new requests.
+     */
+    public void init() throws IOException {
+        headersMsgNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "headerMsg" );
+    }
+ 
+    MessageBytes body=new MessageBytes();
+    private int headersMsgNote;
+    
+    public int invoke( Msg in, MsgContext ep ) 
+        throws IOException
+    {
+        MsgAjp msg=(MsgAjp)ep.getNote( headersMsgNote );
+        if( msg==null ) {
+            msg=new MsgAjp();
+            ep.setNote( headersMsgNote, msg );
+        }
+
+        msg.reset();
+        msg.appendByte(HandlerRequest.JK_AJP13_SEND_HEADERS);
+        msg.appendInt(200);
+        msg.appendBytes(null);
+
+        msg.appendInt(0);
+
+        ep.setType( JkHandler.HANDLE_SEND_PACKET );
+        ep.getSource().invoke( msg, ep );
+        //         msg.dump("out:" );
+
+        msg.reset();
+        msg.appendByte( HandlerRequest.JK_AJP13_SEND_BODY_CHUNK);
+        msg.appendInt( body.getLength() );
+        msg.appendBytes( body );
+
+        
+        ep.getSource().invoke(msg, ep);
+
+        msg.reset();
+        msg.appendByte( HandlerRequest.JK_AJP13_END_RESPONSE );
+        msg.appendInt( 1 );
+        
+        ep.getSource().invoke(msg, ep );
+        return OK;
+    }
+    
+    private static final int dL=0;
+    private static void d(String s ) {
+        System.err.println( "WorkerDummy: " + s );
+    }
+}
+
diff --git a/connectors/jk/java/org/apache/jk/config/ApacheConfig.java b/connectors/jk/java/org/apache/jk/config/ApacheConfig.java
new file mode 100644
index 0000000..8a30c5b
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/config/ApacheConfig.java
@@ -0,0 +1,570 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.config;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Date;
+import java.util.Hashtable;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
+
+/* The idea is to keep all configuration in server.xml and
+   the normal apache config files. We don't want people to
+   touch apache ( or IIS, NES ) config files unless they
+   want to and know what they're doing ( better than we do :-).
+
+   One nice feature ( if someone sends it ) would be to
+   also edit httpd.conf to add the include.
+
+   We'll generate a number of configuration files - this one
+   is trying to generate a native apache config file.
+
+   Some web.xml mappings do not "map" to server configuration - in
+   this case we need to fallback to forward all requests to tomcat.
+
+   Ajp14 will add to that the posibility to have tomcat and
+   apache on different machines, and many other improvements -
+   but this should also work for Ajp12, Ajp13 and Jni.
+
+*/
+
+/**
+    Generates automatic apache mod_jk configurations based on
+    the Tomcat server.xml settings and the war contexts
+    initialized during startup.
+    <p>
+    This config interceptor is enabled by inserting an ApacheConfig
+    <code>Listener</code> in 
+    the server.xml file like so:
+    <pre>
+    * < Server ... >
+    *   ...
+    *   <Listener className=<b>org.apache.ajp.tomcat4.config.ApacheConfig</b> 
+    *       <i>options</i> />
+    *   ...
+    * < /Server >
+    </pre>
+    where <i>options</i> can include any of the following attributes:
+    <ul>
+     <li><b>configHome</b> - default parent directory for the following paths.
+                            If not set, this defaults to TOMCAT_HOME. Ignored
+                            whenever any of the following paths is absolute.
+                             </li>
+     <li><b>jkConfig</b> - path to use for writing Apache mod_jk conf file. If
+                            not set, defaults to
+                            "conf/auto/mod_jk.conf".</li>
+     <li><b>workersConfig</b> - path to workers.properties file used by 
+                            mod_jk. If not set, defaults to
+                            "conf/jk/workers.properties".</li>
+     <li><b>modJk</b> - path to Apache mod_jk plugin file.  If not set,
+                        defaults to "modules/mod_jk.dll" on windows,
+                        "modules/mod_jk.nlm" on netware, and
+                        "libexec/mod_jk.so" everywhere else.</li>
+     <li><b>jkLog</b> - path to log file to be used by mod_jk.</li>
+     <li><b>jkDebug</b> - JK Loglevel setting.  May be debug, info, error, or emerg.
+                          If not set, defaults to emerg.</li>
+     <li><b>jkWorker</b> The desired worker.  Must be set to one of the workers
+                         defined in the workers.properties file. "ajp12", "ajp13"
+                         or "inprocess" are the workers found in the default
+                         workers.properties file. If not specified, defaults
+                         to "ajp13" if an Ajp13Interceptor is in use, otherwise
+                         it defaults to "ajp12".</li>
+     <li><b>forwardAll</b> - If true, forward all requests to Tomcat. This helps
+                             insure that all the behavior configured in the web.xml
+                             file functions correctly.  If false, let Apache serve
+                             static resources. The default is true.
+                             Warning: When false, some configuration in
+                             the web.xml may not be duplicated in Apache.
+                             Review the mod_jk conf file to see what
+                             configuration is actually being set in Apache.</li>
+     <li><b>noRoot</b> - If true, the root context is not mapped to
+                         Tomcat.  If false and forwardAll is true, all requests
+                         to the root context are mapped to Tomcat. If false and
+                         forwardAll is false, only JSP and servlets requests to
+                         the root context are mapped to Tomcat. When false,
+                         to correctly serve Tomcat's root context you must also
+                         modify the DocumentRoot setting in Apache's httpd.conf
+                         file to point to Tomcat's root context directory.
+                         Otherwise some content, such as Apache's index.html,
+                         will be served by Apache before mod_jk gets a chance
+                         to claim the request and pass it to Tomcat.
+                         The default is true.</li>
+    </ul>
+    <p>
+    @author Costin Manolache
+    @author Larry Isaacs
+    @author Mel Martinez
+    @author Bill Barker
+ */
+public class ApacheConfig  extends BaseJkConfig { 
+    
+    /** default path to mod_jk .conf location */
+    public static final String MOD_JK_CONFIG = "conf/auto/mod_jk.conf";
+    /** default path to workers.properties file
+	This should be also auto-generated from server.xml.
+    */
+    public static final String WORKERS_CONFIG = "conf/jk/workers.properties";
+    /** default mod_jk log file location */
+    public static final String JK_LOG_LOCATION = "logs/mod_jk.log";
+    /** default location of mod_jk Apache plug-in. */
+    public static final String MOD_JK;
+    
+    //set up some defaults based on OS type
+    static{
+        String os = System.getProperty("os.name").toLowerCase();
+        if(os.indexOf("windows")>=0){ 
+           MOD_JK = "modules/mod_jk.dll";
+        }else if(os.indexOf("netware")>=0){
+           MOD_JK = "modules/mod_jk.nlm";
+        }else{
+           MOD_JK = "libexec/mod_jk.so";
+        }
+    }
+    
+    private File jkConfig = null;
+    private File modJk = null;
+
+    // ssl settings 
+    private boolean sslExtract=true;
+    private String sslHttpsIndicator="HTTPS";
+    private String sslSessionIndicator="SSL_SESSION_ID";
+    private String sslCipherIndicator="SSL_CIPHER";
+    private String sslCertsIndicator="SSL_CLIENT_CERT";
+
+    Hashtable NamedVirtualHosts=null;
+    
+    public ApacheConfig() {
+    }
+
+    //-------------------- Properties --------------------
+
+    /**
+        set the path to the output file for the auto-generated
+        mod_jk configuration file.  If this path is relative
+        then it will be resolved absolutely against
+        the getConfigHome() path.
+        <p>
+        @param path String path to a file
+    */
+    public void setJkConfig(String path){
+	jkConfig= (path==null)?null:new File(path);
+    }
+
+    /**
+        set the path to the mod_jk Apache Module
+        @param path String path to a file
+    */
+    public void setModJk(String path){
+        modJk=( path==null?null:new File(path));
+    }
+
+    /** By default mod_jk is configured to collect SSL information from
+	the apache environment and send it to the Tomcat workers. The
+	problem is that there are many SSL solutions for Apache and as
+	a result the environment variable names may change.
+
+	The following JK related SSL configureation
+	can be used to customize mod_jk's SSL behaviour.
+
+	Should mod_jk send SSL information to Tomact (default is On)
+    */
+    public void setExtractSSL( boolean sslMode ) {
+	this.sslExtract=sslMode;
+    }
+
+    /** What is the indicator for SSL (default is HTTPS)
+     */
+    public void setHttpsIndicator( String s ) {
+	sslHttpsIndicator=s;
+    }
+
+    /**What is the indicator for SSL session (default is SSL_SESSION_ID)
+     */
+    public void setSessionIndicator( String s ) {
+	sslSessionIndicator=s;
+    }
+    
+    /**What is the indicator for client SSL cipher suit (default is SSL_CIPHER)
+     */
+    public void setCipherIndicator( String s ) {
+	sslCipherIndicator=s;
+    }
+
+    /** What is the indicator for the client SSL certificated(default
+	is SSL_CLIENT_CERT
+     */
+    public void setCertsIndicator( String s ) {
+	sslCertsIndicator=s;
+    }
+
+    // -------------------- Initialize/guess defaults --------------------
+
+    /** Initialize defaults for properties that are not set
+	explicitely
+    */
+    protected void initProperties() {
+        super.initProperties();
+
+	jkConfig= getConfigFile( jkConfig, configHome, MOD_JK_CONFIG);
+	workersConfig=getConfigFile( workersConfig, configHome,
+				     WORKERS_CONFIG);
+	if( modJk == null )
+	    modJk=new File(MOD_JK);
+	else
+	    modJk=getConfigFile( modJk, configHome, MOD_JK );
+	jkLog=getConfigFile( jkLog, configHome, JK_LOG_LOCATION);
+    }
+    // -------------------- Generate config --------------------
+    
+    protected PrintWriter getWriter() throws IOException {
+	String abJkConfig = jkConfig.getAbsolutePath();
+	return new PrintWriter(new FileWriter(abJkConfig, append));
+    }
+			       
+
+    // -------------------- Config sections  --------------------
+
+    /** Generate the loadModule and general options
+     */
+    protected boolean generateJkHead(PrintWriter mod_jk)
+    {
+
+	mod_jk.println("########## Auto generated on " +  new Date() +
+		       "##########" );
+	mod_jk.println();
+
+	// Fail if mod_jk not found, let the user know the problem
+	// instead of running into problems later.
+	if( ! modJk.exists() ) {
+	    log( "mod_jk location: " + modJk );
+	    log( "Make sure it is installed corectly or " +
+		 " set the config location" );
+	    log( "Using <Listener className=\""+getClass().getName()+"\"  modJk=\"PATH_TO_MOD_JK.SO_OR_DLL\" />" );
+	}
+            
+	// Verify the file exists !!
+	mod_jk.println("<IfModule !mod_jk.c>");
+	mod_jk.println("  LoadModule jk_module \""+
+		       modJk.toString().replace('\\','/') +
+                       "\"");
+	mod_jk.println("</IfModule>");
+	mod_jk.println();                
+
+	
+	// Fail if workers file not found, let the user know the problem
+	// instead of running into problems later.
+	if( ! workersConfig.exists() ) {
+	    log( "Can't find workers.properties at " + workersConfig );
+	    log( "Please install it in the default location or " +
+		 " set the config location" );
+	    log( "Using <Listener className=\"" + getClass().getName() + "\"  workersConfig=\"FULL_PATH\" />" );
+	    return false;
+	}
+            
+	mod_jk.println("JkWorkersFile \"" 
+		       + workersConfig.toString().replace('\\', '/') 
+		       + "\"");
+
+	mod_jk.println("JkLogFile \"" 
+		       + jkLog.toString().replace('\\', '/') 
+		       + "\"");
+	mod_jk.println();
+
+	if( jkDebug != null ) {
+	    mod_jk.println("JkLogLevel " + jkDebug);
+	    mod_jk.println();
+	}
+	return true;
+    }
+
+    protected void generateVhostHead(Host host, PrintWriter mod_jk) {
+
+        mod_jk.println();
+        String vhostip = host.getName();
+	String vhost = vhostip;
+	int ppos = vhost.indexOf(":");
+	if(ppos >= 0)
+	    vhost = vhost.substring(0,ppos);
+
+        mod_jk.println("<VirtualHost "+ vhostip + ">");
+        mod_jk.println("    ServerName " + vhost );
+        String [] aliases=host.findAliases();
+        if( aliases.length > 0 ) {
+            mod_jk.print("    ServerAlias " );
+            for( int ii=0; ii < aliases.length ; ii++) {
+                mod_jk.print( aliases[ii] + " " );
+            }
+            mod_jk.println();
+        }
+        indent="    ";
+    }
+
+    protected void generateVhostTail(Host host, PrintWriter mod_jk) {
+        mod_jk.println("</VirtualHost>");
+        indent="";
+    }
+    
+    protected void generateSSLConfig(PrintWriter mod_jk) {
+	if( ! sslExtract ) {
+	    mod_jk.println("JkExtractSSL Off");        
+	}
+	if( ! "HTTPS".equalsIgnoreCase( sslHttpsIndicator ) ) {
+	    mod_jk.println("JkHTTPSIndicator " + sslHttpsIndicator);        
+	}
+	if( ! "SSL_SESSION_ID".equalsIgnoreCase( sslSessionIndicator )) {
+	    mod_jk.println("JkSESSIONIndicator " + sslSessionIndicator);
+	}
+	if( ! "SSL_CIPHER".equalsIgnoreCase( sslCipherIndicator )) {
+	    mod_jk.println("JkCIPHERIndicator " + sslCipherIndicator);
+	}
+	if( ! "SSL_CLIENT_CERT".equalsIgnoreCase( sslCertsIndicator )) {
+	    mod_jk.println("JkCERTSIndicator " + sslCertsIndicator);
+	}
+
+	mod_jk.println();
+    }
+
+    // -------------------- Forward all mode --------------------
+    String indent="";
+    
+    /** Forward all requests for a context to tomcat.
+	The default.
+     */
+    protected void generateStupidMappings(Context context,
+					   PrintWriter mod_jk )
+    {
+	String ctxPath  = context.getPath();
+	if(ctxPath == null)
+	    return;
+
+	String nPath=("".equals(ctxPath)) ? "/" : ctxPath;
+	
+        mod_jk.println();
+	mod_jk.println(indent + "JkMount " +  nPath + " " + jkWorker );
+	if( "".equals(ctxPath) ) {
+	    mod_jk.println(indent + "JkMount " +  nPath + "* " + jkWorker );
+            if ( context.getParent() instanceof Host ) {
+                mod_jk.println(indent + "DocumentRoot \"" +
+                            getApacheDocBase(context) + "\"");
+            } else {
+                mod_jk.println(indent +
+                        "# To avoid Apache serving root welcome files from htdocs, update DocumentRoot");
+                mod_jk.println(indent +
+                        "# to point to: \"" + getApacheDocBase(context) + "\"");
+            }
+
+	} else {
+	    mod_jk.println(indent + "JkMount " +  nPath + "/* " + jkWorker );
+	}
+    }    
+
+    
+    private void generateNameVirtualHost( PrintWriter mod_jk, String ip ) {
+        if( !NamedVirtualHosts.containsKey(ip) ) {
+            mod_jk.println("NameVirtualHost " + ip + "");
+            NamedVirtualHosts.put(ip,ip);
+        }
+    }
+    
+    // -------------------- Apache serves static mode --------------------
+    // This is not going to work for all apps. We fall back to stupid mode.
+    
+    protected void generateContextMappings(Context context, PrintWriter mod_jk )
+    {
+	String ctxPath  = context.getPath();
+	Host vhost = getHost(context);
+
+        if( noRoot &&  "".equals(ctxPath) ) {
+            log("Ignoring root context in non-forward-all mode  ");
+            return;
+        }
+
+	mod_jk.println();
+	mod_jk.println(indent + "#################### " +
+		       ((vhost!=null ) ? vhost.getName() + ":" : "" ) +
+		       (("".equals(ctxPath)) ? "/" : ctxPath ) +
+		       " ####################" );
+        mod_jk.println();
+	// Dynamic /servet pages go to Tomcat
+ 
+	generateStaticMappings( context, mod_jk );
+
+	// InvokerInterceptor - it doesn't have a container,
+	// but it's implemented using a special module.
+	
+	// XXX we need to better collect all mappings
+
+	if(context.getLoginConfig() != null) {
+	    String loginPage = context.getLoginConfig().getLoginPage();
+	    if(loginPage != null) {
+		int lpos = loginPage.lastIndexOf("/");
+		String jscurl = loginPage.substring(0,lpos+1) + "j_security_check";
+		addMapping( ctxPath, jscurl, mod_jk);
+	    }
+	}
+	String [] servletMaps = context.findServletMappings();
+	for(int ii=0; ii < servletMaps.length; ii++) {
+	      addMapping( ctxPath, servletMaps[ii] , mod_jk );
+	}
+    }
+
+    /** Add an Apache extension mapping.
+     */
+    protected boolean addExtensionMapping( String ctxPath, String ext,
+					 PrintWriter mod_jk )
+    {
+        if( debug > 0 )
+            log( "Adding extension map for " + ctxPath + "/*." + ext );
+	mod_jk.println(indent + "JkMount " + ctxPath + "/*." + ext
+		       + " " + jkWorker);
+	return true;
+    }
+    
+    
+    /** Add a fulling specified Appache mapping.
+     */
+    protected boolean addMapping( String fullPath, PrintWriter mod_jk ) {
+        if( debug > 0 )
+            log( "Adding map for " + fullPath );
+	mod_jk.println(indent + "JkMount " + fullPath + "  " + jkWorker );
+	return true;
+    }
+    /** Add a partially specified Appache mapping.
+     */
+    protected boolean addMapping( String ctxP, String ext, PrintWriter mod_jk ) {
+        if( debug > 0 )
+            log( "Adding map for " + ext );
+	if(! ext.startsWith("/") )
+	    ext = "/" + ext;
+	if(ext.length() > 1)
+	    mod_jk.println(indent + "JkMount " + ctxP + ext+ "  " + jkWorker );
+	return true;
+    }
+
+    private void generateWelcomeFiles(Context context, PrintWriter mod_jk ) {
+	String wf[]=context.findWelcomeFiles();
+	if( wf==null || wf.length == 0 )
+	    return;
+	mod_jk.print(indent + "    DirectoryIndex ");
+	for( int i=0; i<wf.length ; i++ ) {
+	    mod_jk.print( wf[i] + " " );
+	}
+	mod_jk.println();
+    }
+
+    /** Mappings for static content. XXX need to add welcome files,
+     *  mime mappings ( all will be handled by Mime and Static modules of
+     *  apache ).
+     */
+    private void generateStaticMappings(Context context, PrintWriter mod_jk ) {
+	String ctxPath  = context.getPath();
+
+	// Calculate the absolute path of the document base
+	String docBase = getApacheDocBase(context);
+
+        if( !"".equals(ctxPath) ) {
+            // Static files will be served by Apache
+            mod_jk.println(indent + "# Static files ");		    
+            mod_jk.println(indent + "Alias " + ctxPath + " \"" + docBase + "\"");
+            mod_jk.println();
+        } else {
+            if ( getHost(context) != null ) {
+                mod_jk.println(indent + "DocumentRoot \"" +
+                            getApacheDocBase(context) + "\"");
+            } else {
+                // For root context, ask user to update DocumentRoot setting.
+                // Using "Alias / " interferes with the Alias for other contexts.
+                mod_jk.println(indent +
+                        "# Be sure to update DocumentRoot");
+                mod_jk.println(indent +
+                        "# to point to: \"" + docBase + "\"");
+            }
+        }
+	mod_jk.println(indent + "<Directory \"" + docBase + "\">");
+	mod_jk.println(indent + "    Options Indexes FollowSymLinks");
+
+	generateWelcomeFiles(context, mod_jk);
+
+	// XXX XXX Here goes the Mime types and welcome files !!!!!!!!
+	mod_jk.println(indent + "</Directory>");
+	mod_jk.println();            
+	
+
+	// Deny serving any files from WEB-INF
+	mod_jk.println();            
+	mod_jk.println(indent +
+		       "# Deny direct access to WEB-INF and META-INF");
+	mod_jk.println(indent + "#");                        
+	mod_jk.println(indent + "<Location \"" + ctxPath + "/WEB-INF/*\">");
+	mod_jk.println(indent + "    AllowOverride None");
+	mod_jk.println(indent + "    deny from all");
+	mod_jk.println(indent + "</Location>");
+	// Deny serving any files from META-INF
+	mod_jk.println();            
+	mod_jk.println(indent + "<Location \"" + ctxPath + "/META-INF/*\">");
+	mod_jk.println(indent + "    AllowOverride None");
+	mod_jk.println(indent + "    deny from all");
+	mod_jk.println(indent + "</Location>");
+	if (File.separatorChar == '\\') {
+	    mod_jk.println(indent + "#");		    
+	    mod_jk.println(indent +
+			   "# Use Directory too. On Windows, Location doesn't"
+			   + " work unless case matches");
+	    mod_jk.println(indent + "#");                        
+	    mod_jk.println(indent +
+			   "<Directory \"" + docBase + "/WEB-INF/\">");
+	    mod_jk.println(indent + "    AllowOverride None");
+	    mod_jk.println(indent + "    deny from all");
+	    mod_jk.println(indent + "</Directory>");
+	    mod_jk.println();
+	    mod_jk.println(indent +
+			   "<Directory \"" + docBase + "/META-INF/\">");
+	    mod_jk.println(indent + "    AllowOverride None");
+	    mod_jk.println(indent + "    deny from all");
+	    mod_jk.println(indent + "</Directory>");
+	}
+	mod_jk.println();
+    }    
+
+    // -------------------- Utils --------------------
+
+    private String getApacheDocBase(Context context)
+    {
+	// Calculate the absolute path of the document base
+	String docBase = getAbsoluteDocBase(context);
+	if (File.separatorChar == '\\') {
+	    // use separator preferred by Apache
+	    docBase = docBase.replace('\\','/');
+	}
+        return docBase;
+    }
+
+    private String getVirtualHostAddress(String vhost, String vhostip) {
+        if( vhostip == null ) {
+            if ( vhost != null && vhost.length() > 0 && Character.isDigit(vhost.charAt(0)) )
+                vhostip=vhost;
+            else
+                vhostip="*";
+        }
+        return vhostip;
+    }
+
+}
diff --git a/connectors/jk/java/org/apache/jk/config/BaseJkConfig.java b/connectors/jk/java/org/apache/jk/config/BaseJkConfig.java
new file mode 100644
index 0000000..0efc72d
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/config/BaseJkConfig.java
@@ -0,0 +1,490 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.config;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Engine;
+import org.apache.catalina.Host;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Server;
+
+
+/**
+    Base class for automatic jk based configurations based on
+    the Tomcat server.xml settings and the war contexts
+    initialized during startup.
+    <p>
+    This config interceptor is enabled by inserting a Config
+    element in the <b>&lt;ContextManager&gt;</b> tag body inside
+    the server.xml file like so:
+    <pre>
+    * < ContextManager ... >
+    *   ...
+    *   <<b>???Config</b> <i>options</i> />
+    *   ...
+    * < /ContextManager >
+    </pre>
+    where <i>options</i> can include any of the following attributes:
+    <ul>
+     <li><b>configHome</b> - default parent directory for the following paths.
+                             If not set, this defaults to TOMCAT_HOME. Ignored
+                             whenever any of the following paths is absolute.
+                             </li>
+     <li><b>workersConfig</b> - path to workers.properties file used by 
+                                jk connector. If not set, defaults to
+                                "conf/jk/workers.properties".</li>
+     <li><b>jkLog</b> - path to log file to be used by jk connector.</li>
+     <li><b>jkDebug</b> - Loglevel setting.  May be debug, info, error, or emerg.
+                          If not set, defaults to emerg.</li>
+     <li><b>jkWorker</b> The desired worker.  Must be set to one of the workers
+                         defined in the workers.properties file. "ajp12", "ajp13"
+                         or "inprocess" are the workers found in the default
+                         workers.properties file. If not specified, defaults
+                         to "ajp13" if an Ajp13Interceptor is in use, otherwise
+                         it defaults to "ajp12".</li>
+     <li><b>forwardAll</b> - If true, forward all requests to Tomcat. This helps
+                             insure that all the behavior configured in the web.xml
+                             file functions correctly.  If false, let Apache serve
+                             static resources. The default is true.
+                             Warning: When false, some configuration in
+                             the web.xml may not be duplicated in Apache.
+                             Review the mod_jk conf file to see what
+                             configuration is actually being set in Apache.</li>
+     <li><b>noRoot</b> - If true, the root context is not mapped to
+                         Tomcat.  If false and forwardAll is true, all requests
+                         to the root context are mapped to Tomcat. If false and
+                         forwardAll is false, only JSP and servlets requests to
+                         the root context are mapped to Tomcat. When false,
+                         to correctly serve Tomcat's root context you may also
+                         need to modify the web server to point it's home
+                         directory to Tomcat's root context directory.
+                         Otherwise some content, such as the root index.html,
+                         may be served by the web server before the connector
+                         gets a chance to claim the request and pass it to Tomcat.
+                         The default is true.</li>
+    </ul>
+    <p>
+    @author Costin Manolache
+    @author Larry Isaacs
+    @author Bill Barker
+	@version $Revision$
+ */
+public class BaseJkConfig  implements LifecycleListener {
+    protected int debug=0;
+    protected File configHome = null;
+    protected File workersConfig = null;
+
+    protected File jkLog = null;
+    protected String jkDebug="emerg";
+    protected String jkWorker = "ajp13";
+
+    protected boolean noRoot=true;
+    protected boolean forwardAll=true;
+
+    protected String tomcatHome;
+    protected boolean regenerate=false;
+    protected boolean append=false;
+    protected boolean legacy=false;
+
+    // -------------------- Tomcat callbacks --------------------
+
+
+    // Auto-config should be able to react to dynamic config changes,
+    // and regenerate the config.
+
+    /** Generate the configuration - only when the server is
+     *  completely initialized ( before starting )
+     */
+    public void lifecycleEvent(LifecycleEvent evt)
+    {
+	if(Lifecycle.START_EVENT.equals(evt.getType())) {
+	   execute( evt );
+	}
+    }
+
+    /** Generate configuration files.  Override with method to generate
+        web server specific configuration.
+     */
+    public void execute(LifecycleEvent evt)
+    {
+	initProperties();
+	PrintWriter mod_jk = null;
+	try {
+	    mod_jk = getWriter();
+	} catch(IOException iex) {
+	    log("Unable to open config file");
+	    return;
+	}
+	Lifecycle who = evt.getLifecycle();
+	if( who instanceof Server ) {
+	    executeServer((Server)who, mod_jk);
+	} else if ( who instanceof Host ) {
+	    executeHost((Host)who, mod_jk);
+	} else if( who instanceof Context ) {
+	    executeContext((Context)who, mod_jk);
+	}
+	mod_jk.close();
+    }
+    /** Generate configuration files.  Override with method to generate
+        web server specific configuration.
+     */
+    public void executeServer(Server svr, PrintWriter mod_jk) {
+	if(! append ) {
+	    if( ! generateJkHead(mod_jk) )
+		return;
+	    generateSSLConfig(mod_jk);
+	    generateJkTail(mod_jk);
+	}
+    }
+
+    /** Generate SSL options
+     */
+    protected void generateSSLConfig(PrintWriter mod_jk)
+    {
+    }
+    /** Generate general options
+     */
+    protected boolean generateJkHead(PrintWriter mod_jk)
+    {
+	return true;
+    }
+    /** Generate general options
+     */
+    protected void generateJkTail(PrintWriter mod_jk)
+    {
+    }
+    /** Generate Virtual Host start
+     */
+    protected void generateVhostHead(Host host, PrintWriter mod_jk) {
+    }
+    /** Generate Virtual Host end
+     */
+    protected void generateVhostTail(Host host, PrintWriter mod_jk) {
+    }
+    /** Generate configuration files.  Override with method to generate
+        web server specific configuration.
+     */
+    protected void executeEngine(Engine egn, PrintWriter mod_jk) {
+	Container [] children = egn.findChildren();
+	for(int ii=0; ii < children.length; ii++) {
+	    if( children[ii] instanceof Host ) {
+		executeHost((Host)children[ii], mod_jk);
+	    } else if( children[ii] instanceof Context ) {
+		executeContext((Context)children[ii], mod_jk);
+	    }
+	}
+    }
+    /** Generate configuration files.  Override with method to generate
+        web server specific configuration.
+     */
+    protected void executeHost(Host hst, PrintWriter mod_jk) {
+	generateVhostHead(hst, mod_jk);
+	Container [] children = hst.findChildren();
+	for(int ii=0; ii < children.length; ii++) {
+	    if(children[ii] instanceof Context) {
+		executeContext((Context)children[ii],mod_jk);
+	    }
+	}
+	generateVhostTail(hst, mod_jk);
+    }
+    /**
+        executes the ApacheConfig interceptor. This method generates apache
+        configuration files for use with  mod_jk.
+        <p>
+        @param context a Context object.
+	  @param mod_jk Writer for output.
+    */
+    public void executeContext(Context context, PrintWriter mod_jk){
+
+	if(context.getPath().length() > 0 || ! noRoot ) {
+	    String docRoot = context.getServletContext().getRealPath("/");
+	    if( forwardAll || docRoot == null)
+		generateStupidMappings( context, mod_jk );
+	    else
+		generateContextMappings( context, mod_jk);
+	}
+    }
+    protected void generateStupidMappings(Context context, PrintWriter mod_jk){
+    }
+    protected void generateContextMappings(Context context, PrintWriter mod_jk){
+    }
+    /** Get the output Writer.  Override with method to generate
+        web server specific configuration.
+     */
+    protected PrintWriter getWriter() throws IOException {
+	return null;
+    }
+    /** Get the host associated with this Container (if any).
+     */
+    protected Host getHost(Container child) {
+	while(child != null && ! (child instanceof Host) ) {
+	    child = child.getParent();
+	}
+	return (Host)child;
+    }
+
+    //-------------------- Properties --------------------
+
+    /** Append to config file.
+     *  Set to <code>true</code> if the config information should be 
+     *  appended.
+     */
+    public void setAppend(boolean apnd) {
+	append = apnd;
+    }
+    /** If false, we'll try to generate a config that will
+     *  let apache serve static files.
+     *  The default is true, forward all requests in a context
+     *  to tomcat.
+     */
+    public void setForwardAll( boolean b ) {
+        forwardAll=b;
+    }
+
+    /** Special option - do not generate mappings for the ROOT
+        context. The default is true, and will not generate the mappings,
+        not redirecting all pages to tomcat (since /* matches everything).
+        This means that the web server's root remains intact but isn't
+        completely servlet/JSP enabled. If the ROOT webapp can be configured
+        with the web server serving static files, there's no problem setting
+        this option to false. If not, then setting it true means the web
+        server will be out of picture for all requests.
+    */
+    public void setNoRoot( boolean b ) {
+        noRoot=b;
+    }
+    
+    /**
+        set a path to the parent directory of the
+        conf folder.  That is, the parent directory
+        within which path setters would be resolved against,
+        if relative.  For example if ConfigHome is set to "/home/tomcat"
+        and regConfig is set to "conf/mod_jk.conf" then the resulting 
+        path used would be: 
+        "/home/tomcat/conf/mod_jk.conf".</p>
+        <p>
+        However, if the path is set to an absolute path,
+        this attribute is ignored.
+        <p>
+        If not set, execute() will set this to TOMCAT_HOME.
+        <p>
+        @param dir - path to a directory
+    */
+    public void setConfigHome(String dir){
+        if( dir==null ) return;
+        File f=new File(dir);
+        if(!f.isDirectory()){
+            throw new IllegalArgumentException(
+                "BaseConfig.setConfigHome(): "+
+                "Configuration Home must be a directory! : "+dir);
+        }
+        configHome = f;
+    }
+
+    /**
+        set a path to the workers.properties file.
+        @param path String path to workers.properties file
+    */
+    public void setWorkersConfig(String path){
+        workersConfig= (path==null?null:new File(path));
+    }
+
+    /**
+        set the path to the log file
+        @param path String path to a file
+    */
+    public void setJkLog(String path){
+        jkLog = ( path==null ? null : new File(path));
+    }
+
+    /** Set the verbosity level
+        ( use debug, error, etc. ) If not set, no log is written.
+     */
+    public void setJkDebug( String level ) {
+        jkDebug=level;
+    }
+
+    /**
+        Sets the JK worker.
+        @param worker The worker
+     */
+    public void setJkWorker(String worker){
+        jkWorker = worker;
+    }
+
+    public void setLegacy(boolean legacy) {
+        this.legacy = legacy;
+    }
+
+    // -------------------- Initialize/guess defaults --------------------
+
+    /** Initialize defaults for properties that are not set
+        explicitely
+    */
+    protected void initProperties() {
+        tomcatHome = System.getProperty("catalina.home");
+        File tomcatDir = new File(tomcatHome);
+        if(configHome==null){
+            configHome=tomcatDir;
+        }
+    }
+
+    // -------------------- Config Utils  --------------------
+
+
+    /** Add an extension mapping. Override with method to generate
+        web server specific configuration
+     */
+    protected boolean addExtensionMapping( String ctxPath, String ext,
+					 PrintWriter pw )
+    {
+	return true;
+    }
+    
+    
+    /** Add a fulling specified mapping.  Override with method to generate
+        web server specific configuration
+     */
+    protected boolean addMapping( String fullPath, PrintWriter pw ) {
+	return true;
+    }
+
+    // -------------------- General Utils --------------------
+
+    protected String getAbsoluteDocBase(Context context)
+    {
+	// Calculate the absolute path of the document base
+	String docBase = context.getServletContext().getRealPath("/");
+	docBase = docBase.substring(0,docBase.length()-1);
+	if (!isAbsolute(docBase)){
+	    docBase = tomcatHome + "/" + docBase;
+	}
+	docBase = patch(docBase);
+        return docBase;
+    }
+
+    // ------------------ Grabbed from FileUtil -----------------
+    public static File getConfigFile( File base, File configDir, String defaultF )
+    {
+        if( base==null )
+            base=new File( defaultF );
+        if( ! base.isAbsolute() ) {
+            if( configDir != null )
+                base=new File( configDir, base.getPath());
+            else
+                base=new File( base.getAbsolutePath()); //??
+        }
+        File parent=new File(base.getParent());
+        if(!parent.exists()){
+            if(!parent.mkdirs()){
+                throw new RuntimeException(
+                    "Unable to create path to config file :"+
+                    base.getAbsolutePath());
+            }
+        }
+        return base;
+    }
+    public static String patch(String path) {
+        String patchPath = path;
+
+        // Move drive spec to the front of the path
+        if (patchPath.length() >= 3 &&
+            patchPath.charAt(0) == '/'  &&
+            Character.isLetter(patchPath.charAt(1)) &&
+            patchPath.charAt(2) == ':') {
+            patchPath=patchPath.substring(1,3)+"/"+patchPath.substring(3);
+        }
+
+        // Eliminate consecutive slashes after the drive spec
+	if (patchPath.length() >= 2 &&
+            Character.isLetter(patchPath.charAt(0)) &&
+            patchPath.charAt(1) == ':') {
+            char[] ca = patchPath.replace('/', '\\').toCharArray();
+            char c;
+            StringBuffer sb = new StringBuffer();
+
+            for (int i = 0; i < ca.length; i++) {
+                if ((ca[i] != '\\') ||
+                    (ca[i] == '\\' &&
+                        i > 0 &&
+                        ca[i - 1] != '\\')) {
+                    if (i == 0 &&
+                        Character.isLetter(ca[i]) &&
+                        i < ca.length - 1 &&
+                        ca[i + 1] == ':') {
+                        c = Character.toUpperCase(ca[i]);
+                    } else {
+                        c = ca[i];
+                    }
+
+                    sb.append(c);
+                }
+            }
+
+            patchPath = sb.toString();
+        }
+
+	// fix path on NetWare - all '/' become '\\' and remove duplicate '\\'
+	if (System.getProperty("os.name").startsWith("NetWare") &&
+	    path.length() >=3 &&
+	    path.indexOf(':') > 0) {
+	    char[] ca = patchPath.replace('/', '\\').toCharArray();
+	    StringBuffer sb = new StringBuffer();
+
+	    for (int i = 0; i < ca.length; i++) {
+		if ((ca[i] != '\\') ||
+		    (ca[i] == '\\' && i > 0 && ca[i - 1] != '\\')) {
+		    sb.append(ca[i]);
+		}
+	    }
+	    patchPath = sb.toString();
+	}
+
+        return patchPath;
+    }
+
+    public static boolean isAbsolute( String path ) {
+	// normal file
+	if( path.startsWith("/" ) ) return true;
+
+	if( path.startsWith(File.separator ) ) return true;
+
+	// win c:
+	if (path.length() >= 3 &&
+            Character.isLetter(path.charAt(0)) &&
+            path.charAt(1) == ':')
+	    return true;
+
+	// NetWare volume:
+	if (System.getProperty("os.name").startsWith("NetWare") &&
+	    path.length() >=3 &&
+	    path.indexOf(':') > 0)
+	    return true;
+
+	return false;
+    }
+    protected void log(String msg) {
+	System.err.println(msg);
+    }
+}
diff --git a/connectors/jk/java/org/apache/jk/config/GeneratorApache2.java b/connectors/jk/java/org/apache/jk/config/GeneratorApache2.java
new file mode 100644
index 0000000..c2b2f4c
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/config/GeneratorApache2.java
@@ -0,0 +1,193 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.config;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Vector;
+
+import org.w3c.dom.Node;
+
+
+/* Naming conventions:
+
+JK_CONF_DIR == serverRoot/work  ( XXX /jkConfig ? )
+
+- Each vhost has a sub-dir named after the canonycal name
+
+- For each webapp in a vhost, there is a separate WEBAPP_NAME.jkmap
+
+- In httpd.conf ( or equivalent servers ), in each virtual host you
+should "Include JK_CONF_DIR/VHOST/jk_apache.conf". The config
+file will contain the Alias declarations and other rules required
+for apache operation. Same for other servers. 
+
+- WebXml2Jk will be invoked by a config tool or automatically for each
+webapp - it'll generate the WEBAPP.jkmap files and config fragments.
+
+WebXml2Jk will _not_ generate anything else but mappings.
+It should _not_ try to guess locations or anything else - that's
+another components' job.
+
+*/
+
+/**
+ *
+ * @author Costin Manolache
+ */
+public class GeneratorApache2 implements WebXml2Jk.MappingGenerator {
+    WebXml2Jk wxml;
+    String vhost;
+    String cpath;
+    String worker;
+    PrintWriter out;
+    
+    public void setWebXmlReader(WebXml2Jk wxml ) {
+        this.wxml=wxml;
+        vhost=wxml.vhost;
+        cpath=wxml.cpath;
+        worker=wxml.worker;
+    }
+
+    public void generateStart() throws IOException {
+        File base=wxml.getJkDir();
+        File outF=new File(base, "jk2.conf");
+        out=new PrintWriter( new FileWriter( outF ));
+
+        out.println("# Must be included in a virtual host context for " + vhost );
+
+        out.println("Alias " + cpath + " \"" + wxml.docBase + "\"");
+        out.println("<Directory \"" + wxml.docBase + "\" >");
+        out.println("  Options Indexes FollowSymLinks");
+        generateMimeMapping( out );
+        generateWelcomeFiles( out);
+
+        // If we use this instead of extension mapping for jsp we can avoid most
+        // jsp security problems.
+        out.println("  AddHandler jakarta-servlet2 .jsp");
+        out.println("</Directory>");
+        out.println();
+        
+        out.println("<Location \"" + cpath + "/WEB-INF\" >");
+        out.println("  AllowOverride None");
+        out.println("  Deny from all");
+        out.println("</Location>");
+        out.println();
+        out.println("<Location \"" + cpath + "/META-INF\" >");
+        out.println("  AllowOverride None");
+        out.println("  Deny from all");
+        out.println("</Location>");
+        out.println();
+    }
+
+    private void generateWelcomeFiles( PrintWriter out ) {
+        Vector wf= wxml.getWellcomeFiles();
+        out.print("  DirectoryIndex ");
+        for( int i=0; i<wf.size(); i++ ) {
+            out.print( " " + (String)wf.elementAt(i));
+        }
+        out.println();
+    }
+    
+    private void generateMimeMapping( PrintWriter out ) {
+        Node webN=wxml.getWebXmlNode();
+        for( Node mapN=WebXml2Jk.getChild( webN, "mime-mapping" );
+             mapN != null; mapN = WebXml2Jk.getNext( mapN ) ) {
+            String ext=WebXml2Jk.getChildContent( mapN, "extension" );
+            String type=WebXml2Jk.getChildContent( mapN, "mime-type" );
+
+            out.println("  AddType " + type + " " + ext );
+        }
+        
+
+    }
+
+    public void generateEnd() {
+        out.close();
+    }
+    
+    public void generateServletMapping( String servlet, String url ) {
+        out.println( "<Location \"" + cpath + url + "\" >");
+        out.println( "  SetHandler jakarta-servlet2" );
+        out.println( "  JkUriSet group " + worker );
+        out.println( "  JkUriSet servlet " +  servlet);
+        out.println( "  JkUriSet host " +  vhost );
+        out.println( "  JkUriSet context " +  cpath );
+        out.println( "</Location>");
+        out.println();
+    }
+
+    public void generateFilterMapping( String servlet, String url ) {
+        out.println( "<Location \"" + cpath + url + "\" >");
+        out.println( "  SetHandler jakarta-servlet2" );
+        out.println( "  JkUriSet group " + worker );
+        out.println( "  JkUriSet servlet " +  servlet);
+        out.println( "  JkUriSet host " +  vhost );
+        out.println( "  JkUriSet context " +  cpath );
+        out.println( "</Location>");
+        out.println();
+    }
+
+    public void generateLoginConfig( String loginPage,
+                                     String errPage, String authM ) {
+        out.println( "<Location \"" + cpath + loginPage + "\" >");
+        out.println( "  SetHandler jakarta-servlet2" );
+        out.println( "  JkUriSet group " + worker );
+        out.println( "  JkUriSet host " +  vhost );
+        out.println( "  JkUriSet context " +  cpath );
+        out.println( "</Location>");
+        out.println();
+    }
+
+    public void generateErrorPage( int err, String location ) {
+        
+    }
+
+    // XXX Only if BASIC/DIGEST and 'integrated auth'
+    public void generateConstraints( Vector urls, Vector methods, Vector roles, boolean isSSL ) {
+        for( int i=0; i<urls.size(); i++ ) {
+            String url=(String)urls.elementAt(i);
+
+            out.println( "<Location \"" + cpath + url + "\" >");
+
+            if( methods.size() > 0 ) {
+                out.print("  <Limit ");
+                for( int j=0; j<methods.size(); j++ ) {
+                    String m=(String)methods.elementAt(j);
+                    out.print( " " +  m);
+                }
+                out.println(  " >" );
+            }
+
+            out.println( "    AuthType basic" );
+            out.print( "    Require group " );
+            for( int j=0; j<roles.size(); j++ ) {
+                String role=(String)roles.elementAt(j);
+                out.print( " " +  role);
+            }
+            out.println();
+
+            if( methods.size() > 0 ) {
+                out.println("  </Limit>");
+            }
+            
+            out.println( "</Location>");
+        }
+    }
+}
diff --git a/connectors/jk/java/org/apache/jk/config/GeneratorJk1.java b/connectors/jk/java/org/apache/jk/config/GeneratorJk1.java
new file mode 100644
index 0000000..ade15ef
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/config/GeneratorJk1.java
@@ -0,0 +1,112 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.config;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Vector;
+
+
+/* Naming conventions:
+
+JK_CONF_DIR == serverRoot/work  ( XXX /jkConfig ? )
+
+- Each vhost has a sub-dir named after the canonycal name
+
+- For each webapp in a vhost, there is a separate WEBAPP_NAME.jkmap
+
+- In httpd.conf ( or equivalent servers ), in each virtual host you
+should "Include JK_CONF_DIR/VHOST/jk_apache.conf". The config
+file will contain the Alias declarations and other rules required
+for apache operation. Same for other servers. 
+
+- WebXml2Jk will be invoked by a config tool or automatically for each
+webapp - it'll generate the WEBAPP.jkmap files and config fragments.
+
+WebXml2Jk will _not_ generate anything else but mappings.
+It should _not_ try to guess locations or anything else - that's
+another components' job.
+
+*/
+
+/**
+ *
+ * @author Costin Manolache
+ */
+public class GeneratorJk1 implements WebXml2Jk.MappingGenerator {
+    WebXml2Jk wxml;
+    String vhost;
+    String cpath;
+    String worker;
+    PrintWriter out;
+    
+    public void setWebXmlReader(WebXml2Jk wxml ) {
+        this.wxml=wxml;
+        vhost=wxml.vhost;
+        cpath=wxml.cpath;
+        worker=wxml.worker;
+    }
+
+    public void generateStart( ) throws IOException  {
+        File base=wxml.getJkDir();
+        File outF=new File(base, "jk.conf");
+        out=new PrintWriter( new FileWriter( outF ));
+        
+        out.println("# This must be included in the virtual host section for " + vhost );
+    }
+
+    public void generateEnd() {
+        out.close();
+    }
+
+    
+    public void generateServletMapping( String servlet, String url ) {
+        out.println( "JkMount " + cpath + url + " " + worker);
+    }
+
+    public void generateFilterMapping( String servlet, String url ) {
+        out.println( "JkMount " + cpath + url + " " + worker);
+    }
+
+    public void generateLoginConfig( String loginPage,
+                                        String errPage, String authM ) {
+        out.println( "JkMount " + cpath + loginPage + " " + worker);
+    }
+
+    public void generateErrorPage( int err, String location ) {
+
+    }
+
+    public void generateMimeMapping( String ext, String type ) {
+
+    }
+    
+    public void generateWelcomeFiles( Vector wf ) {
+
+    }
+                                            
+    
+    public void generateConstraints( Vector urls, Vector methods, Vector roles, boolean isSSL ) {
+        for( int i=0; i<urls.size(); i++ ) {
+            String url=(String)urls.elementAt(i);
+
+            out.println( "JkMount " + cpath + url + " " + worker);
+        }
+    }
+}
diff --git a/connectors/jk/java/org/apache/jk/config/GeneratorJk2.java b/connectors/jk/java/org/apache/jk/config/GeneratorJk2.java
new file mode 100644
index 0000000..450526d
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/config/GeneratorJk2.java
@@ -0,0 +1,143 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.config;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Vector;
+
+
+/* Naming conventions:
+
+JK_CONF_DIR == serverRoot/work  ( XXX /jkConfig ? )
+
+- Each vhost has a sub-dir named after the canonycal name
+
+- For each webapp in a vhost, there is a separate WEBAPP_NAME.jkmap
+
+- In httpd.conf ( or equivalent servers ), in each virtual host you
+should "Include JK_CONF_DIR/VHOST/jk_apache.conf". The config
+file will contain the Alias declarations and other rules required
+for apache operation. Same for other servers. 
+
+- WebXml2Jk will be invoked by a config tool or automatically for each
+webapp - it'll generate the WEBAPP.jkmap files and config fragments.
+
+WebXml2Jk will _not_ generate anything else but mappings.
+It should _not_ try to guess locations or anything else - that's
+another components' job.
+
+*/
+
+/**
+ *
+ * @author Costin Manolache
+ */
+public class GeneratorJk2 implements WebXml2Jk.MappingGenerator {
+    WebXml2Jk wxml;
+    String vhost;
+    String cpath;
+    String worker;
+    PrintWriter out;
+    
+    public void setWebXmlReader(WebXml2Jk wxml ) {
+        this.wxml=wxml;
+        vhost=wxml.vhost;
+        cpath=wxml.cpath;
+        worker=wxml.worker;
+    }
+
+    public void generateStart( ) throws IOException {
+        File base=wxml.getJkDir();
+        File outF=new File(base, "jk2map.properties");
+        out=new PrintWriter( new FileWriter( outF ));
+
+        out.println("# Autogenerated from web.xml" );
+    }
+
+    public void generateEnd() {
+        out.close();
+    }
+    
+    public void generateServletMapping( String servlet, String url ) {
+        out.println( "[uri:" + vhost + cpath + url + "]");
+        out.println( "group=" + worker );
+        out.println( "servlet=" +  servlet);
+        out.println( "host=" +  vhost); 
+        out.println( "context=" +  cpath);
+        out.println();
+    }
+
+    public void generateFilterMapping( String servlet, String url ) {
+        out.println( "[url:" + vhost + cpath + url + "]");
+        out.println( "group=" + worker );
+        out.println( "filter=" +  servlet);
+        out.println( "host=" +  vhost); 
+        out.println( "context=" +  cpath);
+        out.println();
+    }
+
+    public void generateLoginConfig( String loginPage,
+                                        String errPage, String authM ) {
+        out.println("[url:" + vhost + cpath + loginPage  + "]" );
+        out.println( "group=" + worker );
+        out.println( "host=" +  vhost); 
+        out.println( "context=" +  cpath);
+        out.println();
+        out.println("[url:" + vhost + cpath + errPage  + "]" );
+        out.println( "group=" + worker );
+        out.println( "host=" +  vhost); 
+        out.println( "context=" +  cpath);
+        out.println();
+    }
+
+    public void generateErrorPage( int err, String location ) {
+
+    }
+
+    public void generateMimeMapping( String ext, String type ) {
+
+    }
+    
+    public void generateWelcomeFiles( Vector wf ) {
+
+    }
+                                            
+    
+    public void generateConstraints( Vector urls, Vector methods, Vector roles, boolean isSSL ) {
+        for( int i=0; i<urls.size(); i++ ) {
+            String url=(String)urls.elementAt(i);
+
+            out.println("[url:" + vhost + cpath + url + "]");
+            out.println( "group=" + worker );
+            out.println( "host=" +  vhost); 
+            out.println( "context=" +  cpath);
+            for( int j=0; j<roles.size(); j++ ) {
+                String role=(String)roles.elementAt(j);
+                out.println( "role=" +  role);
+            }
+            for( int j=0; j<methods.size(); j++ ) {
+                String m=(String)methods.elementAt(j);
+                out.println( "method=" +  m);
+            }
+            if( isSSL )
+                out.println("ssl=true");
+        }
+    }
+}
diff --git a/connectors/jk/java/org/apache/jk/config/IISConfig.java b/connectors/jk/java/org/apache/jk/config/IISConfig.java
new file mode 100644
index 0000000..60714e3
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/config/IISConfig.java
@@ -0,0 +1,303 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.config;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Date;
+
+import org.apache.catalina.Context;
+
+
+/**
+    Generates automatic IIS isapi_redirect configurations based on
+    the Tomcat server.xml settings and the war contexts
+    initialized during startup.
+    <p>
+    This config interceptor is enabled by inserting an IISConfig
+    element in the <b>&lt;ContextManager&gt;</b> tag body inside
+    the server.xml file like so:
+    <pre>
+    * < ContextManager ... >
+    *   ...
+    *   <<b>IISConfig</b> <i>options</i> />
+    *   ...
+    * < /ContextManager >
+    </pre>
+    where <i>options</i> can include any of the following attributes:
+    <ul>
+     <li><b>configHome</b> - default parent directory for the following paths.
+                            If not set, this defaults to TOMCAT_HOME. Ignored
+                            whenever any of the following paths is absolute.
+                             </li>
+     <li><b>regConfig</b> - path to use for writing IIS isapi_redirect registry
+                            file. If not set, defaults to
+                            "conf/auto/iis_redirect.reg".</li>
+     <li><b>workersConfig</b> - path to workers.properties file used by 
+                                isapi_redirect. If not set, defaults to
+                                "conf/jk/workers.properties".</li>
+     <li><b>uriConfig</b> - path to use for writing IIS isapi_redirect uriworkermap
+                            file. If not set, defaults to
+                            "conf/auto/uriworkermap.properties".</li>
+     <li><b>jkLog</b> - path to log file to be used by isapi_redirect.</li>
+     <li><b>jkDebug</b> - Loglevel setting.  May be debug, info, error, or emerg.
+                          If not set, defaults to emerg.</li>
+     <li><b>jkWorker</b> The desired worker.  Must be set to one of the workers
+                         defined in the workers.properties file. "ajp12", "ajp13"
+                         or "inprocess" are the workers found in the default
+                         workers.properties file. If not specified, defaults
+                         to "ajp13" if an Ajp13Interceptor is in use, otherwise
+                         it defaults to "ajp12".</li>
+     <li><b>forwardAll</b> - If true, forward all requests to Tomcat. This helps
+                             insure that all the behavior configured in the web.xml
+                             file functions correctly.  If false, let IIS serve
+                             static resources assuming it has been configured
+                             to do so. The default is true.
+                             Warning: When false, some configuration in
+                             the web.xml may not be duplicated in IIS.
+                             Review the uriworkermap file to see what
+                             configuration is actually being set in IIS.</li>
+     <li><b>noRoot</b> - If true, the root context is not mapped to
+                         Tomcat.  If false and forwardAll is true, all requests
+                         to the root context are mapped to Tomcat. If false and
+                         forwardAll is false, only JSP and servlets requests to
+                         the root context are mapped to Tomcat. When false,
+                         to correctly serve Tomcat's root context you must also
+                         modify the Home Directory setting in IIS
+                         to point to Tomcat's root context directory.
+                         Otherwise some content, such as the root index.html,
+                         will be served by IIS before isapi_redirect gets a chance
+                         to claim the request and pass it to Tomcat.
+                         The default is true.</li>
+    </ul>
+  <p>
+    @author Costin Manolache
+    @author Larry Isaacs
+    @author Gal Shachor
+    @author Bill Barker
+ */
+public class IISConfig extends BaseJkConfig  { 
+
+    public static final String WORKERS_CONFIG = "/conf/jk/workers.properties";
+    public static final String URI_WORKERS_MAP_CONFIG = "/conf/auto/uriworkermap.properties";
+    public static final String ISAPI_LOG_LOCATION = "/logs/iis_redirect.log";
+    public static final String ISAPI_REG_FILE = "/conf/auto/iis_redirect.reg";    
+
+    private File regConfig = null;
+    private File uriConfig = null;
+
+    public IISConfig() 
+    {
+    }
+
+    //-------------------- Properties --------------------
+    
+    /**
+        set the path to the output file for the auto-generated
+        isapi_redirect registry file.  If this path is relative
+        then getRegConfig() will resolve it absolutely against
+        the getConfigHome() path.
+        <p>
+        @param path String path to a file
+    */
+    public void setRegConfig(String path){
+	regConfig= (path==null)?null:new File(path);
+    }
+
+    /**
+        set a path to the uriworkermap.properties file.
+        @param path String path to uriworkermap.properties file
+    */
+    public void setUriConfig(String path){
+        uriConfig= (path==null?null:new File(path));
+    }
+
+    // -------------------- Initialize/guess defaults --------------------
+
+    /** Initialize defaults for properties that are not set
+	explicitely
+    */
+    protected void initProperties() {
+        super.initProperties();
+
+	regConfig=getConfigFile( regConfig, configHome, ISAPI_REG_FILE);
+	workersConfig=getConfigFile( workersConfig, configHome, WORKERS_CONFIG);
+	uriConfig=getConfigFile( uriConfig, configHome, URI_WORKERS_MAP_CONFIG);
+	jkLog=getConfigFile( jkLog, configHome, ISAPI_LOG_LOCATION);
+    }
+
+    // -------------------- Generate config --------------------
+
+    protected PrintWriter getWriter() throws IOException {
+	String abUriConfig = uriConfig.getAbsolutePath();
+	return new PrintWriter(new FileWriter(abUriConfig,append));        
+    }
+    protected boolean generateJkHead(PrintWriter mod_jk) {
+	try {
+	    PrintWriter regfile = new PrintWriter(new FileWriter(regConfig));
+	    log("Generating IIS registry file = "+regConfig );
+	    generateRegistrySettings(regfile);
+	    regfile.close();
+	} catch(IOException iex) {
+	    log("Unable to generate registry file " +regConfig);
+	    return false;
+	}
+	log("Generating IIS URI worker map file = "+uriConfig );
+	generateUriWorkerHeader(mod_jk);            
+	return true;
+    }
+
+    // -------------------- Config sections  --------------------
+
+    /** Writes the registry settings required by the IIS connector
+     */
+    private void generateRegistrySettings(PrintWriter regfile)
+    {
+        regfile.println("REGEDIT4");
+        regfile.println();
+        regfile.println("[HKEY_LOCAL_MACHINE\\SOFTWARE\\Apache Software Foundation\\Jakarta Isapi Redirector\\1.0]");
+        regfile.println("\"extension_uri\"=\"/jakarta/isapi_redirect.dll\"");
+        regfile.println("\"log_file\"=\"" + dubleSlash(jkLog.toString()) +"\"");
+        regfile.println("\"log_level\"=\"" + jkDebug + "\"");
+        regfile.println("\"worker_file\"=\"" + dubleSlash(workersConfig.toString()) +"\"");
+        regfile.println("\"worker_mount_file\"=\"" + dubleSlash(uriConfig.toString()) +"\"");
+    }
+
+    /** Writes the header information to the uriworkermap file
+     */
+    private void generateUriWorkerHeader(PrintWriter uri_worker)
+    {
+        uri_worker.println("###################################################################");		    
+        uri_worker.println("# Auto generated configuration. Dated: " +  new Date());
+        uri_worker.println("###################################################################");		    
+        uri_worker.println();
+
+        uri_worker.println("#");        
+        uri_worker.println("# Default worker to be used through our mappings");
+        uri_worker.println("#");        
+        uri_worker.println("default.worker=" + jkWorker);        
+        uri_worker.println();
+    }
+
+    /** Forward all requests for a context to tomcat.
+	The default.
+     */
+    protected void generateStupidMappings(Context context, PrintWriter uri_worker )
+    {
+        String ctxPath  = context.getPath();
+	String nPath=("".equals(ctxPath)) ? "/" : ctxPath;
+
+        if( noRoot &&  "".equals(ctxPath) ) {
+            log("Ignoring root context in forward-all mode  ");
+            return;
+        } 
+
+        // map all requests for this context to Tomcat
+        uri_worker.println(nPath +"=$(default.worker)");
+        if( "".equals(ctxPath) ) {
+            uri_worker.println(nPath +"*=$(default.worker)");
+            uri_worker.println(
+                    "# Note: To correctly serve the Tomcat's root context, IIS's Home Directory must");
+            uri_worker.println(
+                    "# must be set to: \"" + getAbsoluteDocBase(context) + "\"");
+        }
+        else
+            uri_worker.println(nPath +"/*=$(default.worker)");
+    }
+
+    protected void generateContextMappings(Context context, PrintWriter uri_worker )
+    {
+        String ctxPath  = context.getPath();
+	String nPath=("".equals(ctxPath)) ? "/" : ctxPath;
+
+        if( noRoot &&  "".equals(ctxPath) ) {
+            log("Ignoring root context in forward-all mode  ");
+            return;
+        } 
+
+        // Static files will be served by IIS
+        uri_worker.println();
+        uri_worker.println("#########################################################");		    
+        uri_worker.println("# Auto configuration for the " + nPath + " context.");
+        uri_worker.println("#########################################################");		    
+        uri_worker.println();
+
+        // Static mappings are not set in uriworkermap, but must be set with IIS admin.
+
+	// InvokerInterceptor - it doesn't have a container,
+	// but it's implemented using a special module.
+
+	// XXX we need to better collect all mappings
+
+	if(context.getLoginConfig() != null) {
+	    String loginPage = context.getLoginConfig().getLoginPage();
+	    if(loginPage != null) {
+		int lpos = loginPage.lastIndexOf("/");
+		String jscurl = loginPage.substring(0,lpos+1) + "j_security_check";
+		addMapping( ctxPath, jscurl, uri_worker);
+	    }
+	}
+		String [] servletMaps=context.findServletMappings();
+	for( int ii=0; ii < servletMaps.length ; ii++) {
+	    addMapping( ctxPath , servletMaps[ii] , uri_worker );
+	}
+    }
+
+    /** Add an IIS extension mapping.
+     */
+    protected boolean addMapping( String ctxPath, String ext,
+					 PrintWriter uri_worker )
+    {
+        if( debug > 0 )
+            log( "Adding extension map for " + ctxPath + "/*." + ext );
+	if(! ext.startsWith("/") )
+	    ext = "/" + ext;
+	if(ext.length() > 1)
+	    uri_worker.println(ctxPath + "/*." + ext + "=$(default.worker)");
+        return true;
+    }
+
+    /** Add a fulling specified IIS mapping.
+     */
+    protected boolean addMapping( String fullPath, PrintWriter uri_worker ) {
+        if( debug > 0 )
+            log( "Adding map for " + fullPath );
+        uri_worker.println(fullPath + "=$(default.worker)" );
+        return true;
+    }
+
+    // -------------------- Utils --------------------
+
+    private String dubleSlash(String in) 
+    {
+        StringBuffer sb = new StringBuffer();
+        
+        for(int i = 0 ; i < in.length() ; i++) {
+            char ch = in.charAt(i);
+            if('\\' == ch) {
+                sb.append("\\\\");
+            } else {
+                sb.append(ch);
+            }
+        }
+        
+        return sb.toString();
+    }
+
+}
diff --git a/connectors/jk/java/org/apache/jk/config/NSConfig.java b/connectors/jk/java/org/apache/jk/config/NSConfig.java
new file mode 100644
index 0000000..435e564
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/config/NSConfig.java
@@ -0,0 +1,326 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.config;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Date;
+
+import org.apache.catalina.Context;
+
+
+/**
+    Generates automatic Netscape nsapi_redirect configurations based on
+    the Tomcat server.xml settings and the war contexts
+    initialized during startup.
+    <p>
+    This config interceptor is enabled by inserting an NSConfig
+    element in the <b>&lt;ContextManager&gt;</b> tag body inside
+    the server.xml file like so:
+    <pre>
+    * < ContextManager ... >
+    *   ...
+    *   <<b>NSConfig</b> <i>options</i> />
+    *   ...
+    * < /ContextManager >
+    </pre>
+    where <i>options</i> can include any of the following attributes:
+    <ul>
+     <li><b>configHome</b> - default parent directory for the following paths.
+                            If not set, this defaults to TOMCAT_HOME. Ignored
+                            whenever any of the following paths is absolute.
+                             </li>
+     <li><b>objConfig</b> - path to use for writing Netscape obj.conf
+                            file. If not set, defaults to
+                            "conf/auto/obj.conf".</li>
+     <li><b>objectName</b> - Name of the Object to execute the requests.
+                             Defaults to "servlet".</li>
+     <li><b>workersConfig</b> - path to workers.properties file used by 
+                                nsapi_redirect. If not set, defaults to
+                                "conf/jk/workers.properties".</li>
+     <li><b>nsapiJk</b> - path to Netscape mod_jk plugin file.  If not set,
+                        defaults to "bin/nsapi_redirect.dll" on windows,
+                        "bin/nsapi_rd.nlm" on netware, and
+                        "bin/nsapi_redirector.so" everywhere else.</li>
+     <li><b>jkLog</b> - path to log file to be used by nsapi_redirect.</li>
+     <li><b>jkDebug</b> - Loglevel setting.  May be debug, info, error, or emerg.
+                          If not set, defaults to emerg.</li>
+     <li><b>jkWorker</b> The desired worker.  Must be set to one of the workers
+                         defined in the workers.properties file. "ajp12", "ajp13"
+                         or "inprocess" are the workers found in the default
+                         workers.properties file. If not specified, defaults
+                         to "ajp13" if an Ajp13Interceptor is in use, otherwise
+                         it defaults to "ajp12".</li>
+     <li><b>forwardAll</b> - If true, forward all requests to Tomcat. This helps
+                             insure that all the behavior configured in the web.xml
+                             file functions correctly.  If false, let Netscape serve
+                             static resources assuming it has been configured
+                             to do so. The default is true.
+                             Warning: When false, some configuration in
+                             the web.xml may not be duplicated in Netscape.
+                             Review the uriworkermap file to see what
+                             configuration is actually being set in Netscape.</li>
+     <li><b>noRoot</b> - If true, the root context is not mapped to
+                         Tomcat.  If false and forwardAll is true, all requests
+                         to the root context are mapped to Tomcat. If false and
+                         forwardAll is false, only JSP and servlets requests to
+                         the root context are mapped to Tomcat. When false,
+                         to correctly serve Tomcat's root context you must also
+                         modify the Home Directory setting in Netscape
+                         to point to Tomcat's root context directory.
+                         Otherwise some content, such as the root index.html,
+                         will be served by Netscape before nsapi_redirect gets a chance
+                         to claim the request and pass it to Tomcat.
+                         The default is true.</li>
+    </ul>
+  <p>
+    @author Costin Manolache
+    @author Larry Isaacs
+    @author Gal Shachor
+    @author Bill Barker
+ */
+public class NSConfig  extends BaseJkConfig { 
+
+    public static final String WORKERS_CONFIG = "/conf/jk/workers.properties";
+    public static final String NS_CONFIG = "/conf/auto/obj.conf";
+    public static final String NSAPI_LOG_LOCATION = "/logs/nsapi_redirect.log";
+    /** default location of nsapi plug-in. */
+    public static final String NSAPI_REDIRECTOR;
+    
+    //set up some defaults based on OS type
+    static{
+        String os = System.getProperty("os.name").toLowerCase();
+        if(os.indexOf("windows")>=0){
+           NSAPI_REDIRECTOR = "bin/nsapi_redirect.dll";
+        }else if(os.indexOf("netware")>=0){
+           NSAPI_REDIRECTOR = "bin/nsapi_rd.nlm";
+        }else{
+           NSAPI_REDIRECTOR = "bin/nsapi_redirector.so";
+        }
+    }
+
+    private File objConfig = null;
+    private File nsapiJk = null;
+    private String objectName = "servlet";
+
+    public NSConfig() 
+    {
+    }
+
+    //-------------------- Properties --------------------
+    
+    /**
+        set the path to the output file for the auto-generated
+        isapi_redirect registry file.  If this path is relative
+        then getRegConfig() will resolve it absolutely against
+        the getConfigHome() path.
+        <p>
+        @param path String path to a file
+    */
+    public void setObjConfig(String path) {
+	objConfig= (path==null)?null:new File(path);
+    }
+
+    /**
+        set the path to the nsapi plugin module
+        @param path String path to a file
+    */
+    public void setNsapiJk(String path) {
+        nsapiJk=( path==null?null:new File(path));
+    }
+
+    /**
+        Set the name for the Object that implements the
+        jk_service call.
+        @param name Name of the obj.conf Object
+    */
+    public void setObjectName(String name) {
+        objectName = name;
+    }
+
+    // -------------------- Initialize/guess defaults --------------------
+
+    /** Initialize defaults for properties that are not set
+	explicitely
+    */
+    protected void initProperties() {
+        super.initProperties();
+
+	objConfig=getConfigFile( objConfig, configHome, NS_CONFIG);
+	workersConfig=getConfigFile( workersConfig, configHome, WORKERS_CONFIG);
+
+	if( nsapiJk == null )
+	    nsapiJk=new File(NSAPI_REDIRECTOR);
+	else
+	    nsapiJk =getConfigFile( nsapiJk, configHome, NSAPI_REDIRECTOR );
+	jkLog=getConfigFile( jkLog, configHome, NSAPI_LOG_LOCATION);
+    }
+
+    // -------------------- Generate config --------------------
+    protected PrintWriter getWriter() throws IOException {
+	String abObjConfig = objConfig.getAbsolutePath();
+	return new PrintWriter(new FileWriter(abObjConfig,append));
+    }
+    protected boolean generateJkHead(PrintWriter mod_jk) {
+	log("Generating netscape web server config = "+objConfig );
+	
+	generateNsapiHead( mod_jk );
+	
+	mod_jk.println("<Object name=default>");
+	return true;
+    }
+
+    private void generateNsapiHead(PrintWriter objfile)
+    {
+        objfile.println("###################################################################");		    
+        objfile.println("# Auto generated configuration. Dated: " +  new Date());
+        objfile.println("###################################################################");		    
+        objfile.println();
+
+        objfile.println("#");        
+        objfile.println("# You will need to merge the content of this file with your ");
+        objfile.println("# regular obj.conf and then restart (=stop + start) your Netscape server. ");
+        objfile.println("#");        
+        objfile.println();
+            
+        objfile.println("#");                    
+        objfile.println("# Loading the redirector into your server");
+        objfile.println("#");        
+        objfile.println();            
+        objfile.println("Init fn=\"load-modules\" funcs=\"jk_init,jk_service\" shlib=\"<put full path to the redirector here>\"");
+        objfile.println("Init fn=\"jk_init\" worker_file=\"" + 
+                        workersConfig.toString().replace('\\', '/') +  
+                        "\" log_level=\"" + jkDebug + "\" log_file=\"" + 
+                        jkLog.toString().replace('\\', '/') + 
+                        "\"");
+        objfile.println();
+    }
+
+    protected void generateJkTail(PrintWriter objfile)
+    {
+        objfile.println();
+        objfile.println("#######################################################");		    
+        objfile.println("# Protecting the WEB-INF and META-INF directories.");
+        objfile.println("#######################################################");		    
+        objfile.println("PathCheck fn=\"deny-existence\" path=\"*/WEB-INF/*\""); 
+        objfile.println("PathCheck fn=\"deny-existence\" path=\"*/META-INF/*\""); 
+        objfile.println();
+
+        objfile.println("</Object>");            
+        objfile.println();
+
+        objfile.println("#######################################################");		    
+        objfile.println("# New object to execute your servlet requests.");
+        objfile.println("#######################################################");		    
+        objfile.println("<Object name=" + objectName + ">");
+        objfile.println("ObjectType fn=force-type type=text/html");
+        objfile.println("Service fn=\"jk_service\" worker=\""+ jkWorker + "\" path=\"/*\"");
+        objfile.println("</Object>");
+        objfile.println();
+    }
+
+    // -------------------- Forward all mode --------------------
+    
+    /** Forward all requests for a context to tomcat.
+	The default.
+     */
+    protected void generateStupidMappings(Context context, PrintWriter objfile )
+    {
+        String ctxPath  = context.getPath();
+	String nPath=("".equals(ctxPath)) ? "/" : ctxPath;
+
+        if( noRoot &&  "".equals(ctxPath) ) {
+            log("Ignoring root context in forward-all mode  ");
+            return;
+        } 
+	objfile.println("<Object name=" + context.getName() + ">");
+
+        objfile.println("NameTrans fn=\"assign-name\" from=\"" + ctxPath + "\" name=\"" + objectName + "\""); 
+        objfile.println("NameTrans fn=\"assign-name\" from=\"" + ctxPath + "/*\" name=\"" + objectName + "\""); 
+	objfile.println("</Object>");
+    }
+
+
+    // -------------------- Netscape serves static mode --------------------
+    // This is not going to work for all apps. We fall back to stupid mode.
+    
+    protected void generateContextMappings(Context context, PrintWriter objfile )
+    {
+        String ctxPath  = context.getPath();
+	String nPath=("".equals(ctxPath)) ? "/" : ctxPath;
+
+        if( noRoot &&  "".equals(ctxPath) ) {
+            log("Ignoring root context in non-forward-all mode  ");
+            return;
+        } 
+	objfile.println("<Object name=" + context.getName() + ">");
+        // Static files will be served by Netscape
+        objfile.println("#########################################################");		    
+        objfile.println("# Auto configuration for the " + nPath + " context starts.");
+        objfile.println("#########################################################");		    
+        objfile.println();
+
+        // XXX Need to determine what if/how static mappings are done
+
+	// InvokerInterceptor - it doesn't have a container,
+	// but it's implemented using a special module.
+	
+	// XXX we need to better collect all mappings
+	if(context.getLoginConfig() != null) {
+	    String loginPage = context.getLoginConfig().getLoginPage();
+	    if(loginPage != null) {
+		int lpos = loginPage.lastIndexOf("/");
+		String jscurl = loginPage.substring(0,lpos+1) + "j_security_check";
+		addMapping( ctxPath, jscurl, objfile);
+	    }
+	}
+	
+	String [] servletMaps=context.findServletMappings();
+	for(int ii=0; ii < servletMaps.length; ii++) {
+	    addMapping( ctxPath , servletMaps[ii] , objfile );
+	}
+	objfile.println("</Object>");
+    }
+
+    /** Add a Netscape extension mapping.
+     */
+    protected boolean addMapping( String ctxPath, String ext,
+					 PrintWriter objfile )
+    {
+        if( debug > 0 )
+            log( "Adding extension map for " + ctxPath + "/*." + ext );
+	if(! ext.startsWith("/") )
+	    ext = "/" + ext;
+	if(ext.length() > 1)
+	    objfile.println("NameTrans fn=\"assign-name\" from=\"" +
+                    ctxPath  + ext + "\" name=\"" + objectName + "\""); 
+	return true;
+    }
+
+    /** Add a fulling specified Netscape mapping.
+     */
+    protected boolean addMapping( String fullPath, PrintWriter objfile ) {
+        if( debug > 0 )
+            log( "Adding map for " + fullPath );
+        objfile.println("NameTrans fn=\"assign-name\" from=\"" +
+                        fullPath + "\" name=\"" + objectName + "\""); 
+	return true;
+    }
+
+}
diff --git a/connectors/jk/java/org/apache/jk/config/WebXml2Jk.java b/connectors/jk/java/org/apache/jk/config/WebXml2Jk.java
new file mode 100644
index 0000000..8e8aa13
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/config/WebXml2Jk.java
@@ -0,0 +1,451 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.config;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+
+/* Naming conventions:
+
+JK_CONF_DIR == serverRoot/work  ( XXX /jkConfig ? )
+
+- Each vhost has a sub-dir named after the canonycal name
+
+- For each webapp in a vhost, there is a separate WEBAPP_NAME.jkmap
+
+- In httpd.conf ( or equivalent servers ), in each virtual host you
+should "Include JK_CONF_DIR/VHOST/jk_apache.conf". The config
+file will contain the Alias declarations and other rules required
+for apache operation. Same for other servers. 
+
+- WebXml2Jk will be invoked by a config tool or automatically for each
+webapp - it'll generate the WEBAPP.jkmap files and config fragments.
+
+WebXml2Jk will _not_ generate anything else but mappings.
+It should _not_ try to guess locations or anything else - that's
+another components' job.
+
+*/
+
+/**
+ * Read a web.xml file and generate the mappings for jk2.
+ * It can be used from the command line or ant.
+ * 
+ * In order for the web server to serve static pages, all webapps
+ * must be deployed on the computer that runs Apache, IIS, etc.
+ *
+ * Dynamic pages can be executed on that computer or other servers
+ * in a pool, but even if the main server doesn't run tomcat,
+ * it must have all the static files and WEB-INF/web.xml.
+ * ( you could have a script remove everything else, including jsps - if
+ *  security paranoia is present ).
+ *
+ * XXX We could have this in WEB-INF/urimap.properties.
+ *
+ * @author Costin Manolache
+ */
+public class WebXml2Jk {
+    String vhost="";
+    String cpath="";
+    String docBase;
+    String file;
+    String worker="lb"; 
+
+    // -------------------- Settings -------------------- 
+
+    // XXX We can also generate location-independent mappings.
+    
+    /** Set the canonycal name of the virtual host.
+     */
+    public void setHost( String vhost ) {
+        this.vhost=vhost; 
+    }
+    
+    /** Set the canonical name of the virtual host.
+     */
+    public void setContext( String contextPath ) {
+        this.cpath=contextPath;
+    }
+
+    
+    /** Set the base directory where the application is
+     *  deployed ( on the web server ).
+     */
+    public void setDocBase(String docBase ) {
+        this.docBase=docBase;
+    }
+
+    // Automatically generated.
+//     /** The file where the jk2 mapping will be generated
+//      */
+//     public void setJk2Conf( String outFile ) {
+//         file=outFile;
+//         type=CONFIG_JK2_URIMAP;
+//     }
+
+//     /** Backward compat: generate JkMounts for mod_jk1
+//      */
+//     public void setJkmountFile( String outFile ) {
+//         file=outFile;
+//         type=CONFIG_JK_MOUNT;
+//     }
+
+    /* By default we map to the lb - in jk2 this is automatically
+     * created and includes all  tomcat instances.
+     *
+     * This is equivalent to the worker in jk1.
+     */
+    public void setGroup(String route ) {
+        worker=route;
+    }
+
+    // -------------------- Generators --------------------
+    public static interface MappingGenerator {
+        void setWebXmlReader(WebXml2Jk wxml );
+
+        /** Start section( vhost declarations, etc )
+         */
+        void generateStart() throws IOException ;
+
+        void generateEnd() throws IOException ;
+        
+        void generateServletMapping( String servlet, String url )throws IOException ;
+        void generateFilterMapping( String servlet, String url ) throws IOException ;
+
+        void generateLoginConfig( String loginPage,
+                                  String errPage, String authM ) throws IOException ;
+
+        void generateErrorPage( int err, String location ) throws IOException ;
+            
+        void generateConstraints( Vector urls, Vector methods, Vector roles, boolean isSSL ) throws IOException ;
+    }    
+    
+    // -------------------- Implementation --------------------
+    Node webN;
+    File jkDir;
+    
+    /** Return the top level node
+     */
+    public Node getWebXmlNode() {
+        return webN;
+    }
+
+    public File getJkDir() {
+        return jkDir;
+    }
+    
+    /** Extract the wellcome files from the web.xml
+     */
+    public Vector getWellcomeFiles() {
+        Node n0=getChild( webN, "welcome-file-list" );
+        Vector wF=new Vector();
+        if( n0!=null ) {
+            for( Node mapN=getChild( webN, "welcome-file" );
+                 mapN != null; mapN = getNext( mapN ) ) {
+                wF.addElement( getContent(mapN));
+            }
+        }
+        // XXX Add index.html, index.jsp
+        return wF;
+    }
+
+
+    void generate(MappingGenerator gen ) throws IOException {
+        gen.generateStart();
+        log.info("Generating mappings for servlets " );
+        for( Node mapN=getChild( webN, "servlet-mapping" );
+             mapN != null; mapN = getNext( mapN ) ) {
+            
+            String serv=getChildContent( mapN, "servlet-name");
+            String url=getChildContent( mapN, "url-pattern");
+            
+            gen.generateServletMapping( serv, url );
+        }
+
+        log.info("Generating mappings for filters " );
+        for( Node mapN=getChild( webN, "filter-mapping" );
+             mapN != null; mapN = getNext( mapN ) ) {
+            
+            String filter=getChildContent( mapN, "filter-name");
+            String url=getChildContent( mapN, "url-pattern");
+
+            gen.generateFilterMapping(  filter, url );
+        }
+
+
+        for( Node mapN=getChild( webN, "error-page" );
+             mapN != null; mapN = getNext( mapN ) ) {
+            String errorCode= getChildContent( mapN, "error-code" );
+            String location= getChildContent( mapN, "location" );
+
+            if( errorCode!=null && ! "".equals( errorCode ) ) {
+                try {
+                    int err=new Integer( errorCode ).intValue();
+                    gen.generateErrorPage(  err, location );
+                } catch( Exception ex ) {
+                    log.error( "Format error " + location, ex);
+                }
+            }
+        }
+
+        Node lcN=getChild( webN, "login-config" );
+        if( lcN!=null ) {
+            log.info("Generating mapping for login-config " );
+            
+            String authMeth=getContent( getChild( lcN, "auth-method"));
+            String realm=getContent( getChild( lcN, "realm-name"));
+            if( authMeth == null ) authMeth="BASIC";
+
+            Node n1=getChild( lcN, "form-login-config");
+            String loginPage= getChildContent( n1, "form-login-page");
+            String errPage= getChildContent( n1, "form-error-page");
+
+	    if(loginPage != null) {
+		int lpos = loginPage.lastIndexOf("/");
+		String jscurl = loginPage.substring(0,lpos+1) + "j_security_check";
+                gen.generateLoginConfig( jscurl, errPage, authMeth );
+	    }
+        }
+
+        log.info("Generating mappings for security constraints " );
+        for( Node mapN=getChild( webN, "security-constraint" );
+             mapN != null; mapN = getNext( mapN )) {
+
+            Vector methods=new Vector();
+            Vector urls=new Vector();
+            Vector roles=new Vector();
+            boolean isSSL=false;
+            
+            Node wrcN=getChild( mapN, "web-resource-collection");
+            for( Node uN=getChild(wrcN, "http-method");
+                 uN!=null; uN=getNext( uN )) {
+                methods.addElement( getContent( uN ));
+            }
+            for( Node uN=getChild(wrcN, "url-pattern");
+                 uN!=null; uN=getNext( uN )) {
+                urls.addElement( getContent( uN ));
+            }
+
+            // Not used at the moment
+            Node acN=getChild( mapN, "auth-constraint");
+            for( Node rN=getChild(acN, "role-name");
+                 rN!=null; rN=getNext( rN )) {
+                roles.addElement(getContent( rN ));
+            }
+
+            Node ucN=getChild( mapN, "user-data-constraint");
+            String transp=getContent(getChild( ucN, "transport-guarantee"));
+            if( transp!=null ) {
+                if( "INTEGRAL".equalsIgnoreCase( transp ) ||
+                    "CONFIDENTIAL".equalsIgnoreCase( transp ) ) {
+                    isSSL=true;
+                }
+            }
+
+            gen.generateConstraints( urls, methods, roles, isSSL );
+        }
+        gen.generateEnd();
+    }
+    
+    // -------------------- Main and ant wrapper --------------------
+    
+    public void execute() {
+        try {
+            if( docBase== null) {
+                log.error("No docbase - please specify the base directory of you web application ( -docBase PATH )");
+                return;
+            }
+            if( cpath== null) {
+                log.error("No context - please specify the mount ( -context PATH )");
+                return;
+            }
+            File docbF=new File(docBase);
+            File wXmlF=new File( docBase, "WEB-INF/web.xml");
+
+            Document wXmlN=readXml(wXmlF);
+            if( wXmlN == null ) return;
+
+            webN = wXmlN.getDocumentElement();
+            if( webN==null ) {
+                log.error("Can't find web-app");
+                return;
+            }
+
+            jkDir=new File( docbF, "WEB-INF/jk2" );
+            jkDir.mkdirs();
+            
+            MappingGenerator generator=new GeneratorJk2();
+            generator.setWebXmlReader( this );
+            generate( generator );
+
+            generator=new GeneratorJk1();
+            generator.setWebXmlReader( this );
+            generate( generator );
+
+            generator=new GeneratorApache2();
+            generator.setWebXmlReader( this );
+            generate( generator );
+
+        } catch( Exception ex ) {
+            ex.printStackTrace();
+        }
+    }
+
+
+    public static void main(String args[] ) {
+        try {
+            if( args.length == 1 &&
+                ( "-?".equals(args[0]) || "-h".equals( args[0])) ) {
+                System.out.println("Usage: ");
+                System.out.println("  WebXml2Jk [OPTIONS]");
+                System.out.println();
+                System.out.println("  -docBase DIR        The location of the webapp. Required");
+                System.out.println("  -group GROUP        Group, if you have multiple tomcats with diffrent content. " );
+                System.out.println("                      The default is 'lb', and should be used in most cases");
+                System.out.println("  -host HOSTNAME      Canonical hostname - for virtual hosts");
+                System.out.println("  -context /CPATH     Context path where the app will be mounted");
+                return;
+            }
+
+            WebXml2Jk w2jk=new WebXml2Jk();
+
+            /* do ant-style property setting */
+            IntrospectionUtils.processArgs( w2jk, args, new String[] {},
+                                            null, new Hashtable());
+            w2jk.execute();
+        } catch( Exception ex ) {
+            ex.printStackTrace();
+        }
+
+    }
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( WebXml2Jk.class );
+
+    
+    // -------------------- DOM utils --------------------
+
+    /** Get the content of a node
+     */
+    public static String getContent(Node n ) {
+        if( n==null ) return null;
+        Node n1=n.getFirstChild();
+        // XXX Check if it's a text node
+
+        String s1=n1.getNodeValue();
+        return s1.trim();
+    }
+    
+    /** Get the first child
+     */
+    public static Node getChild( Node parent, String name ) {
+        if( parent==null ) return null;
+        Node first=parent.getFirstChild();
+        if( first==null ) return null;
+        for (Node node = first; node != null;
+             node = node.getNextSibling()) {
+            //System.out.println("getNode: " + name + " " + node.getNodeName());
+            if( name.equals( node.getNodeName() ) ) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    /** Get the first child's content ( i.e. it's included TEXT node )
+     */
+    public static String getChildContent( Node parent, String name ) {
+        Node first=parent.getFirstChild();
+        if( first==null ) return null;
+        for (Node node = first; node != null;
+             node = node.getNextSibling()) {
+            //System.out.println("getNode: " + name + " " + node.getNodeName());
+            if( name.equals( node.getNodeName() ) ) {
+                return getContent( node );
+            }
+        }
+        return null;
+    }
+
+    /** Get the node in the list of siblings
+     */
+    public static Node getNext( Node current ) {
+        Node first=current.getNextSibling();
+        String name=current.getNodeName();
+        if( first==null ) return null;
+        for (Node node = first; node != null;
+             node = node.getNextSibling()) {
+            //System.out.println("getNode: " + name + " " + node.getNodeName());
+            if( name.equals( node.getNodeName() ) ) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    public static class NullResolver implements EntityResolver {
+        public InputSource resolveEntity (String publicId,
+                                                   String systemId)
+            throws SAXException, IOException
+        {
+            System.out.println("ResolveEntity: " + publicId + " " + systemId);
+            return new InputSource(new StringReader(""));
+        }
+    }
+    
+    public static Document readXml(File xmlF)
+        throws SAXException, IOException, ParserConfigurationException
+    {
+        if( ! xmlF.exists() ) {
+            log.error("No xml file " + xmlF );
+            return null;
+        }
+        DocumentBuilderFactory dbf =
+            DocumentBuilderFactory.newInstance();
+
+        dbf.setValidating(false);
+        dbf.setIgnoringComments(false);
+        dbf.setIgnoringElementContentWhitespace(true);
+        //dbf.setCoalescing(true);
+        //dbf.setExpandEntityReferences(true);
+
+        DocumentBuilder db = null;
+        db = dbf.newDocumentBuilder();
+        db.setEntityResolver( new NullResolver() );
+        
+        // db.setErrorHandler( new MyErrorHandler());
+
+        Document doc = db.parse(xmlF);
+        return doc;
+    }
+
+}
diff --git a/connectors/jk/java/org/apache/jk/core/JkChannel.java b/connectors/jk/java/org/apache/jk/core/JkChannel.java
new file mode 100644
index 0000000..570af2b
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/core/JkChannel.java
@@ -0,0 +1,80 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.core;
+
+import java.io.IOException;
+
+import javax.management.ObjectName;
+
+import org.apache.commons.modeler.Registry;
+import org.apache.coyote.Request;
+import org.apache.tomcat.util.threads.ThreadPool;
+
+/**
+ * A Channel represents a connection point to the outside world.
+ *
+ * @author Bill Barker
+ */
+
+public interface JkChannel {
+
+
+    /**
+     * Return the identifying name of this Channel.
+     */
+    public String getChannelName();
+
+    /**
+     * Send a message back to the client.
+     * @param msg The message to send.
+     * @param ep The connection point for this request.
+     */
+    public int send(Msg msg, MsgContext ep) throws IOException;
+
+    /**
+     * Recieve a message from the client.
+     * @param msg The place to recieve the data into.
+     * @param ep The connection point for this request.
+     */
+    public  int receive(Msg msg, MsgContext ep) throws IOException;
+
+    /**
+     * Flush the data to the client.
+     */
+    public int flush(Msg msg, MsgContext ep) throws IOException;
+
+    /**
+     * Invoke the request chain.
+     */
+    public int invoke(Msg msg, MsgContext ep) throws IOException;
+
+    /**
+     * Confirm that a shutdown request was recieved form us.
+     */
+    public boolean isSameAddress(MsgContext ep);
+
+    /**
+     * Register a new Request in the Request pool.
+     */
+    public void registerRequest(Request req, MsgContext ep, int count);
+
+    /**
+     * Create a new request endpoint.
+     */
+    public MsgContext createMsgContext();
+
+}
diff --git a/connectors/jk/java/org/apache/jk/core/JkHandler.java b/connectors/jk/java/org/apache/jk/core/JkHandler.java
new file mode 100644
index 0000000..0d00de6
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/core/JkHandler.java
@@ -0,0 +1,196 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.core;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+
+import org.apache.commons.modeler.Registry;
+
+/**
+ *
+ * @author Costin Manolache
+ */
+public class JkHandler implements MBeanRegistration, NotificationListener {
+    public static final int OK=0;
+    public static final int LAST=1;
+    public static final int ERROR=2;
+
+    protected Properties properties=new Properties();
+    protected WorkerEnv wEnv;
+    protected JkHandler next;
+    protected String nextName=null;
+    protected String name;
+    protected int id;
+
+    // XXX Will be replaced with notes and (configurable) ids
+    // Each represents a 'chain' - similar with ActionCode in Coyote ( the concepts
+    // will be merged ).    
+    public static final int HANDLE_RECEIVE_PACKET   = 10;
+    public static final int HANDLE_SEND_PACKET      = 11;
+    public static final int HANDLE_FLUSH            = 12;
+    public static final int HANDLE_THREAD_END       = 13;
+    
+    public void setWorkerEnv( WorkerEnv we ) {
+        this.wEnv=we;
+    }
+
+    /** Set the name of the handler. Will allways be called by
+     *  worker env after creating the worker.
+     */
+    public void setName(String s ) {
+        name=s;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    /** Set the id of the worker. We use an id for faster dispatch.
+     *  Since we expect a decent number of handler in system, the
+     *  id is unique - that means we may have to allocate bigger
+     *  dispatch tables. ( easy to fix if needed )
+     */
+    public void setId( int id ) {
+        this.id=id;
+    }
+
+    public int getId() {
+        return id;
+    }
+    
+    /** Catalina-style "recursive" invocation.
+     *  A chain is used for Apache/3.3 style iterative invocation.
+     */
+    public void setNext( JkHandler h ) {
+        next=h;
+    }
+
+    public void setNext( String s ) {
+        nextName=s;
+    }
+
+    public String getNext() {
+        if( nextName==null ) {
+            if( next!=null)
+                nextName=next.getName();
+        }
+        return nextName;
+    }
+
+    /** Should register the request types it can handle,
+     *   same style as apache2.
+     */
+    public void init() throws IOException {
+    }
+
+    /** Clean up and stop the handler
+     */
+    public void destroy() throws IOException {
+    }
+
+    public MsgContext createMsgContext() {
+        return new MsgContext();
+    }
+    
+    public int invoke(Msg msg, MsgContext mc )  throws IOException {
+        return OK;
+    }
+    
+    public void setProperty( String name, String value ) {
+        properties.put( name, value );
+    }
+
+    public String getProperty( String name ) {
+        return properties.getProperty(name) ;
+    }
+
+    /** Experimental, will be replaced. This allows handlers to be
+     *  notified when other handlers are added.
+     */
+    public void addHandlerCallback( JkHandler w ) {
+
+    }
+
+    public void handleNotification(Notification notification, Object handback)
+    {
+//        BaseNotification bNot=(BaseNotification)notification;
+//        int code=bNot.getCode();
+//
+//        MsgContext ctx=(MsgContext)bNot.getSource();
+
+
+    }
+
+    protected String domain;
+    protected ObjectName oname;
+    protected MBeanServer mserver;
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public ObjectName preRegister(MBeanServer server,
+                                  ObjectName oname) throws Exception {
+        this.oname=oname;
+        mserver=server;
+        domain=oname.getDomain();
+        if( name==null ) {
+            name=oname.getKeyProperty("name");
+        }
+        
+        // we need to create a workerEnv or set one.
+        ObjectName wEnvName=new ObjectName(domain + ":type=JkWorkerEnv");
+        if ( wEnv == null ) {
+            wEnv=new WorkerEnv();
+        }
+        if( ! mserver.isRegistered(wEnvName )) {
+            Registry.getRegistry().registerComponent(wEnv, wEnvName, null);
+        }
+        mserver.invoke( wEnvName, "addHandler", 
+                new Object[] {name, this}, 
+                new String[] {"java.lang.String", 
+                              "org.apache.jk.core.JkHandler"});
+        return oname;
+    }
+    
+    public void postRegister(Boolean registrationDone) {
+    }
+
+    public void preDeregister() throws Exception {
+    }
+
+    public void postDeregister() {
+    }
+
+    public void pause() throws Exception {
+    }
+
+    public void resume() throws Exception {
+    }
+
+}
diff --git a/connectors/jk/java/org/apache/jk/core/Msg.java b/connectors/jk/java/org/apache/jk/core/Msg.java
new file mode 100644
index 0000000..8176dc0
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/core/Msg.java
@@ -0,0 +1,153 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.core;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+
+
+/**
+ * A single packet for communication between the web server and the
+ * container.
+ *
+ * In a more generic sense, it's the event that drives the processing chain.
+ * XXX Use Event, make Msg a particular case.
+ *
+ * @author Henri Gomez [hgomez@apache.org]
+ * @author Dan Milstein [danmil@shore.net]
+ * @author Keith Wannamaker [Keith@Wannamaker.org]
+ * @author Kevin Seguin
+ * @author Costin Manolache
+ */
+public abstract class Msg {
+
+    
+    
+    /**
+     * Prepare this packet for accumulating a message from the container to
+     * the web server.  Set the write position to just after the header
+     * (but leave the length unwritten, because it is as yet unknown).
+     */
+    public abstract void reset();
+
+    /**
+     * For a packet to be sent to the web server, finish the process of
+     * accumulating data and write the length of the data payload into
+     * the header.  
+     */
+    public abstract void end();
+
+    public abstract  void appendInt( int val );
+
+    public abstract void appendByte( int val );
+	
+    public abstract void appendLongInt( int val );
+
+    /**
+     */
+    public abstract void appendBytes(MessageBytes mb) throws IOException;
+
+    public abstract void appendByteChunk(ByteChunk bc) throws IOException;
+    
+    /** 
+     * Copy a chunk of bytes into the packet, starting at the current
+     * write position.  The chunk of bytes is encoded with the length
+     * in two bytes first, then the data itself, and finally a
+     * terminating \0 (which is <B>not</B> included in the encoded
+     * length).
+     *
+     * @param b The array from which to copy bytes.
+     * @param off The offset into the array at which to start copying
+     * @param numBytes The number of bytes to copy.  
+     */
+    public abstract void appendBytes( byte b[], int off, int numBytes );
+
+    /**
+     * Read an integer from packet, and advance the read position past
+     * it.  Integers are encoded as two unsigned bytes with the
+     * high-order byte first, and, as far as I can tell, in
+     * little-endian order within each byte.  
+     */
+    public abstract int getInt();
+
+    public abstract int peekInt();
+
+    public abstract byte getByte();
+
+    public abstract byte peekByte();
+
+    public abstract void getBytes(MessageBytes mb);
+    
+    /**
+     * Copy a chunk of bytes from the packet into an array and advance
+     * the read position past the chunk.  See appendBytes() for details
+     * on the encoding.
+     *
+     * @return The number of bytes copied.
+     */
+    public abstract int getBytes(byte dest[]);
+
+    /**
+     * Read a 32 bits integer from packet, and advance the read position past
+     * it.  Integers are encoded as four unsigned bytes with the
+     * high-order byte first, and, as far as I can tell, in
+     * little-endian order within each byte.
+     */
+    public abstract int getLongInt();
+
+    public abstract int getHeaderLength();
+
+    public abstract int processHeader();
+
+    public abstract byte[] getBuffer();
+
+    public abstract int getLen();
+    
+    public abstract void dump(String msg);
+
+    /* -------------------- Utilities -------------------- */
+    // XXX Move to util package
+
+    public static String hexLine( byte buf[], int start, int len ) {
+        StringBuffer sb=new StringBuffer();
+        for( int i=start; i< start+16 ; i++ ) {
+            if( i < len + 4)
+                sb.append( hex( buf[i] ) + " ");
+            else
+                sb.append( "   " );
+        }
+        sb.append(" | ");
+        for( int i=start; i < start+16 && i < len + 4; i++ ) {
+            if( ! Character.isISOControl( (char)buf[i] ))
+                sb.append( new Character((char)buf[i]) );
+            else
+                sb.append( "." );
+        }
+        return sb.toString();
+    }
+
+    private  static String hex( int x ) {
+        //	    if( x < 0) x=256 + x;
+        String h=Integer.toHexString( x );
+        if( h.length() == 1 ) h = "0" + h;
+        return h.substring( h.length() - 2 );
+    }
+
+
+}
diff --git a/connectors/jk/java/org/apache/jk/core/MsgContext.java b/connectors/jk/java/org/apache/jk/core/MsgContext.java
new file mode 100644
index 0000000..efdbbae
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/core/MsgContext.java
@@ -0,0 +1,189 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.core;
+
+import java.io.IOException;
+
+
+/**
+ *
+ * @author Henri Gomez [hgomez@apache.org]
+ * @author Dan Milstein [danmil@shore.net]
+ * @author Keith Wannamaker [Keith@Wannamaker.org]
+ * @author Kevin Seguin
+ * @author Costin Manolache
+ */
+public class MsgContext {
+    private int type;
+    private Object notes[]=new Object[32];
+    private JkHandler next;
+    private JkChannel source;
+    private Object req;
+    private WorkerEnv wEnv;
+    private Msg msgs[]=new Msg[10];
+    private int status=0;
+    // Control object
+    private Object control;
+
+    // Application managed, like notes
+    private long timers[]=new long[20];
+    
+    // The context can be used by JNI components as well
+    private long jkEndpointP;
+    private long xEnvP;
+
+    // Temp: use notes and dynamic strings
+    public static final int TIMER_RECEIVED=0;
+    public static final int TIMER_PRE_REQUEST=1;
+    public static final int TIMER_POST_REQUEST=2;
+    
+    public final Object getNote( int id ) {
+        return notes[id];
+    }
+
+    public final void setNote( int id, Object o ) {
+        notes[id]=o;
+    }
+
+    /** The id of the chain */
+    public final int getType() {
+        return type;
+    }
+
+    public final void setType(int i) {
+        type=i;
+    }
+
+    public final void setLong( int i, long l) {
+        timers[i]=l;
+    }
+    
+    public final long getLong( int i) {
+        return timers[i];
+    }
+    
+    // Common attributes ( XXX should be notes for flexibility ? )
+
+    public final WorkerEnv getWorkerEnv() {
+        return wEnv;
+    }
+
+    public final void setWorkerEnv( WorkerEnv we ) {
+        this.wEnv=we;
+    }
+    
+    public final JkChannel getSource() {
+        return source;
+    }
+    
+    public final void setSource(JkChannel ch) {
+        this.source=ch;
+    }
+
+    public final int getStatus() {
+        return status;
+    }
+
+    public final void setStatus( int s ) {
+        status=s;
+    }
+    
+    public final JkHandler getNext() {
+        return next;
+    }
+    
+    public final void setNext(JkHandler ch) {
+        this.next=ch;
+    }
+
+    /** The high level request object associated with this context
+     */
+    public final void setRequest( Object req ) {
+        this.req=req;
+    }
+
+    public final  Object getRequest() {
+        return req;
+    }
+
+    /** The context may store a number of messages ( buffers + marshalling )
+     */
+    public final Msg getMsg(int i) {
+        return msgs[i];
+    }
+
+    public final void setMsg(int i, Msg msg) {
+        this.msgs[i]=msg;
+    }
+    
+    /** Each context contains a number of byte[] buffers used for communication.
+     *  The C side will contain a char * equivalent - both buffers are long-lived
+     *  and recycled.
+     *
+     *  This will be called at init time. A long-lived global reference to the byte[]
+     *  will be stored in the C context.
+     */
+    public byte[] getBuffer( int id ) {
+        // We use a single buffer right now. 
+        if( msgs[id]==null ) {
+            return null;
+        }
+        return msgs[id].getBuffer();
+    }
+
+    /** Invoke a java hook. The xEnv is the representation of the current execution
+     *  environment ( the jni_env_t * )
+     */
+    public int execute() throws IOException {
+        int status=next.invoke(msgs[0], this);
+        return status;
+    }
+
+    // -------------------- Jni support --------------------
+    
+    /** Store native execution context data when this handler is called
+     *  from JNI. This will change on each call, represent temproary
+     *  call data.
+     */
+    public void setJniEnv( long xEnvP ) {
+            this.xEnvP=xEnvP;
+    }
+
+    public long getJniEnv() {
+        return xEnvP;
+    }
+    
+    /** The long-lived JNI context associated with this java context.
+     *  The 2 share pointers to buffers and cache data to avoid expensive
+     *  jni calls.
+     */
+    public void setJniContext( long cContext ) {
+        this.jkEndpointP=cContext;
+    }
+
+    public long getJniContext() {
+        return jkEndpointP;
+    }
+
+    public Object getControl() {
+        return control;
+    }
+
+    public void setControl(Object control) {
+        this.control = control;
+    }
+}
diff --git a/connectors/jk/java/org/apache/jk/core/WorkerEnv.java b/connectors/jk/java/org/apache/jk/core/WorkerEnv.java
new file mode 100644
index 0000000..5392460
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/core/WorkerEnv.java
@@ -0,0 +1,144 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.core;
+
+import java.util.Hashtable;
+import javax.management.ObjectName;
+
+/**
+ * The controller object. It manages all other jk objects, acting as the root of
+ * the jk object model.
+ *
+ * @author Gal Shachor
+ * @author Henri Gomez [hgomez@apache.org]
+ * @author Dan Milstein [danmil@shore.net]
+ * @author Keith Wannamaker [Keith@Wannamaker.org]
+ * @author Kevin Seguin
+ * @author Costin Manolache
+ */
+public class WorkerEnv {
+
+    Hashtable properties;
+
+    public static final int ENDPOINT_NOTE=0;
+    public static final int REQUEST_NOTE=1;
+    public static final int SSL_CERT_NOTE=16;
+    int noteId[]=new int[4];
+    String noteName[][]=new String[4][];
+    private Object notes[]=new Object[32];
+
+    Hashtable handlersMap=new Hashtable();
+    JkHandler handlersTable[]=new JkHandler[20];
+    int handlerCount=0;
+    
+    // base dir for the jk webapp
+    String home;
+    int localId=0;
+    
+    public WorkerEnv() {
+        for( int i=0; i<noteId.length; i++ ) {
+            noteId[i]=7;
+            noteName[i]=new String[20];
+        }
+    }
+
+    public void setLocalId(int id) {
+        localId=id;
+    }
+    
+    public int getLocalId() {
+        return localId;
+    }
+    
+    public void setJkHome( String s ) {
+        home=s;
+    }
+
+    public String getJkHome() {
+        return home;
+    }
+    
+    public final Object getNote(int i ) {
+        return notes[i];
+    }
+
+    public final void setNote(int i, Object o ) {
+        notes[i]=o;
+    }
+
+    public int getNoteId( int type, String name ) {
+        for( int i=0; i<noteId[type]; i++ ) {
+            if( name.equals( noteName[type][i] ))
+                return i;
+        }
+        int id=noteId[type]++;
+        noteName[type][id]=name;
+        return id;
+    }
+
+    public void addHandler( String name, JkHandler w ) {
+        JkHandler oldH = getHandler(name);
+        if(oldH == w) {
+            // Already added
+            return;
+        }
+        w.setWorkerEnv( this );
+        w.setName( name );
+        handlersMap.put( name, w );
+        if( handlerCount > handlersTable.length ) {
+            JkHandler newT[]=new JkHandler[ 2 * handlersTable.length ];
+            System.arraycopy( handlersTable, 0, newT, 0, handlersTable.length );
+            handlersTable=newT;
+        }
+        if(oldH == null) {
+            handlersTable[handlerCount]=w;
+            w.setId( handlerCount );
+            handlerCount++;
+        } else {
+            handlersTable[oldH.getId()]=w;
+            w.setId(oldH.getId());
+        }
+
+        // Notify all other handlers of the new one
+        // XXX Could be a Coyote action ?
+        for( int i=0; i< handlerCount ; i++ ) {
+            handlersTable[i].addHandlerCallback( w );
+        }
+    }
+
+    public final JkHandler getHandler( String name ) {
+        return (JkHandler)handlersMap.get(name);
+    }
+
+    public final JkHandler getHandler( int id ) {
+        return handlersTable[id];
+    }
+
+    public final int getHandlerCount() {
+        return handlerCount;
+    }
+    
+    public ObjectName[] getHandlersObjectName() {
+        
+        ObjectName onames[]=new ObjectName[ handlerCount ];
+        for( int i=0; i<handlerCount; i++ ) {
+            onames[i]=handlersTable[i].getObjectName();
+        }
+        return onames;
+    }
+
+}
diff --git a/connectors/jk/java/org/apache/jk/core/package.html b/connectors/jk/java/org/apache/jk/core/package.html
new file mode 100644
index 0000000..0257174
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/core/package.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+  <title></title>
+</head>
+<body>
+<h2>Jk2 interfaces</h2>
+<p>Core interfaces for jk2, similar with the interfaces defined in the C
+side ( jk2/include ).<br>
+</p>
+<p>The implementations are in common/ and server/.<br>
+</p>
+<p><br>
+</p>
+</body>
+</html>
diff --git a/connectors/jk/java/org/apache/jk/mbeans-descriptors.xml b/connectors/jk/java/org/apache/jk/mbeans-descriptors.xml
new file mode 100644
index 0000000..b07bf3b
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/mbeans-descriptors.xml
@@ -0,0 +1,472 @@
+<?xml version="1.0"?>
+<!DOCTYPE mbeans-descriptors PUBLIC
+ "-//Apache Software Foundation//DTD Model MBeans Configuration File"
+ "http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd">
+
+<!--
+     Descriptions of JMX MBeans for jk
+ -->
+
+<mbeans-descriptors>
+
+  <mbean name="ChannelSocket"
+         description="Socket channel"
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.common.ChannelSocket">
+
+    <attribute name="port"
+               description="Destination port"
+               type="java.lang.Integer"/>
+
+  </mbean>
+
+  <mbean name="JkWorkerEnv"
+         description="Worker env for jk"
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.core.WorkerEnv">
+
+    <attribute name="localId"
+               description="If automatic port allocation is enabled, ChannelSocket will allocate ports sequentially. This is the sequence number"
+               type="java.lang.Integer"/>
+
+    <attribute name="jkHome"
+               description="Base directory for jk"
+               type="java.lang.String"/>
+
+    <attribute name="managedResource"
+               description="Access to the object"
+               type="java.lang.Object" writeable="false" />
+
+    <attribute name="handlersObjectName"
+               description="List of all jk handlers"
+               type="[Ljavax.management.ObjectName;"/>
+
+    <operation name="addHandler"
+               description="add a jk component"
+               returnType="void">
+      <parameter name="name"
+                 description="local name"
+                 type="java.lang.String"/>
+      <parameter name="handler"
+                 description="handler"
+                 type="org.apache.jk.core.JkHandler"/>
+    </operation>
+
+  </mbean>
+
+  <!-- Native connectors -->
+  <mbean name="JkAjp13"
+         description="native Ajp13 connector"
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.modjk.ajp13">
+
+    <attribute name="Id"
+               description="Internal id"
+               type="java.lang.String"/>
+
+    <attribute name="disabled"
+               description="State"
+               type="java.lang.Integer"/>
+
+    <attribute name="ver"
+               description="Generation"
+               type="java.lang.Integer"/>
+
+    <attribute name="debug"
+               description="Debug level"
+               type="java.lang.Integer"/>
+
+    <attribute name="lb_factor"
+               description=""
+               type="java.lang.Integer"/>
+    <attribute name="lb_value"
+               description=""
+               type="java.lang.Integer"/>
+    <attribute name="epCount"
+               description=""
+               type="java.lang.Integer"/>
+    <attribute name="graceful"
+               description=""
+               type="java.lang.Integer"/>
+
+    <attribute name="route"
+               description=""
+               type="java.lang.String"/>
+
+  </mbean>
+
+  <mbean name="JkChannelSocket"
+         description="native Ajp13 connector"
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.modjk.channel.socket">
+
+    <attribute name="Id"
+               description="Internal id"
+               type="java.lang.String"/>
+
+    <attribute name="disabled"
+               description="State"
+               type="java.lang.Integer"/>
+
+    <attribute name="ver"
+               description="Generation"
+               type="java.lang.Integer"/>
+
+    <attribute name="debug"
+               description="Debug level"
+               type="java.lang.Integer"/>
+
+  </mbean>
+
+  <mbean name="JkWorkerEnv" 
+         description=""
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.modjk.workerEnv">
+
+    <attribute name="Id"
+               description="Internal id"
+               type="java.lang.String"/>
+
+    <attribute name="disabled"
+               description="State"
+               type="java.lang.Integer"/>
+
+    <attribute name="ver"
+               description="Generation"
+               type="java.lang.Integer"/>
+
+    <attribute name="debug"
+               description="Debug level"
+               type="java.lang.Integer"/>
+
+  </mbean>
+
+  <mbean name="JkLoggerApache2" 
+         description=""
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.modjk.logger.apache2">
+    <attribute name="Id"
+               description="Internal id"
+               type="java.lang.String"/>
+
+    <attribute name="disabled"
+               description="State"
+               type="java.lang.Integer"/>
+
+    <attribute name="ver"
+               description="Generation"
+               type="java.lang.Integer"/>
+
+    <attribute name="debug"
+               description="Debug level"
+               type="java.lang.Integer"/>
+
+
+  </mbean>
+
+  <mbean name="JkUriMap" 
+         description=""
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.modjk.uriMap">
+    <attribute name="Id"
+               description="Internal id"
+               type="java.lang.String"/>
+
+    <attribute name="disabled"
+               description="State"
+               type="java.lang.Integer"/>
+
+    <attribute name="ver"
+               description="Generation"
+               type="java.lang.Integer"/>
+
+    <attribute name="debug"
+               description="Debug level"
+               type="java.lang.Integer"/>
+
+
+  </mbean>
+
+  <mbean name="JkConfig" 
+         description=""
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.modjk.config">
+    <attribute name="Id"
+               description="Internal id"
+               type="java.lang.String"/>
+
+    <attribute name="disabled"
+               description="State"
+               type="java.lang.Integer"/>
+
+    <attribute name="ver"
+               description="Generation"
+               type="java.lang.Integer"/>
+
+    <attribute name="debug"
+               description="Debug level"
+               type="java.lang.Integer"/>
+
+    <attribute name="file"
+               description="Config file"
+               type="java.lang.String"/>
+
+  </mbean>
+  <mbean name="JkShm" 
+         description=""
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.modjk.shm">
+    <attribute name="Id"
+               description="Internal id"
+               type="java.lang.String"/>
+
+    <attribute name="disabled"
+               description="State"
+               type="java.lang.Integer"/>
+
+    <attribute name="ver"
+               description="Generation"
+               type="java.lang.Integer"/>
+
+    <attribute name="debug"
+               description="Debug level"
+               type="java.lang.Integer"/>
+
+
+  </mbean>
+  <mbean name="JkUri" 
+         description=""
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.modjk.uri">
+
+    <attribute name="Id"
+               description="Internal id"
+               type="java.lang.String"/>
+
+    <attribute name="disabled"
+               description="State"
+               type="java.lang.Integer"/>
+
+    <attribute name="ver"
+               description="Generation"
+               type="java.lang.Integer"/>
+
+    <attribute name="debug"
+               description="Debug level"
+               type="java.lang.Integer"/>
+
+    <attribute name="host"
+               description="Uri components"
+               type="java.lang.String"/>
+    <attribute name="uri"
+               description="Uri"
+               type="java.lang.String"/>
+    <attribute name="path"
+               description="Uri"
+               type="java.lang.String"/>
+
+  </mbean>
+
+  <mbean name="JkVm" 
+         description=""
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.modjk.vm">
+    <attribute name="Id"
+               description="Internal id"
+               type="java.lang.String"/>
+
+    <attribute name="disabled"
+               description="State"
+               type="java.lang.Integer"/>
+
+    <attribute name="ver"
+               description="Generation"
+               type="java.lang.Integer"/>
+
+    <attribute name="debug"
+               description="Debug level"
+               type="java.lang.Integer"/>
+
+
+  </mbean>
+
+  <mbean name="JkChannelUn" 
+         description=""
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.modjk.channel.un">
+    <attribute name="Id"
+               description="Internal id"
+               type="java.lang.String"/>
+
+    <attribute name="disabled"
+               description="State"
+               type="java.lang.Integer"/>
+
+    <attribute name="ver"
+               description="Generation"
+               type="java.lang.Integer"/>
+
+    <attribute name="debug"
+               description="Debug level"
+               type="java.lang.Integer"/>
+
+
+  </mbean>
+
+  <mbean name="JkChannelJni" 
+         description=""
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.modjk.channel.jni">
+    <attribute name="Id"
+               description="Internal id"
+               type="java.lang.String"/>
+
+    <attribute name="disabled"
+               description="State"
+               type="java.lang.Integer"/>
+
+    <attribute name="ver"
+               description="Generation"
+               type="java.lang.Integer"/>
+
+    <attribute name="debug"
+               description="Debug level"
+               type="java.lang.Integer"/>
+
+
+  </mbean>
+
+  <mbean name="JkWorkerJni" 
+         description=""
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.modjk.worker.jni">
+    <attribute name="Id"
+               description="Internal id"
+               type="java.lang.String"/>
+
+    <attribute name="disabled"
+               description="State"
+               type="java.lang.Integer"/>
+
+    <attribute name="ver"
+               description="Generation"
+               type="java.lang.Integer"/>
+
+    <attribute name="debug"
+               description="Debug level"
+               type="java.lang.Integer"/>
+
+
+  </mbean>
+
+  <mbean name="JkStatus" 
+         description=""
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.modjk.status">
+    <attribute name="Id"
+               description="Internal id"
+               type="java.lang.String"/>
+
+    <attribute name="disabled"
+               description="State"
+               type="java.lang.Integer"/>
+
+    <attribute name="ver"
+               description="Generation"
+               type="java.lang.Integer"/>
+
+    <attribute name="debug"
+               description="Debug level"
+               type="java.lang.Integer"/>
+
+
+  </mbean>
+  <mbean name="JkHandlerResponse" 
+         description=""
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.modjk.handler.response">
+    <attribute name="Id"
+               description="Internal id"
+               type="java.lang.String"/>
+
+    <attribute name="disabled"
+               description="State"
+               type="java.lang.Integer"/>
+
+    <attribute name="ver"
+               description="Generation"
+               type="java.lang.Integer"/>
+
+    <attribute name="debug"
+               description="Debug level"
+               type="java.lang.Integer"/>
+
+
+  </mbean>
+  <mbean name="JkHandlerLogon" 
+         description=""
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.modjk.handler.logon">
+    <attribute name="Id"
+               description="Internal id"
+               type="java.lang.String"/>
+
+    <attribute name="disabled"
+               description="State"
+               type="java.lang.Integer"/>
+
+    <attribute name="ver"
+               description="Generation"
+               type="java.lang.Integer"/>
+
+    <attribute name="debug"
+               description="Debug level"
+               type="java.lang.Integer"/>
+
+
+  </mbean>
+
+  <mbean name="JkLb" 
+         description=""
+         domain="Catalina"
+         group="Jk"
+         type="org.apache.jk.modjk.lb">
+   
+    <attribute name="Id"
+               description="Internal id"
+               type="java.lang.String"/>
+
+    <attribute name="disabled"
+               description="State"
+               type="java.lang.Integer"/>
+
+    <attribute name="ver"
+               description="Generation"
+               type="java.lang.Integer"/>
+
+    <attribute name="debug"
+               description="Debug level"
+               type="java.lang.Integer"/>
+
+  </mbean>
+
+
+
+</mbeans-descriptors>
diff --git a/connectors/jk/java/org/apache/jk/server/JkCoyoteHandler.java b/connectors/jk/java/org/apache/jk/server/JkCoyoteHandler.java
new file mode 100644
index 0000000..4494b30
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/server/JkCoyoteHandler.java
@@ -0,0 +1,536 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.server;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.PrivilegedExceptionAction;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedAction;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.commons.modeler.Registry;
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.ActionHook;
+import org.apache.coyote.Adapter;
+import org.apache.coyote.ProtocolHandler;
+import org.apache.coyote.Request;
+import org.apache.coyote.Response;
+import org.apache.coyote.RequestInfo;
+import org.apache.coyote.Constants;
+import org.apache.jk.common.HandlerRequest;
+import org.apache.jk.common.JkInputStream;
+import org.apache.jk.common.MsgAjp;
+import org.apache.jk.core.JkHandler;
+import org.apache.jk.core.Msg;
+import org.apache.jk.core.MsgContext;
+import org.apache.jk.core.WorkerEnv;
+import org.apache.jk.core.JkChannel;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.C2BConverter;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.HttpMessages;
+import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.net.SSLSupport;
+
+/** Plugs Jk2 into Coyote. Must be named "type=JkHandler,name=container"
+ *
+ * @jmx:notification-handler name="org.apache.jk.SEND_PACKET
+ * @jmx:notification-handler name="org.apache.coyote.ACTION_COMMIT
+ */
+public class JkCoyoteHandler extends JkHandler implements
+    ProtocolHandler,
+    ActionHook,
+    org.apache.coyote.OutputBuffer,
+    org.apache.coyote.InputBuffer
+{
+    protected static org.apache.commons.logging.Log log 
+        = org.apache.commons.logging.LogFactory.getLog(JkCoyoteHandler.class);
+    // Set debug on this logger to see the container request time
+    private static org.apache.commons.logging.Log logTime=
+        org.apache.commons.logging.LogFactory.getLog( "org.apache.jk.REQ_TIME" );
+
+    // ----------------------------------------------------------- DoPrivileged
+    private final class StatusLinePrivilegedAction implements PrivilegedAction {
+	int status;
+	StatusLinePrivilegedAction(int status) {
+	    this.status = status;
+	}
+	public Object run() {
+	    return HttpMessages.getMessage(status);
+	}
+    }
+
+    int headersMsgNote;
+    int c2bConvertersNote;
+    int tmpMessageBytesNote;
+    int utfC2bNote;
+    int obNote;
+    int epNote;
+    int inputStreamNote;
+    private boolean paused = false;
+    
+    Adapter adapter;
+    protected JkMain jkMain=null;
+
+    public final int JK_STATUS_NEW=0;
+    public final int JK_STATUS_HEAD=1;
+    public final int JK_STATUS_CLOSED=2;
+
+    /** Set a property. Name is a "component.property". JMX should
+     * be used instead.
+     */
+    public void setProperty( String name, String value ) {
+        if( log.isTraceEnabled())
+            log.trace("setProperty " + name + " " + value );
+        getJkMain().setProperty( name, value );
+        properties.put( name, value );
+    }
+
+    public String getProperty( String name ) {
+        return properties.getProperty(name) ;
+    }
+
+    /** Pass config info
+     */
+    public void setAttribute( String name, Object value ) {
+        if( log.isDebugEnabled())
+            log.debug("setAttribute " + name + " " + value );
+        if( value instanceof String )
+            this.setProperty( name, (String)value );
+    }
+
+    /**
+     * Retrieve config info.
+     * Primarily for use with the admin webapp.
+     */   
+    public Object getAttribute( String name ) {
+        return getJkMain().getProperty(name);
+    }
+
+    /** The adapter, used to call the connector 
+     */
+    public void setAdapter(Adapter adapter) {
+        this.adapter=adapter;
+    }
+
+    public Adapter getAdapter() {
+        return adapter;
+    }
+
+    public JkMain getJkMain() {
+        if( jkMain == null ) {
+            jkMain=new JkMain();
+            jkMain.setWorkerEnv(wEnv);
+            
+        }
+        return jkMain;
+    }
+    
+    boolean started=false;
+    
+    /** Start the protocol
+     */
+    public void init() {
+        if( started ) return;
+
+        started=true;
+        
+        if( wEnv==null ) {
+            // we are probably not registered - not very good.
+            wEnv=getJkMain().getWorkerEnv();
+            wEnv.addHandler("container", this );
+        }
+
+        try {
+            // jkMain.setJkHome() XXX;
+            
+            getJkMain().init();
+
+            headersMsgNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "headerMsg" );
+            tmpMessageBytesNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "tmpMessageBytes" );
+            utfC2bNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "utfC2B" );
+            epNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "ep" );
+            obNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "coyoteBuffer" );
+            inputStreamNote= wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE,
+                                             "jkInputStream");
+
+        } catch( Exception ex ) {
+            log.error("Error during init",ex);
+        }
+    }
+
+    public void start() {
+        try {
+            if( oname != null && getJkMain().getDomain() == null) {
+                try {
+                    Registry.getRegistry().registerComponent(getJkMain(), oname.getDomain(),
+                            "JkMain", "type=JkMain");
+                } catch (Exception e) {
+                    log.error( "Error registering jkmain " + e );
+                }
+            }
+            getJkMain().start();
+        } catch( Exception ex ) {
+            log.error("Error during startup",ex);
+        }
+    }
+
+    public void pause() throws Exception {
+        if(!paused) {
+            paused = true;
+            getJkMain().pause();
+        }
+    }
+
+    public void resume() throws Exception {
+        if(paused) {
+            paused = false;
+            getJkMain().resume();
+        }
+    }
+
+    public void destroy() {
+        if( !started ) return;
+
+        started = false;
+        getJkMain().stop();
+    }
+
+    // -------------------- OutputBuffer implementation --------------------
+
+        
+    public int doWrite(ByteChunk chunk, Response res) 
+        throws IOException
+    {
+        if (!res.isCommitted()) {
+            // Send the connector a request for commit. The connector should
+            // then validate the headers, send them (using sendHeader) and 
+            // set the filters accordingly.
+            res.sendHeaders();
+        }
+        MsgContext ep=(MsgContext)res.getNote( epNote );
+
+        MsgAjp msg=(MsgAjp)ep.getNote( headersMsgNote );
+
+        int len=chunk.getLength();
+        byte buf[]=msg.getBuffer();
+        // 4 - hardcoded, byte[] marshalling overhead 
+        int chunkSize=buf.length - msg.getHeaderLength() - 4;
+        int off=0;
+        while( len > 0 ) {
+            int thisTime=len;
+            if( thisTime > chunkSize ) {
+                thisTime=chunkSize;
+            }
+            len-=thisTime;
+            
+            msg.reset();
+            msg.appendByte( HandlerRequest.JK_AJP13_SEND_BODY_CHUNK);
+            if( log.isDebugEnabled() ) log.debug("doWrite " + off + " " + thisTime + " " + len );
+            msg.appendBytes( chunk.getBytes(), chunk.getOffset() + off, thisTime );
+            off+=thisTime;
+            ep.setType( JkHandler.HANDLE_SEND_PACKET );
+            ep.getSource().send( msg, ep );
+        }
+        return 0;
+    }
+    
+    public int doRead(ByteChunk chunk, Request req) 
+        throws IOException
+    {
+        Response res=req.getResponse();
+        if( log.isDebugEnabled() )
+            log.debug("doRead " + chunk.getBytes() + " " +  chunk.getOffset() + " " + chunk.getLength());
+        MsgContext ep=(MsgContext)res.getNote( epNote );
+        
+        JkInputStream jkIS=(JkInputStream)ep.getNote( inputStreamNote );
+        // return jkIS.read( chunk.getBytes(), chunk.getOffset(), chunk.getLength());
+        return jkIS.doRead( chunk );
+    }
+    
+    // -------------------- Jk handler implementation --------------------
+    // Jk Handler mehod
+    public int invoke( Msg msg, MsgContext ep ) 
+        throws IOException
+    {
+        if( logTime.isDebugEnabled() ) 
+                ep.setLong( MsgContext.TIMER_PRE_REQUEST, System.currentTimeMillis());
+        
+        org.apache.coyote.Request req=(org.apache.coyote.Request)ep.getRequest();
+        org.apache.coyote.Response res=req.getResponse();
+        res.setHook( this );
+
+        if( log.isDebugEnabled() )
+            log.debug( "Invoke " + req + " " + res + " " + req.requestURI().toString());
+        
+        res.setOutputBuffer( this );
+        req.setInputBuffer( this );
+        
+        if( ep.getNote( headersMsgNote ) == null ) {
+            Msg msg2=new MsgAjp();
+            ep.setNote( headersMsgNote, msg2 );
+        }
+        
+        res.setNote( epNote, ep );
+        ep.setStatus( JK_STATUS_HEAD );
+	RequestInfo rp = req.getRequestProcessor();
+	rp.setStage(Constants.STAGE_SERVICE);
+        try {
+            adapter.service( req, res );
+        } catch( Exception ex ) {
+            log.info("Error servicing request " + req,ex);
+        }
+        if(ep.getStatus() != JK_STATUS_CLOSED) {
+            res.finish();
+        }
+
+        ep.setStatus( JK_STATUS_NEW );
+
+        req.recycle();
+        req.updateCounters();
+        res.recycle();
+	rp.setStage(Constants.STAGE_KEEPALIVE);
+        return OK;
+    }
+
+    private void appendHead(org.apache.coyote.Response res)
+        throws IOException
+    {
+        if( log.isDebugEnabled() )
+            log.debug("COMMIT sending headers " + res + " " + res.getMimeHeaders() );
+        
+        C2BConverter c2b=(C2BConverter)res.getNote( utfC2bNote );
+        if( c2b==null ) {
+            if(System.getSecurityManager() != null) {
+                try {
+                    c2b = (C2BConverter)
+                        AccessController.doPrivileged(
+                              new PrivilegedExceptionAction () {
+                                      public Object run() 
+                                          throws IOException{
+                                          return new C2BConverter(  "iso-8859-1" );
+                                      }
+                                  });
+                } catch(PrivilegedActionException pae) {
+                    Exception ex = pae.getException();
+                    if(ex instanceof IOException)
+                        throw (IOException)ex;
+                }
+            } else {
+                c2b=new C2BConverter(  "iso-8859-1" );
+	    }
+            res.setNote( utfC2bNote, c2b );
+        }
+        
+        MsgContext ep=(MsgContext)res.getNote( epNote );
+        MsgAjp msg=(MsgAjp)ep.getNote( headersMsgNote );
+        msg.reset();
+        msg.appendByte(HandlerRequest.JK_AJP13_SEND_HEADERS);
+        msg.appendInt( res.getStatus() );
+        
+        MessageBytes mb=(MessageBytes)ep.getNote( tmpMessageBytesNote );
+        if( mb==null ) {
+            mb=new MessageBytes();
+            ep.setNote( tmpMessageBytesNote, mb );
+        }
+        String message=res.getMessage();
+        if( message==null ){
+	    if( System.getSecurityManager() != null ) {
+		message = (String)AccessController.doPrivileged(
+               				new StatusLinePrivilegedAction(res.getStatus()));
+	    } else {
+		message= HttpMessages.getMessage(res.getStatus());
+	    }
+        } else {
+            message = message.replace('\n', ' ').replace('\r', ' ');
+        }
+        mb.setString( message );
+        c2b.convert( mb );
+        msg.appendBytes(mb);
+
+        // XXX add headers
+        
+        MimeHeaders headers=res.getMimeHeaders();
+        String contentType = res.getContentType();
+        if( contentType != null ) {
+            headers.setValue("Content-Type").setString(contentType);
+        }
+        String contentLanguage = res.getContentLanguage();
+        if( contentLanguage != null ) {
+            headers.setValue("Content-Language").setString(contentLanguage);
+        }
+	int contentLength = res.getContentLength();
+        if( contentLength >= 0 ) {
+            headers.setValue("Content-Length").setInt(contentLength);
+        }
+        int numHeaders = headers.size();
+        msg.appendInt(numHeaders);
+        for( int i=0; i<numHeaders; i++ ) {
+            MessageBytes hN=headers.getName(i);
+            // no header to sc conversion - there's little benefit
+            // on this direction
+            c2b.convert ( hN );
+            msg.appendBytes( hN );
+                        
+            MessageBytes hV=headers.getValue(i);
+            c2b.convert( hV );
+            msg.appendBytes( hV );
+        }
+        ep.setType( JkHandler.HANDLE_SEND_PACKET );
+        ep.getSource().send( msg, ep );
+    }
+    
+    // -------------------- Coyote Action implementation --------------------
+    
+    public void action(ActionCode actionCode, Object param) {
+        try {
+            if( actionCode==ActionCode.ACTION_COMMIT ) {
+                if( log.isDebugEnabled() ) log.debug("COMMIT " );
+                org.apache.coyote.Response res=(org.apache.coyote.Response)param;
+
+                if(  res.isCommitted() ) {
+                    if( log.isInfoEnabled() )
+                        log.info("Response already commited " );
+                } else {
+                    appendHead( res );
+                }
+            } else if( actionCode==ActionCode.ACTION_RESET ) {
+                if( log.isDebugEnabled() )
+                    log.debug("RESET " );
+
+            } else if( actionCode==ActionCode.ACTION_CLIENT_FLUSH ) {
+                if( log.isDebugEnabled() ) log.debug("CLIENT_FLUSH " );
+                org.apache.coyote.Response res=(org.apache.coyote.Response)param;
+                MsgContext ep=(MsgContext)res.getNote( epNote );
+                ep.setType( JkHandler.HANDLE_FLUSH );
+                ep.getSource().flush( null, ep );
+                
+            } else if( actionCode==ActionCode.ACTION_CLOSE ) {
+                if( log.isDebugEnabled() ) log.debug("CLOSE " );
+
+                org.apache.coyote.Response res=(org.apache.coyote.Response)param;
+                MsgContext ep=(MsgContext)res.getNote( epNote );
+                if( ep.getStatus()== JK_STATUS_CLOSED ) {
+                    // Double close - it may happen with forward 
+                    if( log.isDebugEnabled() ) log.debug("Double CLOSE - forward ? " + res.getRequest().requestURI() );
+                    return;
+                }
+                 
+                if( !res.isCommitted() )
+                    this.action( ActionCode.ACTION_COMMIT, param );
+                
+                MsgAjp msg=(MsgAjp)ep.getNote( headersMsgNote );
+                msg.reset();
+                msg.appendByte( HandlerRequest.JK_AJP13_END_RESPONSE );
+                msg.appendByte( 1 );
+
+                try {                
+                    ep.setType( JkHandler.HANDLE_SEND_PACKET );
+                    ep.getSource().send( msg, ep );
+
+                    ep.setType( JkHandler.HANDLE_FLUSH );
+                    ep.getSource().flush( msg, ep );
+                } catch(IOException iex) {
+                    log.debug("Connection error ending request.",iex);
+                }
+                ep.setStatus(JK_STATUS_CLOSED );
+
+                if( logTime.isDebugEnabled() ) 
+                    logTime(res.getRequest(), res);
+            } else if( actionCode==ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
+                org.apache.coyote.Request req=(org.apache.coyote.Request)param;
+
+                // Extract SSL certificate information (if requested)
+                MessageBytes certString = (MessageBytes)req.getNote(WorkerEnv.SSL_CERT_NOTE);
+                if( certString != null && !certString.isNull() ) {
+                    ByteChunk certData = certString.getByteChunk();
+                    ByteArrayInputStream bais = 
+                        new ByteArrayInputStream(certData.getBytes(),
+                                                 certData.getStart(),
+                                                 certData.getLength());
+ 
+                    // Fill the first element.
+                    X509Certificate jsseCerts[] = null;
+                    try {
+                        CertificateFactory cf =
+                            CertificateFactory.getInstance("X.509");
+                        X509Certificate cert = (X509Certificate)
+                            cf.generateCertificate(bais);
+                        jsseCerts =  new X509Certificate[1];
+                        jsseCerts[0] = cert;
+                    } catch(java.security.cert.CertificateException e) {
+                        log.error("Certificate convertion failed" , e );
+                        return;
+                    }
+ 
+                    req.setAttribute(SSLSupport.CERTIFICATE_KEY, 
+                                     jsseCerts);
+                }
+                
+            } else if( actionCode==ActionCode.ACTION_REQ_HOST_ATTRIBUTE ) {
+                org.apache.coyote.Request req=(org.apache.coyote.Request)param;
+
+				// If remoteHost not set by JK, get it's name from it's remoteAddr
+            	if( req.remoteHost().isNull())
+                	req.remoteHost().setString(InetAddress.getByName(req.remoteAddr().toString()).getHostName());
+
+            // } else if( actionCode==ActionCode.ACTION_POST_REQUEST ) {
+
+            } else if( actionCode==ActionCode.ACTION_ACK ) {
+                if( log.isDebugEnabled() )
+                    log.debug("ACK " );
+                // What should we do here ? Who calls it ? 
+            }
+        } catch( Exception ex ) {
+            log.error( "Error in action code ", ex );
+        }
+    }
+
+    private void logTime(Request req, Response res ) {
+        // called after the request
+        //            org.apache.coyote.Request req=(org.apache.coyote.Request)param;
+        //            Response res=req.getResponse();
+        MsgContext ep=(MsgContext)res.getNote( epNote );
+        String uri=req.requestURI().toString();
+        if( uri.indexOf( ".gif" ) >0 ) return;
+        
+        ep.setLong( MsgContext.TIMER_POST_REQUEST, System.currentTimeMillis());
+        long t1= ep.getLong( MsgContext.TIMER_PRE_REQUEST ) -
+            ep.getLong( MsgContext.TIMER_RECEIVED );
+        long t2= ep.getLong( MsgContext.TIMER_POST_REQUEST ) -
+            ep.getLong( MsgContext.TIMER_PRE_REQUEST );
+        
+        logTime.debug("Time pre=" + t1 + "/ service=" + t2 + " " +
+                      res.getContentLength() + " " + 
+                      uri );
+    }
+
+    public ObjectName preRegister(MBeanServer server,
+                                  ObjectName oname) throws Exception
+    {
+        // override - we must be registered as "container"
+        this.name="container";        
+        return super.preRegister(server, oname);
+    }
+}
diff --git a/connectors/jk/java/org/apache/jk/server/JkMain.java b/connectors/jk/java/org/apache/jk/server/JkMain.java
new file mode 100644
index 0000000..e165062
--- /dev/null
+++ b/connectors/jk/java/org/apache/jk/server/JkMain.java
@@ -0,0 +1,671 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.server;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.apache.commons.modeler.Registry;
+import org.apache.jk.core.JkHandler;
+import org.apache.jk.core.WorkerEnv;
+import org.apache.tomcat.util.IntrospectionUtils;
+
+/** Main class used to startup and configure jk. It manages the conf/jk2.properties file
+ *  and is the target of JMX proxy.
+ *
+ *  It implements a policy of save-on-change - whenever a property is changed at
+ *  runtime the jk2.properties file will be overriden. 
+ *
+ *  You can edit the config file when tomcat is stoped ( or if you don't use JMX or
+ *  other admin tools ).
+ *
+ *  The format of jk2.properties:
+ *  <dl>
+ *   <dt>TYPE[.LOCALNAME].PROPERTY_NAME=VALUE
+ *   <dd>Set a property on the associated component. TYPE will be used to
+ *   find the class name and instantiate the component. LOCALNAME allows
+ *   multiple instances. In JMX mode, TYPE and LOCALNAME will form the
+ *   JMX name ( eventually combined with a 'jk2' component )
+ *
+ *   <dt>NAME=VALUE
+ *   <dd>Define global properties to be used in ${} substitutions
+ *
+ *   <dt>class.COMPONENT_TYPE=JAVA_CLASS_NAME
+ *   <dd>Adds a new 'type' of component. We predefine all known types.
+ * </dl>
+ *
+ * Instances are created the first time a component name is found. In addition,
+ * 'handler.list' property will override the list of 'default' components that are
+ * loaded automatically.
+ *
+ *  Note that the properties file is just one (simplistic) way to configure jk. We hope
+ *  to see configs based on registry, LDAP, db, etc. ( XML is not necesarily better )
+ * 
+ * @author Costin Manolache
+ * @deprecated Will be replaced with JMX operations
+ */
+public class JkMain implements MBeanRegistration
+{
+    WorkerEnv wEnv;
+    String propFile;
+    Properties props=new Properties();
+
+    Properties modules=new Properties();
+    boolean modified=false;
+    boolean started=false;
+    boolean saveProperties=false;
+
+    public JkMain()
+    {
+        JkMain.jkMain=this;
+        modules.put("channelSocket", "org.apache.jk.common.ChannelSocket");
+        modules.put("channelUnix", "org.apache.jk.common.ChannelUn");
+        modules.put("channelJni", "org.apache.jk.common.ChannelJni");
+        modules.put("apr", "org.apache.jk.apr.AprImpl");
+        modules.put("mx", "org.apache.jk.common.JkMX");
+        modules.put("modeler", "org.apache.jk.common.JkModeler");
+        modules.put("shm", "org.apache.jk.common.Shm");
+        modules.put("request","org.apache.jk.common.HandlerRequest");
+        modules.put("container","org.apache.jk.common.HandlerRequest");
+        modules.put("modjk","org.apache.jk.common.ModJkMX");
+
+    }
+
+    public static JkMain getJkMain() {
+        return jkMain;
+    }
+
+    private static String DEFAULT_HTTPS="com.sun.net.ssl.internal.www.protocol";
+    private void initHTTPSUrls() {
+        try {
+            // 11657: if only ajp is used, https: redirects need to work ( at least for 1.3+)
+            String value = System.getProperty("java.protocol.handler.pkgs");
+            if (value == null) {
+                value = DEFAULT_HTTPS;
+            } else if (value.indexOf(DEFAULT_HTTPS) >= 0  ) {
+                return; // already set
+            } else {
+                value += "|" + DEFAULT_HTTPS;
+            }
+            System.setProperty("java.protocol.handler.pkgs", value);
+        } catch(Exception ex ) {
+            ex.printStackTrace();
+        }
+    }
+
+    // -------------------- Setting --------------------
+    
+    /** Load a .properties file into and set the values
+     *  into jk2 configuration.
+     */
+    public void setPropertiesFile( String p  ) {
+        propFile=p;
+        try {
+            props.load( new FileInputStream(propFile) );
+        } catch(IOException ex ){
+            ex.printStackTrace();
+        }
+    }
+
+    public String getPropertiesFile() {
+        return propFile;
+    }
+
+    public void setSaveProperties( boolean b ) {
+        saveProperties=b;
+    }
+
+    /** Set a name/value as a jk2 property
+     */
+    public void setProperty( String n, String v ) {
+        if( "jkHome".equals( n ) ) {
+            setJkHome( v );
+        } 
+        props.put( n, v );
+        if( started ) {
+            processProperty( n, v );
+            saveProperties();
+        }
+    }
+    /**
+     * Retrieve a property.
+     */
+    public Object getProperty(String name) {
+        String alias = (String)replacements.get(name);
+        Object result = null;
+        if(alias != null) {
+            result = props.get(alias);
+        }
+        if(result == null) {
+            result = props.get(name);
+        }
+        return result;
+    }
+    /**
+     * Set the <code>channelClassName</code> that will used to connect to
+     * httpd.
+     */
+    public void setChannelClassName(String name) {
+        props.put( "handler.channel.className",name);
+    }
+
+    public String getChannelClassName() {
+        return (String)props.get( "handler.channel.className");
+    }
+
+    /**
+     * Set the <code>workerClassName</code> that will handle the request.
+     * ( sort of 'pivot' in axis :-)
+     */
+    public void setWorkerClassName(String name) {
+        props.put( "handler.container.className",name);
+    }
+
+    public String getWorkerClassName() {
+        return (String)props.get( "handler.container.className");
+    }
+
+    /** Set the base dir of jk2. ( including WEB-INF if in a webapp ).
+     *  We'll try to guess it from classpath if none is set ( for
+     *  example on command line ), but if in a servlet environment
+     *  you need to use Context.getRealPath or a system property or
+     *  set it expliciltey.
+     */
+    public void setJkHome( String s ) {
+        getWorkerEnv().setJkHome(s);
+    }
+
+    public String getJkHome() {
+        return getWorkerEnv().getJkHome();
+    }
+
+    String out;
+    String err;
+    File propsF;
+    
+    public void setOut( String s ) {
+        this.out=s;
+    }
+
+    public String getOut() {
+        return this.out;
+    }
+
+    public void setErr( String s ) {
+        this.err=s;
+    }
+    
+    public String getErr() {
+        return this.err;
+    }
+    
+    // -------------------- Initialization --------------------
+    
+    public void init() throws IOException
+    {
+        long t1=System.currentTimeMillis();
+        if(null != out) {
+            PrintStream outS=new PrintStream(new FileOutputStream(out));
+            System.setOut(outS);
+        }
+        if(null != err) {
+            PrintStream errS=new PrintStream(new FileOutputStream(err));
+            System.setErr(errS);
+        }
+
+        String home=getWorkerEnv().getJkHome();
+        if( home==null ) {
+            // XXX use IntrospectionUtil to find myself
+            this.guessHome();
+        }
+        home=getWorkerEnv().getJkHome();
+        if( home==null ) {
+            log.info( "Can't find home, jk2.properties not loaded");
+        }
+        if( home != null ) {
+            File hF=new File(home);
+            File conf=new File( home, "conf" );
+            if( ! conf.exists() )
+                conf=new File( home, "etc" );
+
+            propsF=new File( conf, "jk2.properties" );
+            
+            if( propsF.exists() ) {
+                log.debug("Starting Jk2, base dir= " + home + " conf=" + propsF );
+                setPropertiesFile( propsF.getAbsolutePath());
+            } else {
+                log.debug("Starting Jk2, base dir= " + home );
+                if( log.isDebugEnabled() )
+                    log.debug( "No properties file found " + propsF );
+            }
+        }
+        String initHTTPS = (String)props.get("class.initHTTPS");
+        if("true".equalsIgnoreCase(initHTTPS)) {
+            initHTTPSUrls();
+        }
+
+        long t2=System.currentTimeMillis();
+        initTime=t2-t1;
+    }
+    
+    static String defaultHandlers[]= { "request",
+                                       "container",
+                                       "channelSocket"};
+    /*
+     static String defaultHandlers[]= { "apr",
+                                       "shm",
+                                       "request",
+                                       "container",
+                                       "channelSocket",
+                                       "channelJni",
+                                       "channelUnix"};
+    */
+    
+    public void stop() 
+    {
+        for( int i=0; i<wEnv.getHandlerCount(); i++ ) {
+            if( wEnv.getHandler(i) != null ) {
+                try {
+                    wEnv.getHandler(i).destroy();
+                } catch( IOException ex) {
+                    log.error("Error stoping " + wEnv.getHandler(i).getName(), ex);
+                }
+            }
+        }
+
+        started=false;
+    }
+    
+    public void start() throws IOException
+    {
+        long t1=System.currentTimeMillis();
+        // We must have at least 3 handlers:
+        // channel is the 'transport'
+        // request is the request processor or 'global' chain
+        // container is the 'provider'
+        // Additional handlers may exist and be used internally
+        // or be chained to create one of the standard handlers 
+
+        String handlers[]=defaultHandlers;
+        // backward compat
+        String workers=props.getProperty( "handler.list", null );
+        if( workers!=null ) {
+            handlers= split( workers, ",");
+        }
+
+        // Load additional component declarations
+        processModules();
+        
+        for( int i=0; i<handlers.length; i++ ) {
+            String name= handlers[i];
+            JkHandler w=getWorkerEnv().getHandler( name );
+            if( w==null ) {
+                newHandler( name, "", name );
+            }
+        }
+
+        // Process properties - and add aditional handlers.
+        processProperties();
+
+        for( int i=0; i<wEnv.getHandlerCount(); i++ ) {
+            if( wEnv.getHandler(i) != null ) {
+                try {
+                    wEnv.getHandler(i).init();
+                } catch( IOException ex) {
+                    if( "apr".equals(wEnv.getHandler(i).getName() )) {
+                        log.info( "APR not loaded, disabling jni components: " + ex.toString());
+                    } else {
+                        log.error( "error initializing " + wEnv.getHandler(i).getName(), ex );
+                    }
+                }
+            }
+        }
+
+        started=true;
+        long t2=System.currentTimeMillis();
+        startTime=t2-t1;
+
+        this.saveProperties();
+        log.info("Jk running ID=" + wEnv.getLocalId() + " time=" + initTime + "/" + startTime +
+                 "  config=" + propFile);
+    }
+
+    // -------------------- Usefull methods --------------------
+    
+    public WorkerEnv getWorkerEnv() {
+        if( wEnv==null ) { 
+            wEnv=new WorkerEnv();
+        }
+        return wEnv;
+    }
+
+    public void setWorkerEnv(WorkerEnv wEnv) {
+        this.wEnv = wEnv;
+    }
+
+    /* A bit of magic to support workers.properties without giving
+       up the clean get/set
+    */
+    public void setBeanProperty( Object target, String name, String val ) {
+        if( val!=null )
+            val=IntrospectionUtils.replaceProperties( val, props );
+        if( log.isDebugEnabled())
+            log.debug( "setProperty " + target + " " + name + "=" + val );
+        
+        IntrospectionUtils.setProperty( target, name, val );
+    }
+
+    /* 
+     * Set a handler property
+     */
+    public void setPropertyString( String handlerN, String name, String val ) {
+        if( log.isDebugEnabled() )
+            log.debug( "setProperty " + handlerN + " " + name + "=" + val );
+        Object target=getWorkerEnv().getHandler( handlerN );
+
+        setBeanProperty( target, name, val );
+        if( started ) {
+            saveProperties();
+        }
+
+    }
+
+    /** The time it took to initialize jk ( ms)
+     */
+    public long getInitTime() {
+        return initTime;
+    }
+
+    /** The time it took to start jk ( ms )
+     */
+    public long getStartTime() {
+        return startTime;
+    }
+    
+    // -------------------- Main --------------------
+
+    long initTime;
+    long startTime;
+    static JkMain jkMain=null;
+
+    public static void main(String args[]) {
+        try {
+            if( args.length == 1 &&
+                ( "-?".equals(args[0]) || "-h".equals( args[0])) ) {
+                System.out.println("Usage: ");
+                System.out.println("  JkMain [args]");
+                System.out.println();
+                System.out.println("  Each bean setter corresponds to an arg ( like -debug 10 )");
+                System.out.println("  System properties:");
+                System.out.println("    jk2.home    Base dir of jk2");
+                return;
+            }
+
+            jkMain=new JkMain();
+
+            IntrospectionUtils.processArgs( jkMain, args, new String[] {},
+                                            null, new Hashtable());
+
+            jkMain.init();
+            jkMain.start();
+        } catch( Exception ex ) {
+            ex.printStackTrace();
+        }
+    }
+
+    // -------------------- Private methods --------------------
+
+    public  void saveProperties() {
+        if( !saveProperties) return;
+        
+        // Temp - to check if it works
+        String outFile=propFile + ".save";
+        log.debug("Saving properties " + outFile );
+        try {
+            props.save( new FileOutputStream(outFile), "AUTOMATICALLY GENERATED" );
+        } catch(IOException ex ){
+            ex.printStackTrace();
+        }
+    }
+
+    // translate top-level keys ( from coyote or generic ) into component keys
+    static Hashtable replacements=new Hashtable();
+    static {
+        replacements.put("port","channelSocket.port");
+        replacements.put("maxThreads", "channelSocket.maxThreads");   
+        replacements.put("minSpareThreads", "channelSocket.minSpareThreads");   
+        replacements.put("maxSpareThreads", "channelSocket.maxSpareThreads");   
+        replacements.put("backlog", "channelSocket.backlog");   
+        replacements.put("tcpNoDelay", "channelSocket.tcpNoDelay");
+        replacements.put("soTimeout", "channelSocket.soTimeout");
+        replacements.put("timeout", "channelSocket.timeout");
+        replacements.put("address", "channelSocket.address");            
+        replacements.put("tomcatAuthentication", "request.tomcatAuthentication");            
+    }
+
+    private void preProcessProperties() {
+        Enumeration keys=props.keys();
+        Vector v=new Vector();
+        
+        while( keys.hasMoreElements() ) {
+            String key=(String)keys.nextElement();          
+            Object newName=replacements.get(key);
+            if( newName !=null ) {
+                v.addElement(key);
+            }
+        }
+        keys=v.elements();
+        while( keys.hasMoreElements() ) {
+            String key=(String)keys.nextElement();
+            Object propValue=props.getProperty( key );
+            String replacement=(String)replacements.get(key);
+            props.put(replacement, propValue);
+            if( log.isDebugEnabled()) 
+                log.debug("Substituting " + key + " " + replacement + " " + 
+                    propValue);
+        }
+    }
+    
+    private void processProperties() {
+        preProcessProperties();
+        Enumeration keys=props.keys();
+
+        while( keys.hasMoreElements() ) {
+            String name=(String)keys.nextElement();
+            String propValue=props.getProperty( name );
+
+            processProperty( name, propValue );
+        }
+    }
+
+    private void processProperty(String name, String propValue) {
+        String type=name;
+        String fullName=name;
+        String localName="";
+        String propName="";
+        // ignore
+        if( name.startsWith("key.")) return;
+
+        int dot=name.indexOf(".");
+        int lastDot=name.lastIndexOf(".");
+        if( dot > 0 ) {
+            type=name.substring(0, dot );
+            if( dot != lastDot ) {
+                localName=name.substring( dot + 1, lastDot );
+                fullName=type + "." + localName;
+            } else {
+                fullName=type;
+            }
+            propName=name.substring( lastDot+1);
+        } else {
+            return;
+        }
+        
+        if( log.isDebugEnabled() )
+            log.debug( "Processing " + type + ":" + localName + ":" + fullName + " " + propName );
+        if( "class".equals( type ) || "handler".equals( type ) ) {
+            return;
+        }
+        
+        JkHandler comp=getWorkerEnv().getHandler( fullName );
+        if( comp==null ) {
+            comp=newHandler( type, localName, fullName );
+        }
+        if( comp==null )
+            return;
+        
+        if( log.isDebugEnabled() ) 
+            log.debug("Setting " + propName + " on " + fullName + " " + comp);
+        this.setBeanProperty( comp, propName, propValue );
+    }
+
+    private JkHandler newHandler( String type, String localName, String fullName )
+    {
+        JkHandler handler;
+        String classN=modules.getProperty(type);
+        if( classN == null ) {
+            log.error("No class name for " + fullName + " " + type );
+            return null;
+        }
+        try {
+            Class channelclass = Class.forName(classN);
+            handler=(JkHandler)channelclass.newInstance();
+        } catch (Throwable ex) {
+            handler=null;
+            log.error( "Can't create " + fullName, ex );
+            return null;
+        }
+        if( this.domain != null ) {
+            try {
+                Registry.getRegistry().registerComponent(handler, this.domain, classN,
+                        "type=JkHandler,name=" + fullName);
+            } catch (Exception e) {
+                log.error( "Error registering " + fullName, e );
+            }
+
+        }
+        wEnv.addHandler( fullName, handler );
+        return handler;
+    }
+
+    private void processModules() {
+        Enumeration keys=props.keys();
+        int plen=6;
+        
+        while( keys.hasMoreElements() ) {
+            String k=(String)keys.nextElement();
+            if( ! k.startsWith( "class." ) )
+                continue;
+
+            String name= k.substring( plen );
+            String propValue=props.getProperty( k );
+
+            if( log.isDebugEnabled()) log.debug("Register " + name + " " + propValue );
+            modules.put( name, propValue );
+        }
+    }
+
+    private String[] split(String s, String delim ) {
+         Vector v=new Vector();
+        StringTokenizer st=new StringTokenizer(s, delim );
+        while( st.hasMoreTokens() ) {
+            v.addElement( st.nextToken());
+        }
+        String res[]=new String[ v.size() ];
+        for( int i=0; i<res.length; i++ ) {
+            res[i]=(String)v.elementAt(i);
+        }
+        return res;
+    }
+
+    // guessing home
+    private static String CNAME="org/apache/jk/server/JkMain.class";
+
+    private void guessHome() {
+        String home= wEnv.getJkHome();
+        if( home != null )
+            return;
+        home=IntrospectionUtils.guessInstall( "jk2.home","jk2.home",
+                                              "tomcat-jk2.jar", CNAME );
+        if( home != null ) {
+            log.info("Guessed home " + home );
+            wEnv.setJkHome( home );
+        }
+    }
+
+    static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( JkMain.class );
+
+    protected String domain;
+    protected ObjectName oname;
+    protected MBeanServer mserver;
+
+    public ObjectName getObjectName() {
+        return oname;
+    }
+
+    public String getDomain() {
+        return domain;
+    }
+
+    public ObjectName preRegister(MBeanServer server,
+                                  ObjectName name) throws Exception {
+        oname=name;
+        mserver=server;
+        domain=name.getDomain();
+        return name;
+    }
+
+    public void postRegister(Boolean registrationDone) {
+    }
+
+    public void preDeregister() throws Exception {
+    }
+
+    public void postDeregister() {
+    }
+
+    public void pause() throws Exception {
+        for( int i=0; i<wEnv.getHandlerCount(); i++ ) {
+            if( wEnv.getHandler(i) != null ) {
+                wEnv.getHandler(i).pause();
+            }
+        }
+    }
+
+    public void resume() throws Exception {
+        for( int i=0; i<wEnv.getHandlerCount(); i++ ) {
+            if( wEnv.getHandler(i) != null ) {
+                wEnv.getHandler(i).resume();
+            }
+        }
+    }
+
+
+}
diff --git a/connectors/jk/jkant/ant.tasks b/connectors/jk/jkant/ant.tasks
new file mode 100644
index 0000000..8571965
--- /dev/null
+++ b/connectors/jk/jkant/ant.tasks
@@ -0,0 +1,3 @@
+so=org.apache.jk.ant.SoTask
+libtoolCompile=org.apache.jk.ant.compilers.LibtoolCompiler
+ccCompile=org.apache.jk.ant.compilers.CcCompiler
diff --git a/connectors/jk/jkant/buildJakarta.xml b/connectors/jk/jkant/buildJakarta.xml
new file mode 100644
index 0000000..5da7d15
--- /dev/null
+++ b/connectors/jk/jkant/buildJakarta.xml
@@ -0,0 +1,237 @@
+<?xml version="1.0" ?>
+<!-- EXPERIMENTAL - JUST AN EXAMPLE ON HOW TO USE GCJ TASK -->
+<!-- MAY REQUIRE SMALL/TRIVIAL MODIFICATION IN SOURCES -->
+<!-- REQUIRES GCJ FROM CVS - 3.0.1 DOESN'T WORK -->
+<project name="tomcat_gcj" default="main" basedir=".">
+
+  <description>Native build for tomcat</description>
+  
+  <property file="${user.home}/.ant.properties" />
+  <property file="${user.home}/build.properties" />
+  <property file="build.properties" />
+
+  <property name="LIB" value="/opt/java" />
+  <property name="tomcat.src" value="/ws/33" />
+  <property name="ant.src" value="/ws/jakarta/jakarta-ant" />
+  <property name="crimson.src" value="/ws/jakarta/xml-crimson" />
+  <property name="xmlcommons.src" value="/ws/jakarta/xml-commons" />
+  <property name="servletapi.src" value="/ws/jakarta/jakarta-servletapi" />
+  <path id="jkant" >
+    <pathelement location="build/jkant.jar"/>
+  </path>
+
+  <property name="native.dir" location="." />
+
+  <property name="so.debug" value="true" />
+  <property name="so.optimize" value="true" />
+  <property name="so.profile" value="false" />
+  
+  <!-- ==================== Targets ==================== -->
+
+  <target name="main" depends="all">
+  </target>
+  
+  <target name="init" >
+    <taskdef resource="META-INF/ant.tasks" 
+	     classpathref="jkant" />
+    <mkdir dir="build"/>
+    <mkdir dir="build/obj"/>
+  </target>
+
+  <target name="all" depends="init,org-apache-crimson,javax-servlet,javax-xml,org-apache-tomcat,examples,link" />  
+
+  <target name="link" >
+    <exec dir="build/obj" executable="gcj">
+      <arg value="--main=org.apache.tomcat.startup.EmbededTomcat"/>
+      <arg value="-Dtomcat.home=."/>
+      <arg value="-Djavax.xml.parsers.SAXParserFactory=org.apache.crimson.jaxp.SAXParserFactoryImpl" />
+      <arg value="-g"/>
+      <arg value="-o"/>
+      <arg value="tomcat"/>
+      <arg value="lib-org-apache-tomcat.so"/>
+      <arg value="lib-javax-servlet.so"/>
+      <arg value="lib-org-apache-crimson.so"/>
+      <arg value="lib-javax-xml.so"/>
+      <arg value="lib-examples.so"/>
+    </exec>
+  </target>
+
+  <target name="org-apache-tomcat" depends="init">
+    <property name="build.compiler.cc" value="gcj" />
+    <so sofile="lib-org-apache-tomcat" 
+	buildDir="build/obj" 
+	optimize="${so.optimize}"
+	debug="${so.debug}"
+	profile="${so.profile}"
+	taskDebug="0"
+	>
+      <src dir="${tomcat.src}/src/share">
+	<include name="**/*.java" />
+	<include name="**/*.properties" />
+	<include name="**/*.dtd" />
+	<exclude name="org/apache/tomcat/util/compat/JSSECertCompat.java" />
+	<exclude name="org/apache/tomcat/util/net/SSLSocketFactory.java" />
+	<exclude name="org/apache/tomcat/util/test/**/*.java" />
+	<exclude name="org/apache/tomcat/modules/server/JNI*.java" />
+	<exclude name="org/apache/jasper/**/*.java" /> 
+	<exclude name="org/apache/jasper/compiler/CommandLineCompiler.java" />
+	<exclude name="org/apache/jasper/compiler/SunJavaCompiler.java" />
+      </src>
+      <src dir="${tomcat.src}/src/facade22">
+	<include name="**/*.java" />
+	<exclude name="org/apache/tomcat/facade/JspInterceptor.java" />
+	<exclude name="org/apache/tomcat/facade/TagPoolManagerInterceptor.java" />
+      </src>
+      <includes>
+        <include name="${LIB}/crimson-1.1.3/crimson.jar" />
+        <include name="${tomcat.src}/src/share" />
+        <include name="${tomcat.src}/src/facade22" />
+        <include name="${LIB}/jakarta-servletapi-22/lib/servlet.jar" />
+      </includes>
+    </so>
+  </target>
+
+  <target name="ant" depends="init">
+    <property name="build.compiler.cc" value="gcj" />
+    <so sofile="lib-org-apache-tools-ant" 
+	buildDir="build/obj" 
+	optimize="${so.optimize}"
+	debug="${so.debug}"
+	profile="${so.profile}"
+	taskDebug="0"
+	>
+      <src dir="${ant.src}/src/main">
+	<include name="**/*.java" />
+	<include name="**/*.properties" />
+	<include name="**/*.txt" />
+	<include name="**/*.dtd" />
+	<exclude name="org/apache/tools/ant/taskdefs/optional/**" />
+	<exclude name="org/apache/tools/ant/types/optional/**" />
+	<exclude name="org/apache/tools/ant/util/depend/**" />
+	<exclude name="org/apache/tools/ant/util/regexp/JakartaOroMatcher.java" />
+	<exclude name="org/apache/tools/ant/util/regexp/JakartaRegexpMatcher.java" />
+	<exclude name="org/apache/tools/ant/util/regexp/Jdk14RegexpMatcher.java" />
+	<exclude name="org/apache/tools/ant/util/regexp/Jdk14RegexpRegexp.java" />
+	<exclude name="org/apache/tools/ant/util/regexp/JakartaOroRegexp.java" />
+	<exclude name="org/apache/tools/ant/util/regexp/JakartaRegexpRegexp.java" />
+	<exclude name="org/apache/tools/ant/listener/Log4jListener.java" />
+      </src>
+      <includes>
+        <include name="${LIB}/crimson-1.1.3/crimson.jar" />
+        <include name="${ant.src}/src/main" />
+      </includes>
+    </so>
+    <exec dir="build/obj" executable="gcj">
+      <arg value="--main=org.apache.tools.ant.Main"/>
+      <arg value="-Dant.home=."/>
+      <arg value="-Djavax.xml.parsers.SAXParserFactory=org.apache.crimson.jaxp.SAXParserFactoryImpl" />
+      <arg value="-g"/>
+      <arg value="-o"/>
+      <arg value="ant"/>
+      <arg value="lib-org-apache-tools-ant.so"/>
+      <arg value="lib-org-apache-crimson.so"/>
+      <arg value="lib-javax-xml.so"/>
+    </exec>
+  </target>
+
+  <target name="examples" depends="init">
+    <property name="build.compiler.cc" value="gcj" />
+    <so sofile="lib-examples" 
+	buildDir="build/obj" 
+	optimize="${so.optimize}"
+	debug="${so.debug}"
+	profile="${so.profile}"
+	taskDebug="0"
+	>
+      <src dir="${tomcat.src}/build/tomcat/webapps/examples/WEB-INF/classes">
+	<include name="**/*.java" />
+      </src>
+      <includes>
+        <include name="${LIB}/crimson-1.1.3/crimson.jar" />
+        <include name="${tomcat.src}/src/share" />
+        <include name="${tomcat.src}/src/facade22" />
+        <include name="${tomcat.src}/build/tomcat/webapps/examples/WEB-INF/classes" />
+        <include name="${LIB}/jakarta-servletapi-22/lib/servlet.jar" />
+      </includes>
+    </so>
+  </target>
+
+  <target name="javax-servlet" depends="init">
+    <property name="build.compiler.cc" value="gcj" />
+    <so sofile="lib-javax-servlet" 
+	buildDir="build/obj" 
+	optimize="${so.optimize}"
+	debug="${so.debug}"
+	profile="${so.profile}"
+	taskDebug="0"
+	>
+      <src dir="${servletapi.src}/src/share">
+	<include name="**/*.java" />
+	<include name="**/*.properties" />
+      </src>
+      <includes>
+        <include name="${servletapi.src}/src/share" />
+      </includes>
+    </so>
+  </target>
+
+  <target name="org-apache-crimson" depends="init">
+    <property name="build.compiler.cc" value="gcj" />
+    <so sofile="lib-org-apache-crimson" 
+	buildDir="build/obj" 
+	optimize="${so.optimize}"
+	debug="${so.debug}"
+	profile="${so.profile}"
+	taskDebug="0"
+	>
+      <src dir="${crimson.src}/src">
+	<include name="**/*.java" />
+	<include name="**/*.properties" />
+	<include name="META-INF/**" />
+	<exclude name="org/w3c/**" />
+	<exclude name="org/xml/**" />
+      </src>
+      <includes>
+        <include name="${crimson.src}/src" />
+        <include name="${xmlcommons.src}/java/external/src" />
+      </includes>
+    </so>
+  </target>
+
+  <target name="javax-xml" depends="init">
+    <property name="build.compiler.cc" value="gcj" />
+    <so sofile="lib-javax-xml" 
+	buildDir="build/obj" 
+	optimize="${so.optimize}"
+	debug="${so.debug}"
+	profile="${so.profile}"
+	taskDebug="0"
+	>
+      <src dir="${xmlcommons.src}/java/external/src">
+	<include name="javax/xml/**/*.java" />
+	<include name="**/*.properties" />
+	<exclude name="org/**" />
+      </src>
+      <includes>
+        <include name="${crimson.src}/src" />
+        <include name="${xmlcommons.src}/java/external/src" />
+      </includes>
+    </so>
+  </target>
+
+  <target name="clean" >
+    <delete >
+      <fileset dir=".">
+	<include name="**/*.o"/>
+	<include name="**/*.so"/>
+	<include name="**/*.lo"/>
+	<include name="**/*.la"/>
+	<include name="**/.libs"/>
+	<include name="**/*.nlm"/>
+	<include name="**/*.map"/>
+	<include name="**/*.sym"/>
+      </fileset>
+    </delete>
+  </target>
+
+</project>
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/ApacheConfig.java b/connectors/jk/jkant/java/org/apache/jk/ant/ApacheConfig.java
new file mode 100644
index 0000000..09d8ca9
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/ApacheConfig.java
@@ -0,0 +1,53 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ *  Set preferences for compiling Apache modules.
+ *  XXX will use apxs query to detect the flags.
+ * 
+ * @author Costin Manolache
+ */
+public class ApacheConfig {
+    
+    
+    public ApacheConfig() {
+    }
+
+    public void setApxs( String s ) {
+
+    }
+    
+    /** Return include path for Apache
+     */
+    public String[] getIncludes() {
+	return null;
+    }
+
+    /** Return extra C flags that are needed to compile
+     */
+    public String[] getCflags() {
+	return null;
+    }
+    
+    public void execute() throws BuildException {
+
+    }
+    
+}
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/Def.java b/connectors/jk/jkant/java/org/apache/jk/ant/Def.java
new file mode 100644
index 0000000..fa511ca
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/Def.java
@@ -0,0 +1,82 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant;
+
+import org.apache.tools.ant.Project;
+
+/** Define name/value, value is optional
+ *  The define will take place if the condition
+ *  is met.
+ *
+ *  A <so> task should include all the defines it supports,
+ *  with the default value. This allows the builder to customize
+ *  without having to look at the source.
+ */
+public class Def {
+    String name;
+    String info;
+    String value;
+    String ifCond;
+    String unlessCond;
+    Project project;
+
+    public Def() {
+    }
+
+    public void setProject( Project p ) {
+	project=p;
+    }
+    
+    public void setName(String n) {
+	name=n;
+    }
+
+    public void setValue( String v ) {
+	value=v;
+    }
+
+    public void setIf( String ifCond ) {
+	this.ifCond=ifCond;
+    }
+
+    public void setUnless( String unlessCond ) {
+	this.unlessCond=unlessCond;
+    }
+
+    /** Informations about the option
+     */
+    public void setInfo(String n ) {
+	info=n;
+    }
+
+    // -------------------- Getters --------------------
+
+    /** Return the name of the define, or null if the define is not
+     *  valid ( if/unless conditions )
+     */
+    public String getName() {
+	if( ifCond!=null && project.getProperty(ifCond) == null )
+	    return null;
+	if (unlessCond != null && project.getProperty(unlessCond) != null) 
+	    return null;
+	return name;
+    }
+
+    public String getValue() {
+	return value;
+    }
+}
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/JkData.java b/connectors/jk/jkant/java/org/apache/jk/ant/JkData.java
new file mode 100644
index 0000000..3f27854
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/JkData.java
@@ -0,0 +1,85 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant;
+
+import org.apache.tools.ant.Project;
+
+/**
+ *  Set platform specific data.  Can be used to specify files (fileName="value")
+ *  symbols (symbol="value") or generic values (value="value").  A platform
+ *  can also be set on the object so that platform specific compilers can
+ *  determine if they want to use the values.
+ *  XXX will use apxs query to detect the flags.
+ * 
+ * @author Mike Anderson
+ */
+public class JkData {
+
+    private String value;
+    private boolean isfile = false;
+    private String ifCond;
+    String unlessCond;
+    Project project;
+    
+    
+    public JkData() {
+    }
+
+    public void setProject( Project p ) {
+        project=p;
+    }
+    
+    public void setFileName( String s ) {
+        value = s;
+        isfile = true;
+    }
+    
+    public void setSymbol( String s ) {
+        value = s;
+        isfile = false;
+    }
+
+    public void setValue( String s ) {
+        value = s;
+    }
+
+    public void setIsFile( boolean isf ) {
+        isfile = isf;
+    }
+
+    public void setIf( String s ) {
+        ifCond = s;
+    }
+
+    public void setUnless( String s ) {
+        unlessCond = s;
+    }
+
+    public String getValue()
+    {
+        if( ifCond!=null && project.getProperty(ifCond) == null )
+            return null;
+        if (unlessCond != null && project.getProperty(unlessCond) != null) 
+            return null;
+        return value;
+    }
+
+    public boolean isFile()
+    {
+        return isfile;
+    }
+}
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/JniConfig.java b/connectors/jk/jkant/java/org/apache/jk/ant/JniConfig.java
new file mode 100644
index 0000000..9deef61
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/JniConfig.java
@@ -0,0 +1,59 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant;
+
+import org.apache.tools.ant.BuildException;
+
+/*  Base task for 'config guessers'.
+ *  Each guesser will set properties based on OS, environment,
+ *  properties, well-known locations, other programs.
+ *  
+ *  Examples:
+ *   - set the required include files for JNI compilation
+ *   - use apxs to detect apache directories and flags
+ *
+ *  XXX Should be usable at top-level as well as in <so>
+ */
+
+/**
+ *  Set preferences for compiling Jni .so files.
+ * 
+ * @author Costin Manolache
+ */
+public class JniConfig  {
+    String includes[];
+    
+    public JniConfig() {
+    }
+
+    /** Return include path for JNI
+     */
+    public String[] getIncludes() {
+	return null;
+    }
+
+    /** Return extra C flags that are needed to compile
+     */
+    public String[] getCflags() {
+	return null;
+    }
+    
+    public void execute() throws BuildException {
+	
+    }
+    
+}
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/SoTask.java b/connectors/jk/jkant/java/org/apache/jk/ant/SoTask.java
new file mode 100644
index 0000000..3beb7fe
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/SoTask.java
@@ -0,0 +1,539 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.jk.ant.compilers.CcCompiler;
+import org.apache.jk.ant.compilers.CompilerAdapter;
+import org.apache.jk.ant.compilers.GcjCompiler;
+import org.apache.jk.ant.compilers.GcjLinker;
+import org.apache.jk.ant.compilers.LibtoolCompiler;
+import org.apache.jk.ant.compilers.LibtoolLinker;
+import org.apache.jk.ant.compilers.LinkerAdapter;
+import org.apache.jk.ant.compilers.MsvcCompiler;
+import org.apache.jk.ant.compilers.MsvcLinker;
+import org.apache.jk.ant.compilers.MwccCompiler;
+import org.apache.jk.ant.compilers.MwldLinker;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.taskdefs.PumpStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.PatternSet;
+
+/** Global properties
+
+    Same idea as in javac, some user .properties will set the local preferences,
+    including machine-specific. If one is not specified we'll guess it. The
+    build file will be clean.
+
+    TODO: can we get configure to generate such a file ? 
+
+    build.native.cc=gcc
+    # Path to libtool ( used as a backup )
+    build.native.libtool=
+    # Platform-specific flags for compilation.
+    build.native.extra_cflags=
+*/
+/* XXX add a optional "compiler" attribute 
+    to not guess the compiler based on the executable name 
+    present in a global property.
+
+*/
+
+
+/**
+ * Task to generate a .so file, similar with ( or using ) libtool.
+ * I hate libtool, so long term I would like to replace most of it
+ * with decent java code. Short term it'll just use libtool and
+ * hide some of the ugliness.
+ * 
+ * arguments:
+ * <ul>
+ * <li>source
+ * </ul>
+ *
+ * <p>
+ *
+ * @author Costin Manolache
+ * @author Mike Anderson
+ * @author Ignacio J. Ortega
+ */
+public class SoTask extends Task {
+    protected String apxs;
+    // or FileSet ?
+    protected Vector src; //[FileSet]
+    protected PatternSet includes;
+    protected Path depends;
+    protected Path libs;
+    protected String module;
+    protected String soFile;
+    protected String soExt = ".so";
+    protected String cflags;
+    protected File buildDir;
+    protected int debug;
+
+    protected boolean optG=true;
+    protected boolean optWgcc=true;
+    protected boolean optimize=false;
+    protected boolean profile=false;
+    protected Vector defines    = new Vector();
+    protected Vector imports    = new Vector();     // used by the NetWare, win32 linkers
+    protected Vector exports    = new Vector();     // used by the NetWare, win32 linkers
+    protected Vector modules    = new Vector();     // used by the NetWare linker
+    protected Vector linkOpts   = new Vector();     // used by the NetWare, win32 linkers
+    protected Vector altSoFiles = new Vector();     // used by the NetWare linker
+    protected Vector resources  = new Vector();     // used by the win32 linker
+
+    // Computed fields 
+    //    protected Vector compileList; // [Source]
+    protected Vector srcList=new Vector();
+    protected CompilerAdapter compiler;
+    //    protected GlobPatternMapper co_mapper;
+    
+    public SoTask() {};
+
+    // Hack to allow individual compilers/linkers to work
+    // as regular Tasks, independnetly.
+    public void duplicateTo(SoTask so) {
+        // This will act as a proxy for the child task 
+        so.project=project;
+        so.target=target;
+        so.location=location;
+        so.taskName=taskName;
+        so.taskType=taskType;
+        
+        so.apxs=apxs;
+        so.src=src;
+        so.includes=includes;
+        so.depends=depends;
+        so.libs=libs;
+        so.module=module;
+        so.soFile=soFile;
+        so.soExt=soExt;
+        so.cflags=cflags;
+        so.buildDir=buildDir;
+        so.debug=debug;
+        so.optG=optG;
+        so.optWgcc=optWgcc;
+        so.optimize=optimize;
+        so.profile=profile;
+        so.defines=defines;
+        so.imports=imports;
+        so.exports=exports;
+        so.resources=resources;
+        so.modules=modules;
+        so.linkOpts=linkOpts;
+        so.srcList=srcList;
+        //      so.compileList=compileList;
+        so.compiler=compiler;
+        //      so.co_mapper=co_mapper;
+        so.altSoFiles=altSoFiles;
+    }
+
+    /**  @deprecated use setTarget
+     */
+    public void setSoFile(String s ) {
+        soFile=s;
+    }
+
+    /** Add debug information
+     */
+    public void setDebug(boolean b) {
+        optG=b;
+    }
+
+    /** Add debug information
+     */
+    public void setOptimize(boolean b) {
+        optimize=b;
+    }
+
+    /** Add profiling information
+     */
+    public void setProfile(boolean b) {
+        profile=b;
+    }
+
+    /** Gcc warnings
+     */
+    public void setGccWarn(boolean b) {
+        optWgcc=b;
+    }
+
+    /** Debug the <so> task
+     */
+    public void setTaskDebug(int i) {
+        debug=i;
+    }
+
+    /** Add a -D option. Note that each define has
+     *  an if/unless attribute
+     */ 
+    public void addDef(Def var ) {
+        var.setProject( project );
+        defines.addElement(var);
+    }
+
+    /**
+     * Add an import file/symbol for NetWare or win32 platform
+     *
+     * 
+     */
+    public void addImport(JkData imp) {
+        imp.setProject( project );
+        imports.add(imp);
+    }
+
+    /**
+     * Add an export file/symbol for NetWare or win32 platform
+     *
+     * 
+     */
+    public void addExport(JkData exp) {
+        exp.setProject( project );
+        exports.add(exp);
+    }
+
+    /**
+     * Add an resource file on win32 platform
+     *
+     * 
+     */
+    public void addResource(JkData res) {
+        res.setProject( project );
+        resources.add(res);
+    }
+
+    /**
+     * Add a link option for NetWare or win32 platform
+     *
+     * 
+     */
+    public void addLinkOpt(JkData option) {
+        option.setProject( project );
+        linkOpts.add(option);
+    }
+
+    /**
+     * Add an NLMModule dependancy
+     *
+     * 
+     */
+    public void addNLMModule(JkData module) {
+        module.setProject( project );
+        modules.add(module);
+    }
+
+    /**
+     * Add an alternate target since some platforms (NetWare) have file name
+     * limitations.
+     * 
+     */
+    public void addAltSoFile(JkData altSo) {
+        altSo.setProject( project );
+        altSoFiles.add(altSo);
+    }
+
+    /** Set the target for this compilation. Don't include any
+     *  directory or suffix ( not sure about prefix - we may want
+     *  to add lib automatically for unix, and nothing on win/etc ?  ).
+     */
+    public void setTarget(String s ) {
+        soFile=s;
+    }
+
+    /** Set the extension for the target.  This will depend on the platform
+     *  we are compiling for.
+     */
+    public void setExtension(String s ) {
+        soExt=s;
+    }
+
+    /** Directory where intermediary objects will be
+     *  generated
+     */
+    public void setBuildDir( File s ) {
+        buildDir=s;
+    }
+
+    public void setCflags(String s ) {
+        cflags=s;
+    }
+    
+    /** Directory where the .so file will be generated
+     */
+    public void setSoDir( String s ) {
+        
+    }
+
+    public void addJniConfig( JniConfig jniCfg ) {
+
+    }
+
+    public void addApacheConfig( ApacheConfig apacheCfg ) {
+
+    }
+    
+    
+    /**
+     * Source files ( .c )
+     *
+     * @return a nested src element.
+     */
+    public void addSrc(FileSet fl) {
+        if( src==null ) src=new Vector();
+        src.addElement(fl);
+    }
+
+    /**
+     * Include files
+     */
+    public PatternSet createIncludes() {
+        includes=new PatternSet(); //Path(project);
+        return includes;
+    }
+
+    /**
+     * General dependencies. If any of the files is modified
+     * ( i.e. is newer than the oldest .o ) we'll recompile everything.
+     *
+     * This can be used for headers ( until we add support for makedepend)
+     * or any important file that could invalidate the build.
+     * Another example is checking httpd or apxs ( if a new version
+     * was installed, maybe some flags or symbols changed )
+     */
+    public Path createDepends() {
+        depends=new Path(project);
+        return depends;
+    }
+
+    /**
+     * Libraries ( .a, .so or .dll ) files to link to.
+     */
+    public Path createLibs() {
+        libs=new Path(project);
+        return libs;
+    }
+    
+    
+    /**
+     * The name of the target file.
+     * (XXX including extension - this should be automatically added )
+     */
+    public void setModule(String modName) {
+        this.module = modName;	// Should be this ?
+    }
+
+    // XXX Add specific code for Netware, Windows and platforms where libtool
+    // is problematic
+
+    // XXX Add specific code for Linux and platforms where things are
+    // clean, libtool should be just a fallback.
+    public void execute() throws BuildException {
+        compiler=findCompilerAdapter();
+        //      co_mapper=compiler.getOMapper();
+        LinkerAdapter linker=findLinkerAdapter();
+
+        if( soFile==null )
+            throw new BuildException("No target ( " + soExt + " file )");
+        if (src == null) 
+            throw new BuildException("No source files");
+
+        // XXX makedepend-type dependencies - how ??
+        // We could generate a dummy Makefile and parse the content...
+        findSourceFiles();
+
+        // Copy all settings into compiler
+        this.duplicateTo(compiler);
+        compiler.compile( srcList );
+
+        // XXX move this checking to linker
+        File soTarget=new File( buildDir, soFile + soExt );
+        if( compiler.getCompiledFiles().size() == 0 && soTarget.exists()) {
+            // No dependency, no need to relink
+            return;
+        }
+
+        this.duplicateTo(linker);
+        linker.link(srcList);
+    }
+
+    public CompilerAdapter findCompilerAdapter() {
+        CompilerAdapter compilerAdapter;
+        String cc;
+        cc=project.getProperty("build.compiler.cc");
+        if( cc!=null ) {
+            if( "cc".equals( cc ) ) {
+                compilerAdapter=new CcCompiler();
+                compilerAdapter.setSoTask( this );
+                return compilerAdapter;
+            }
+            if( "gcj".equals( cc ) ) {
+                compilerAdapter=new GcjCompiler();
+                compilerAdapter.setSoTask( this );
+                return compilerAdapter;
+            }
+            if( cc.indexOf("mwccnlm") != -1 ) {
+                compilerAdapter=new MwccCompiler();
+                compilerAdapter.setSoTask( this );
+                return compilerAdapter;
+            }
+            if( cc.indexOf("cl") != -1 ) {
+                compilerAdapter=new MsvcCompiler();
+                compilerAdapter.setSoTask( this );
+                return compilerAdapter;
+            }
+        }
+        
+        compilerAdapter=new LibtoolCompiler(); 
+        compilerAdapter.setSoTask( this );
+        return compilerAdapter;
+   }
+
+    public LinkerAdapter findLinkerAdapter() {
+        LinkerAdapter linkerAdapter;
+        String ld=project.getProperty("build.compiler.ld");
+        if( ld!=null ) {
+            if( ld.indexOf("mwldnlm") != -1 ) {
+                linkerAdapter=new MwldLinker();
+                linkerAdapter.setSoTask( this );
+                return linkerAdapter;
+            }
+            if( ld.indexOf("link") != -1 ) {
+                linkerAdapter=new MsvcLinker();
+                linkerAdapter.setSoTask( this );
+                return linkerAdapter;
+            }
+            //      if( "ld".equals( cc ) ) {
+            //          linkerAdapter=new LdLinker();
+            //          linkerAdapter.setSoTask( this );
+            //          return cc;
+            //      }
+        }
+
+        String cc=project.getProperty("build.compiler.cc");
+        if( "gcj".equals( cc ) ) {
+            linkerAdapter=new GcjLinker();
+            linkerAdapter.setSoTask( this );
+            return linkerAdapter;
+        }
+
+        
+        linkerAdapter=new LibtoolLinker(); 
+        linkerAdapter.setSoTask( this );
+        return linkerAdapter;
+   }
+
+    /** Find all source files declared with <src> elements
+     */
+    public void findSourceFiles() {
+        if (buildDir == null) buildDir = project.getBaseDir();
+
+        Enumeration e=src.elements();
+        while( e.hasMoreElements() ) {
+            FileSet fs=(FileSet)e.nextElement();
+            DirectoryScanner ds=fs.getDirectoryScanner( project );
+            String localList[]= ds.getIncludedFiles(); 
+            if (localList.length == 0) 
+                throw new BuildException("No source files ");
+            for( int i=0; i<localList.length; i++ ) {
+                srcList.addElement( new Source( fs.getDir(project), localList[i]));
+            }
+        }
+    }
+ 
+    /** If any file declared in <depend> element has changed, we'll do
+        a full rebuild.
+    */
+    public boolean checkDepend(long oldestO, File oldestOFile) {
+        if( depends==null )
+            return false;
+        String dependsA[]=depends.list(); 
+        for( int i=0; i< dependsA.length; i++ ) {
+            File f=new File( dependsA[i] );
+            if( ! f.exists() ) {
+                log("Depend not found " + f );
+                return true;
+            }
+            if( f.lastModified() > oldestO ) {
+                log( "Depend " + f + " newer than " + oldestOFile );
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    // ==================== Execution utils ==================== 
+
+    protected ExecuteStreamHandler streamhandler = null;
+    protected ByteArrayOutputStream outputstream = null;
+    protected ByteArrayOutputStream errorstream = null;
+
+    public int execute( Commandline cmd ) throws BuildException
+    {
+        createStreamHandler();
+        Execute exe = new Execute(streamhandler, null);
+        exe.setAntRun(project);
+
+        exe.setWorkingDirectory(buildDir);
+
+        exe.setCommandline(cmd.getCommandline());
+        int result=0;
+        try {
+            result=exe.execute();
+        } catch (IOException e) {
+            throw new BuildException(e, location);
+        } 
+        return result;
+    }
+
+    public void createStreamHandler()  throws BuildException {
+        //      try {
+        outputstream= new ByteArrayOutputStream();
+        errorstream = new ByteArrayOutputStream();
+        
+        streamhandler =
+            new PumpStreamHandler(new PrintStream(outputstream),
+                                  new PrintStream(errorstream));
+        //      }  catch (IOException e) {
+        //          throw new BuildException(e,location);
+        //      }
+    }
+
+    public void closeStreamHandler() {
+        try {
+            if (outputstream != null) 
+                outputstream.close();
+            if (errorstream != null) 
+                errorstream.close();
+            outputstream=null;
+            errorstream=null;
+        } catch (IOException e) {}
+    }
+}
+
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/Source.java b/connectors/jk/jkant/java/org/apache/jk/ant/Source.java
new file mode 100644
index 0000000..1f054c9
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/Source.java
@@ -0,0 +1,50 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant;
+
+import java.io.File;
+
+import org.apache.tools.ant.util.GlobPatternMapper;
+
+public class Source {
+    File dir;
+    String localPart;
+    
+    public Source( File dir, String localPart ) {
+	this.dir=dir;
+	this.localPart=localPart;
+    }
+
+    public String getTargetFile( GlobPatternMapper mapper ) {
+	String targetNA[]=mapper.mapFileName( localPart );
+	if( targetNA==null )
+	    return null; // strange, probably different extension ?
+	String target=targetNA[0];
+	return target;
+    }
+
+    public File getFile() {
+	return  new File( dir, localPart );
+    }
+
+    public String getPackage() {
+	int lastSlash=localPart.lastIndexOf("/");
+	if( lastSlash==-1 )
+	    return "";
+	return localPart.substring( 0, lastSlash );
+    }
+}
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/compilers/CcCompiler.java b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/CcCompiler.java
new file mode 100644
index 0000000..a014e68
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/CcCompiler.java
@@ -0,0 +1,126 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant.compilers;
+
+import java.io.File;
+
+import org.apache.jk.ant.Source;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.GlobPatternMapper;
+
+/**
+ *  Compile using Gcc.
+ *
+ * @author Costin Manolache
+ */
+public class CcCompiler extends CompilerAdapter {
+    GlobPatternMapper co_mapper=new GlobPatternMapper();
+
+    public CcCompiler() {
+	super();
+	co_mapper.setFrom("*.c");
+	co_mapper.setTo("*.o");
+    }
+
+    public String[] getTargetFiles( Source src ) {
+        File srcFile = src.getFile();
+        String name=srcFile.getName();
+        
+        return co_mapper.mapFileName( name );
+    }
+    
+    String cc;
+    
+    /** Compile  using 'standard' gcc flags. This assume a 'current' gcc on
+     *  a 'normal' platform - no need for libtool
+     */
+    public void compileSingleFile(Source sourceObj) throws BuildException {
+	File f=sourceObj.getFile();
+	String source=f.toString();
+	Commandline cmd = new Commandline();
+
+	cc=project.getProperty("build.native.cc");
+	if(cc==null) cc="cc";
+	
+	cmd.setExecutable( cc );
+
+	cmd.createArgument().setValue( "-c" );
+
+	addIncludes(cmd);
+	addExtraFlags( cmd );
+	addDebug(cmd);
+	addDefines( cmd );
+	addOptimize( cmd );
+	addProfile( cmd );
+
+	cmd.createArgument().setValue( source );
+
+	project.log( "Compiling " + source);
+
+	int result=execute( cmd );
+        displayError( result, source, cmd );
+	closeStreamHandler();
+    }
+    protected void addDebug(Commandline cmd) {
+	if( optG ) {
+	    cmd.createArgument().setValue("-g" );
+        }
+
+        if( optWgcc ) {
+	    if( ! "HP-UX".equalsIgnoreCase( System.getProperty( "os.name" )) ) {
+                // HP-UX uses -W for some other things
+                cmd.createArgument().setValue("-W");
+            }
+
+            if( cc!= null && cc.indexOf( "gcc" ) >= 0 ) {
+                //cmd.createArgument().setValue("-Wall");
+                cmd.createArgument().setValue("-Wimplicit");
+                cmd.createArgument().setValue("-Wreturn-type");
+                cmd.createArgument().setValue("-Wcomment");
+                cmd.createArgument().setValue("-Wformat");
+                cmd.createArgument().setValue("-Wchar-subscripts");
+                cmd.createArgument().setValue("-O");
+                cmd.createArgument().setValue("-Wuninitialized");
+                
+                // Non -Wall
+                // 	    cmd.createArgument().setValue("-Wtraditional");
+                // 	    cmd.createArgument().setValue("-Wredundant-decls");
+                cmd.createArgument().setValue("-Wmissing-declarations");
+                cmd.createArgument().setValue("-Wmissing-prototypes");
+                //	    cmd.createArgument().setValue("-Wconversions");
+                cmd.createArgument().setValue("-Wcast-align");
+                // 	    cmd.createArgument().setValue("-pedantic" );
+            }
+	}
+    }
+    protected void addOptimize( Commandline cmd ) {
+	if( optimize )
+	    cmd.createArgument().setValue("-O3" );
+    }
+
+    protected void addProfile( Commandline cmd ) {
+	if( profile ) {
+	    cmd.createArgument().setValue("-pg" );
+	    // bb.in 
+	    // cmd.createArgument().setValue("-ax" );
+	}
+    }
+
+
+}
+
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/compilers/CompilerAdapter.java b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/CompilerAdapter.java
new file mode 100644
index 0000000..e8f2d3b
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/CompilerAdapter.java
@@ -0,0 +1,288 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant.compilers;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.jk.ant.Def;
+import org.apache.jk.ant.SoTask;
+import org.apache.jk.ant.Source;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+
+/* Modeled after javac
+ */
+
+/**
+ * s/javac/C compiler/
+ *
+ * The interface that all compiler adapters must adher to.  
+ *
+ * <p>A compiler adapter is an adapter that interprets the javac's
+ * parameters in preperation to be passed off to the compier this
+ * adapter represents.  As all the necessary values are stored in the
+ * Javac task itself, the only thing all adapters need is the javac
+ * task, the execute command and a parameterless constructor (for
+ * reflection).</p>
+ *
+ * @author Jay Dickon Glanville <a href="mailto:jayglanville@home.com">jayglanville@home.com</a>
+ * @author Costin Manolache
+ */
+public abstract class CompilerAdapter extends SoTask {
+    SoTask so;
+    Vector compileList;
+
+    public CompilerAdapter() {
+	so=this;
+    };
+
+    public void setSoTask(SoTask so ) {
+	this.so=so;
+	so.duplicateTo( this );
+    }
+
+//     /** @deprecated
+//      */
+//     public GlobPatternMapper getOMapper() {
+//         return null;
+//     }
+
+    /** Return the files that depend on a src file.
+     *  The first item should be the .o file used for linking.
+     */
+    public abstract String[] getTargetFiles( Source src );
+
+    public void execute() throws BuildException {
+        super.findSourceFiles();
+        Vector compileList=findCompileList(srcList);
+	compile( compileList );
+    }
+
+    /** Verify if a .c file needs compilation.
+     *	As with javac, we assume a fixed build structure, where all .o
+     *	files are in a separate directory from the sources ( no mess ).
+     *
+     *  XXX Hack makedepend somehow into this.
+     */
+    public boolean needCompile( Source source ) {
+	// For each .c file we'll have a .o file in the build dir,
+	// with the same name.
+	File srcF = source.getFile();
+	if( !srcF.exists() ) {
+            if( debug > 0 )
+                log("No source file " + srcF ); 
+            return false;
+        }
+
+	String targetNames[]= getTargetFiles( source );
+	if( targetNames==null || targetNames.length==0 ) {
+            if( debug > 0 )
+                log("No target files " + srcF ); 
+	    return true; // strange, probably different extension ?
+        }
+        String targetName=targetNames[0];
+
+        String targetDir=source.getPackage();
+        File f1=new File( buildDir, targetDir );
+        File target=new File( f1, targetName );
+	//	System.out.println("XXX " + target );
+	if( ! target.exists() ) {
+            if( debug > 0 )
+                log("Target doesn't exist " + target ); 
+	    return true;
+        }
+	if( oldestO > target.lastModified() ) {
+	    oldestO=target.lastModified();
+	    oldestOFile=target;
+	}
+	if( srcF.lastModified() > target.lastModified() ) 
+	    return true;
+
+	if( debug > 0 )
+	    log("No need to compile " + srcF + " target " + target ); 
+	return false;
+    }
+
+    /** Remove all generated files, cleanup
+     */
+    public void removeOFiles( Vector srcList ) {
+        for (int i = 0; i < srcList.size(); i++) {
+            //            log( "Checking " + (Source)srcList.elementAt(i));
+            Source source=(Source)srcList.elementAt(i);
+	    String targetNA[]=getTargetFiles(source);
+	    if( targetNA==null )
+		continue;
+            String targetDir=source.getPackage();
+            File f1=new File( buildDir, targetDir );
+            for( int j=0; j<targetNA.length; j++ ) {
+                File target=new File( f1, targetNA[j] );
+                // Check the dependency
+                if( target.exists() ) {
+                    // Remove it - we'll do a full build
+                    target.delete();
+                    log("Removing " + target );
+                }
+            }
+        }
+    }
+
+    // XXX modified as side-effect of checking files with needCompile()
+    long oldestO=System.currentTimeMillis();
+    File oldestOFile=null;
+
+    /** Find the subset of the source list that needs compilation.
+     */
+    protected Vector findCompileList(Vector srcList) throws BuildException {
+        Vector compileList=new Vector();
+
+        for (int i = 0; i < srcList.size(); i++) {
+	    Source source=(Source)srcList.elementAt(i);
+	    File srcFile=source.getFile();
+	   
+            if (!srcFile.exists()) {
+                throw new BuildException("Source \"" + srcFile.getPath() +
+                                         "\" does not exist!", location);
+            }
+
+	    // Check the dependency
+	    if( needCompile( source ) ) 
+		compileList.addElement( source );
+	}
+
+	if( checkDepend(oldestO, oldestOFile) ) {
+	    log("Dependency expired, removing "
+                + srcList.size() + " .o files and doing a full build ");
+	    removeOFiles(srcList);
+            compileList=new Vector();
+	    for(int i=0; i<srcList.size(); i++ ) {
+		Source source=(Source)srcList.elementAt(i);
+		compileList.addElement( source );
+	    }
+            return compileList;
+	}
+
+
+        return compileList;
+    }
+    
+    /** Return the files that were actually compiled
+     */
+    public Vector getCompiledFiles() {
+        return compileList;
+    }
+
+    /** Compile the source files. The compiler adapter can override either this method
+     *  ( if it can compile multiple files at once ) or compileSingleFile().
+     *  Note that the list includes _all_ sources, findCompileList() can be used to
+     *  avoid compiling files that are up-to-date.
+     */
+    public void compile(Vector sourceFiles ) throws BuildException {
+        compileList=findCompileList(sourceFiles);
+
+        log("Compiling " + compileList.size() + " out of " + sourceFiles.size());
+	Enumeration en=compileList.elements();
+	while( en.hasMoreElements() ) {
+	    Source source=(Source)en.nextElement();
+	    compileSingleFile(source);
+	}
+    }
+    
+    /** Compile single file
+     */
+    public void compileSingleFile(Source sourceObj) throws BuildException {
+    }
+
+
+    protected void displayError( int result, String source, Commandline cmd )
+	throws BuildException
+    {
+        if( result == 0 ) {
+            String err=errorstream.toString();
+            if(err==null ) return;
+            if( err.indexOf( "warning" ) <= 0 )
+                return;
+            log("Warnings: ");
+            log( err );
+            return;
+        }
+        
+	log("Compile failed " + result + " " +  source );
+	log("Command:" + cmd.toString());
+	log("Output:" );
+	if( outputstream!=null ) 
+	    log( outputstream.toString());
+	log("StdErr:" );
+	if( errorstream!=null ) 
+	    log( errorstream.toString());
+	
+	throw new BuildException("Compile failed " + source);
+    }
+
+    protected void addIncludes(Commandline cmd) {
+	String [] includeList = ( includes==null ) ?
+	    new String[] {} : includes.getIncludePatterns(project); 
+	for( int i=0; i<includeList.length; i++ ) {
+	    cmd.createArgument().setValue("-I" + includeList[i] );
+	}
+    }
+
+    /** Common cc parameters
+     */
+    protected void addExtraFlags(Commandline cmd )  {
+	String extra_cflags=project.getProperty("build.native.extra_cflags");
+	String localCflags=cflags;
+	if( localCflags==null ) {
+	    localCflags=extra_cflags;
+	} else {
+	    if( extra_cflags!=null ) {
+		localCflags+=" " + extra_cflags;
+	    }
+ 	}
+	if( localCflags != null )
+	    cmd.createArgument().setLine( localCflags );
+    }
+
+    protected void addDefines( Commandline cmd ) {
+        // Define by default the OS ( as known to java )
+        String os=System.getProperty("java.os");
+
+        if( defines.size() > 0 ) {
+	    Enumeration defs=defines.elements();
+	    while( defs.hasMoreElements() ) {
+		Def d=(Def)defs.nextElement();
+		String name=d.getName();
+		String val=d.getValue();
+		if( name==null ) continue;
+		String arg="-D" + name;
+		if( val!=null )
+		    arg+= "=" + val;
+		cmd.createArgument().setValue( arg );
+            }
+        }
+    }
+
+    protected void addDebug(Commandline cmd) {
+    }
+
+    protected void addOptimize( Commandline cmd ) {
+    }
+
+    protected void addProfile( Commandline cmd ) {
+    }
+}
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/compilers/GcjCompiler.java b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/GcjCompiler.java
new file mode 100644
index 0000000..1888574
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/GcjCompiler.java
@@ -0,0 +1,116 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant.compilers;
+
+import java.io.File;
+
+import org.apache.jk.ant.Source;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ *  Compile using Gcj. This is ( even more ) experimental.
+ * 
+ * @author Costin Manolache
+ */
+public class GcjCompiler extends CcCompiler {
+    
+    public GcjCompiler() {
+	super();
+	co_mapper.setFrom("*.java");
+	co_mapper.setTo("*.o");
+    }
+
+    public String[] getTargetFiles( Source src ) {
+        File srcFile = src.getFile();
+        String name=srcFile.getName();
+        if( name.endsWith( ".java" ) ) {
+            return co_mapper.mapFileName( name );
+        } else {
+            return new String[]
+            { name + ".o" };
+        }
+    }
+
+    
+    /** Compile using libtool.
+     */
+    public void compileSingleFile(Source sourceObj) throws BuildException {
+	File f=sourceObj.getFile();
+	String source=f.toString();
+	Commandline cmd = new Commandline();
+
+	cmd.setExecutable( "gcj" );
+
+	cmd.createArgument().setValue("-c" );
+	
+	if( optG ) {
+            //	    cmd.createArgument().setValue("-g" );
+            cmd.createArgument().setValue("-ggdb3" );
+            //            cmd.createArgument().setValue("-save-temps" );
+	    //  cmd.createArgument().setValue("-Wall");
+	}
+	addOptimize( cmd );
+	addExtraFlags( cmd );
+	cmd.createArgument().setValue("-fPIC" );
+	addIncludes( cmd );
+        String targetDir=sourceObj.getPackage();
+	try {
+	    File f1=new File( buildDir, targetDir );
+	    f1.mkdirs();
+            cmd.createArgument().setValue( "-o" );
+            String targetO[]=getTargetFiles( sourceObj );
+            if( targetO==null ) {
+                log("no target for " + sourceObj.getFile() );
+                return;
+            }
+            File ff=new File( f1, targetO[0]);
+            cmd.createArgument().setValue( ff.toString() );
+	} catch( Exception ex ) {
+	    ex.printStackTrace();
+	}
+	
+        if( ! source.endsWith(".java") ) {
+            cmd.createArgument().setValue("-R" );
+            cmd.createArgument().setValue( targetDir + "/" + f.getName() );
+            //System.out.println("XXX resource " + targetDir + "/" + f.getName() );
+        } else {
+            //            cmd.createArgument().setValue("-fno-bounds-check" );
+        }
+        cmd.createArgument().setValue( source );
+	project.log( "Compiling " + source);
+
+	if( debug > 0 )
+	    project.log( "Command: " + cmd ); 
+	int result=execute( cmd );
+	if( result!=0 ) {
+	    displayError( result, source, cmd );
+	}
+	closeStreamHandler();
+    }
+
+    protected void addIncludes(Commandline cmd) {
+	String [] includeList = ( includes==null ) ?
+	    new String[] {} : includes.getIncludePatterns(project); 
+	for( int i=0; i<includeList.length; i++ ) {
+	    cmd.createArgument().setValue("-I" + includeList[i] );
+	}
+    }
+
+
+}
+
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/compilers/GcjLinker.java b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/GcjLinker.java
new file mode 100644
index 0000000..65aaf33
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/GcjLinker.java
@@ -0,0 +1,125 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant.compilers;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.jk.ant.JkData;
+import org.apache.jk.ant.SoTask;
+import org.apache.jk.ant.Source;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.GlobPatternMapper;
+
+/**
+ * Link java using gcj.
+ * 
+ * @author Costin Manolache
+ */
+public class GcjLinker extends LinkerAdapter {
+    SoTask so;
+    protected static GlobPatternMapper co_mapper=new GlobPatternMapper();
+    static {
+	co_mapper.setFrom("*.java");
+	co_mapper.setTo("*.o");
+    }
+    public GcjLinker() {
+	so=this;
+    }
+
+    public String[] getTargetFiles( Source src ) {
+        File srcFile = src.getFile();
+        String name=srcFile.getName();
+        if( name.endsWith( ".java" ) )
+            return co_mapper.mapFileName( name );
+        else
+            return new String[]
+            { name + ".o" };
+    }
+
+
+    
+    public void setSoTask(SoTask so ) {
+	this.so=so;
+	so.duplicateTo( this );
+    }
+
+    public void execute() throws BuildException {
+	findSourceFiles();
+	link(this.srcList);
+    }
+
+    /** Link using libtool.
+     */
+    public boolean link(Vector srcList) throws BuildException {
+//         link( srcList, false );
+        link( srcList, true );
+	return true;
+    }
+    public boolean link(Vector srcList, boolean shared) throws BuildException {
+	Commandline cmd = new Commandline();
+
+	cmd.setExecutable( "gcj" );
+
+        if( shared )
+            cmd.createArgument().setValue( "--shared" );
+	cmd.createArgument().setValue( "-o" );
+        if( shared )
+            cmd.createArgument().setValue( soFile + ".so" );
+        else
+            cmd.createArgument().setValue( soFile + ".a" );
+
+	project.log( "Linking " + buildDir + "/" + soFile + ".so");
+
+        // write out any additional link options
+        Enumeration opts = linkOpts.elements();
+        while( opts.hasMoreElements() ) {
+            JkData opt = (JkData) opts.nextElement();
+            String option = opt.getValue();
+            if( option == null ) continue;
+
+            cmd.createArgument().setValue( option );
+        }
+
+	for( int i=0; i<srcList.size(); i++ ) {
+	    Source source=(Source)srcList.elementAt(i);
+	    File f1=new File(buildDir, source.getPackage());
+	    File srcF=new File(f1, getTargetFiles(source)[0]);
+	    cmd.createArgument().setValue( srcF.toString() );
+	}
+	
+	int result=execute( cmd );
+	if( result!=0 ) {
+	    log("Link failed " + result );
+	    log("Command:" + cmd.toString());
+	    log("Output:" );
+	    if( outputstream!=null ) 
+		log( outputstream.toString());
+	    log("StdErr:" );
+	    if( errorstream!=null ) 
+		log( errorstream.toString());
+	    
+	    throw new BuildException("Link failed " + soFile);
+	}
+	closeStreamHandler();
+
+	return true;
+    }
+}
+
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/compilers/LibtoolCompiler.java b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/LibtoolCompiler.java
new file mode 100644
index 0000000..36227b3
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/LibtoolCompiler.java
@@ -0,0 +1,105 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant.compilers;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.jk.ant.Source;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ *  Compile using libtool.
+ * 
+ *  It extends SoTask so we can debug it or use it independently of <so>.
+ *  For normal use you should use the generic task, and system-specific
+ *  properties to choose the right compiler plugin ( like we select
+ *  jikes ).
+ *
+ * @author Costin Manolache
+ */
+public class LibtoolCompiler extends CcCompiler {
+
+    public LibtoolCompiler() {
+	super();
+    };
+
+    public void compile(Vector sourceFiles ) throws BuildException {
+        compileList=findCompileList(sourceFiles);
+        
+        log("Compiling " + compileList.size() + " out of " + sourceFiles.size());
+	Enumeration en=compileList.elements();
+	while( en.hasMoreElements() ) {
+	    Source source=(Source)en.nextElement();
+	    compileSingleFile(source);
+	}
+    }
+    
+
+    /** Compile using libtool.
+     */
+    public void compileSingleFile(Source sourceObj) throws BuildException {
+	File f=sourceObj.getFile();
+	String source=f.toString();
+	Commandline cmd = new Commandline();
+
+	String libtool=project.getProperty("build.native.libtool");
+	if(libtool==null) libtool="libtool";
+
+	cmd.setExecutable( libtool );
+	
+	cmd.createArgument().setValue("--mode=compile");
+
+	String cc=project.getProperty("build.native.cc");
+	if(cc==null) cc="cc";
+
+	cmd.createArgument().setValue( cc );
+
+	cmd.createArgument().setValue( "-c" );
+	
+	cmd.createArgument().setValue( "-o" );
+	
+	File ff=new File( buildDir, sourceObj.getTargetFile(co_mapper));
+	cmd.createArgument().setValue( ff.toString() );
+	try {
+	    String targetDir=sourceObj.getPackage();
+	    File f1=new File( buildDir, targetDir );
+	    f1.mkdirs();
+	    // System.out.println("mkdir " + f1 );
+	} catch( Exception ex ) {
+	    ex.printStackTrace();
+	}
+
+	addIncludes(cmd);
+	addExtraFlags( cmd );
+	addDebug(cmd);
+	addDefines( cmd );
+	addOptimize( cmd );
+	addProfile( cmd );
+
+	project.log( "Compiling " + source);
+	cmd.createArgument().setValue( source );
+
+        if( debug > 0 ) project.log(cmd.toString());
+	int result=execute( cmd );
+        displayError( result, source, cmd );
+	closeStreamHandler();
+    }
+}
+
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/compilers/LibtoolLinker.java b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/LibtoolLinker.java
new file mode 100644
index 0000000..f65814a
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/LibtoolLinker.java
@@ -0,0 +1,157 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant.compilers;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.jk.ant.JkData;
+import org.apache.jk.ant.SoTask;
+import org.apache.jk.ant.Source;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.GlobPatternMapper;
+
+/**
+ * Link using libtool.
+ * 
+ * @author Costin Manolache
+ */
+public class LibtoolLinker extends LinkerAdapter {
+    SoTask so;
+    GlobPatternMapper lo_mapper=new GlobPatternMapper();
+    public LibtoolLinker() {
+	so=this;
+	lo_mapper.setFrom("*.c");
+	lo_mapper.setTo("*.lo");
+    }
+
+    /** Link using libtool.
+     */
+    public boolean link(Vector srcList) throws BuildException {
+	so.duplicateTo( this );
+	Commandline cmd = new Commandline();
+
+	String libtool=project.getProperty("build.native.libtool");
+	if(libtool==null) libtool="libtool";
+
+	cmd.setExecutable( libtool );
+	
+	cmd.createArgument().setValue("--mode=link");
+
+	String cc=project.getProperty("build.native.cc");
+	if(cc==null) cc="cc";
+
+	cmd.createArgument().setValue( cc );
+	
+	cmd.createArgument().setValue("-module");
+	cmd.createArgument().setValue("-avoid-version");
+	cmd.createArgument().setValue("-rpath");
+	cmd.createArgument().setValue( buildDir.getAbsolutePath());
+
+	cmd.createArgument().setValue( "-o" );
+	cmd.createArgument().setValue( soFile + ".la" );
+
+	if( profile )
+	    cmd.createArgument().setValue("-pg" );
+
+        // write out any additional link options
+        Enumeration opts = linkOpts.elements();
+        while( opts.hasMoreElements() ) {
+            JkData opt = (JkData) opts.nextElement();
+            String option = opt.getValue();
+            if( option == null ) continue;
+
+            cmd.createArgument().setValue( option );
+        }
+        
+	// All .o files must be included
+	project.log( "Linking " + buildDir + "/" + soFile + ".so");
+
+        if( libs!=null ) {
+            String libsA[]=libs.list(); 
+            for( int i=0; i< libsA.length; i++ ) {
+                cmd.createArgument().setValue( "-l" + libsA[i] );
+                //XXX debug
+                project.log("XXX -l" + libsA[i] );
+            }
+        }
+	
+	for( int i=0; i<srcList.size(); i++ ) {
+	    Source sourceObj=(Source)srcList.elementAt(i);
+	    
+	    File ff=new File( buildDir, sourceObj.getTargetFile(lo_mapper));
+	    cmd.createArgument().setValue( ff.toString() );
+	}
+	
+	int result=execute( cmd );
+	if( result!=0 ) {
+	    log("Link failed " + result );
+	    log("Command:" + cmd.toString());
+	    log("Output:" );
+	    if( outputstream!=null ) 
+		log( outputstream.toString());
+	    log("StdErr:" );
+	    if( errorstream!=null ) 
+		log( errorstream.toString());
+	    
+	    throw new BuildException("Link failed " + soFile);
+	}
+	closeStreamHandler();
+
+	executeLibtoolInstall();
+	return true;
+    }
+
+    /** Final step using libtool.
+     */
+    private void executeLibtoolInstall() throws BuildException {
+	Commandline cmd = new Commandline();
+
+	String libtool=project.getProperty("build.native.libtool");
+	if(libtool==null) libtool="libtool";
+
+	cmd.setExecutable( libtool );
+	
+	cmd.createArgument().setValue("--mode=install");
+
+	cmd.createArgument().setValue( "cp" );
+
+	File laFile=new File( buildDir, soFile + ".la" );
+	cmd.createArgument().setValue( laFile.getAbsolutePath());
+	
+	File soFileF=new File( buildDir, soFile + ".so" );
+	cmd.createArgument().setValue( soFileF.getAbsolutePath());
+
+	int result=execute( cmd );
+	if( result!=0 ) {
+	    log("Link/install failed " + result );
+	    log("Command:" + cmd.toString());
+	    log("Output:" );
+	    if( outputstream!=null ) 
+		log( outputstream.toString());
+	    log("StdErr:" );
+	    if( errorstream!=null ) 
+		log( errorstream.toString());
+	    
+	    throw new BuildException("Link failed " + soFile);
+	}
+	closeStreamHandler();
+    }    
+}
+
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/compilers/LinkerAdapter.java b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/LinkerAdapter.java
new file mode 100644
index 0000000..41a4a53
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/LinkerAdapter.java
@@ -0,0 +1,64 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.jk.ant.*;
+
+import java.util.*;
+
+/* Modeled after javac
+ */
+
+/**
+ * s/javac/C linker/
+ *
+ * The interface that all compiler adapters must adher to.  
+ *
+ * <p>A compiler adapter is an adapter that interprets the javac's
+ * parameters in preperation to be passed off to the compier this
+ * adapter represents.  As all the necessary values are stored in the
+ * Javac task itself, the only thing all adapters need is the javac
+ * task, the execute command and a parameterless constructor (for
+ * reflection).</p>
+ *
+ * @author Jay Dickon Glanville <a href="mailto:jayglanville@home.com">jayglanville@home.com</a>
+ * @author Costin Manolache
+ */
+public abstract class LinkerAdapter extends SoTask {
+    protected SoTask so;
+
+    /**
+     * Sets the compiler attributes, which are stored in the Javac task.
+     */
+    public void setSoTask(SoTask so ) {
+	this.so=so;
+    }
+
+    public void execute() throws BuildException {
+	findSourceFiles();
+	link(this.srcList);
+    }
+
+
+    /**
+     * Executes the task.
+     *
+     * @return has the compilation been successful
+     */
+    public abstract boolean link(Vector srcFiles) throws BuildException;
+}
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/compilers/MsvcCompiler.java b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/MsvcCompiler.java
new file mode 100644
index 0000000..637d3c7
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/MsvcCompiler.java
@@ -0,0 +1,169 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant.compilers;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+
+import org.apache.jk.ant.Def;
+import org.apache.jk.ant.SoTask;
+import org.apache.jk.ant.Source;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.GlobPatternMapper;
+
+/**
+ *  Compile using Microsoft Visual C++ v6.0
+ * 
+ * @author Costin Manolache
+ * @author Ignacio J. Ortega
+ * @author Mike Anderson
+ * @author Larry Isaacs
+ */
+public class MsvcCompiler extends CompilerAdapter {
+    GlobPatternMapper co_mapperS=new GlobPatternMapper();
+    
+    public MsvcCompiler() {
+        super();
+	co_mapperS.setFrom("*.c");
+	co_mapperS.setTo("*.obj");
+    }
+
+    public String[] getTargetFiles( Source src ) {
+        File srcFile = src.getFile();
+        String name=srcFile.getName();
+        
+        return co_mapperS.mapFileName( name );
+    }
+
+    public void setSoTask(SoTask so ) {
+        this.so=so;
+        so.setExtension(".dll");
+        so.duplicateTo( this );
+        project.setProperty("win32", "true");
+        if (optG)
+            project.setProperty("win32.debug", "true");
+        else
+            project.setProperty("win32.release", "true");
+    }
+
+    /** Compile using msvc 
+     */
+    public void compileSingleFile(Source sourceObj) throws BuildException {
+	File f=sourceObj.getFile();
+	String source=f.toString();
+        String [] includeList = ( includes==null ) ?
+            new String[] {} : includes.getIncludePatterns(project);
+
+        Commandline cmd = new Commandline();
+
+        String cc=project.getProperty("build.compiler.cc");
+        if(cc==null) cc="cl";
+        
+        cmd.setExecutable( cc );
+        addCCArgs( cmd, source, includeList );
+
+        int result=execute( cmd );
+        if( result!=0 ) {
+            log("Compile failed " + result + " " +  source );
+            log("Output:" );
+            if( outputstream!=null ) 
+                log( outputstream.toString());
+            log("StdErr:" );
+            if( errorstream!=null ) 
+                log( errorstream.toString());
+            
+            throw new BuildException("Compile failed " + source);
+        }
+        File ccOpt = new File(buildDir, "cc.opt");
+        ccOpt.delete();
+        closeStreamHandler();
+
+    }
+
+    /** common compiler args
+     */
+    private void addCCArgs(Commandline cmd, String source, String includeList[]) {
+        String extra_cflags=project.getProperty("build.native.extra_cflags");
+        String localCflags=cflags;
+        File ccOpt = new File(buildDir, "cc.opt");
+        if( localCflags==null ) {
+            localCflags=new String("-nologo -W3 -GX -O2 -c");
+            if( extra_cflags!=null ) {
+                localCflags+=" " + extra_cflags;
+            }
+        }
+
+        if (optG)
+            localCflags += " -MTd -Zi";
+        else
+            localCflags += " -MT";
+
+        // create a cc.opt file 
+        PrintWriter ccpw = null;
+        try
+        {
+            ccpw = new PrintWriter(new FileWriter(ccOpt));
+            // write the compilation flags out
+            ccpw.println(localCflags);
+            for( int i=0; i<includeList.length; i++ ) {
+                ccpw.print("-I");
+                if (!includeList[i].startsWith("\"")) {
+                    ccpw.print("\"");
+                }
+                ccpw.print(includeList[i] );
+                if (!includeList[i].endsWith("\"")) {
+                    ccpw.print("\"");
+                }
+                ccpw.println();
+            }
+
+            if( defines.size() > 0 ) {
+                Enumeration defs=defines.elements();
+                while( defs.hasMoreElements() ) {
+                    Def d=(Def)defs.nextElement();
+                    String name=d.getName();
+                    String val=d.getValue();
+                    if( name==null ) continue;
+                    String arg="-D" + name;
+                    if( val!=null )
+                        arg+= "=" + val;
+                    ccpw.println(arg);
+                }
+            }
+        }
+        catch (IOException ioe)
+        {
+            System.out.println("Caught IOException");
+        }
+        finally
+        {
+            if (ccpw != null)
+            {
+                ccpw.close();
+            }
+        }
+
+        project.log( "Compiling " + source);
+        cmd.createArgument().setValue( source );
+        cmd.createArgument().setValue( "@cc.opt" );
+    }
+}
+
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/compilers/MsvcLinker.java b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/MsvcLinker.java
new file mode 100644
index 0000000..fd1fb2d
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/MsvcLinker.java
@@ -0,0 +1,194 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant.compilers;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.jk.ant.JkData;
+import org.apache.jk.ant.SoTask;
+import org.apache.jk.ant.Source;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.GlobPatternMapper;
+
+/**
+ * Link using MSVC Linker
+ *
+ * @author Costin Manolache
+ * @author Ignacio J. Ortega
+ * @author Mike Anderson
+ * @author Larry Isaacs
+ */
+public class MsvcLinker extends LinkerAdapter {
+    SoTask so;
+    GlobPatternMapper co_mapper=new GlobPatternMapper();
+    
+    public MsvcLinker() {
+        so=this;
+        co_mapper.setFrom("*.c");
+        co_mapper.setTo("*.obj");
+    }
+
+    public void setSoTask(SoTask so ) {
+        this.so=so;
+        so.setExtension(".dll");
+        so.duplicateTo( this );
+        project.setProperty("win32", "true");
+        if (optG)
+            project.setProperty("win32.debug", "true");
+        else
+            project.setProperty("win32.release", "true");
+    }
+
+    public void execute() throws BuildException {
+        findSourceFiles();
+        link(this.srcList);
+    }
+
+    public boolean link(Vector srcList) throws BuildException {
+        Commandline cmd = new Commandline();
+        File linkOpt = new File(buildDir, "link.opt");
+        File linkDef = new File(buildDir, "link.def");
+
+        String libtool=project.getProperty("build.compiler.ld");
+        if(libtool==null) libtool="link";
+
+        cmd.setExecutable( libtool );
+
+        // All .obj files must be included
+        project.log( "Linking " + buildDir + "/" + soFile + ".dll");
+
+        // create a .opt file and a .def file
+        PrintWriter linkOptPw = null;
+        PrintWriter linkDefPw = null;
+        try
+        {
+            linkOptPw = new PrintWriter(new FileWriter(linkOpt));
+            linkDefPw = new PrintWriter(new FileWriter(linkDef));
+
+            // write the imports to link with to the .opt file
+            linkOptPw.print("  ");
+            Enumeration imps = imports.elements();
+            while( imps.hasMoreElements() ) {
+                JkData imp = (JkData) imps.nextElement();
+                String name = imp.getValue();
+                if( name==null ) continue;
+                linkOptPw.print(name+" ");
+            }
+
+            // write the link flags out
+
+            linkOptPw.print("/machine:I386 ");
+            linkOptPw.print("/out:" + soFile + ".dll ");
+            linkOptPw.print("/nologo ");
+            linkOptPw.print("/dll ");
+            linkOptPw.print("/incremental:no ");
+
+            // write out any additional link options
+            Enumeration opts = linkOpts.elements();
+            while( opts.hasMoreElements() ) {
+                JkData opt = (JkData) opts.nextElement();
+                String option = opt.getValue();
+                if( option == null ) continue;
+                linkOptPw.println( option );
+            }
+
+            // add debug information in if requested
+            if (optG)
+            {
+                linkOptPw.print("/debug ");
+            }
+            // def file
+            linkOptPw.println("/def:link.def");
+            // write the objects to link with to the .opt file
+            for( int i=0; i<srcList.size(); i++ ) {
+                Source source=(Source)srcList.elementAt(i);
+                File srcF = source.getFile();
+                String name=srcF.getName();
+                String targetNA[]=co_mapper.mapFileName( name );
+                if( targetNA!=null )
+                    linkOptPw.println( targetNA[0] );
+            }
+            // Write the resources to link to .opt file
+            Enumeration ress = resources.elements();
+            while( ress.hasMoreElements() ) {
+                JkData res = (JkData) ress.nextElement();
+                String name = res.getValue();
+                if( name==null ) continue;
+                linkOptPw.println(name);
+            }
+            
+            // Write the library name to the def file
+            linkDefPw.println("LIBRARY\t\""+soFile+"\"");
+
+            // write the exported symbols to the .def file
+            Enumeration exps = exports.elements();
+            if ( exps.hasMoreElements() )
+            {
+                linkDefPw.println("EXPORTS");
+                while( exps.hasMoreElements() ) {
+                    JkData exp = (JkData) exps.nextElement();
+                    String name = exp.getValue();
+                    if( name==null ) continue;
+                    linkDefPw.println("\t" + name);
+                }
+            }
+        }
+        catch (IOException ioe)
+        {
+            System.out.println("Caught IOException");
+        }
+        finally
+        {
+            if (linkOptPw != null)
+            {
+                linkOptPw.close();
+            }
+
+            if (linkDefPw != null)
+            {
+                linkDefPw.close();
+            }
+        }
+
+
+        cmd.createArgument().setValue( "@link.opt" );
+        int result=execute( cmd );
+        if( result!=0 ) {
+            log("Link failed " + result );
+            log("Command:" + cmd.toString());
+            log("Output:" );
+            if( outputstream!=null )
+                log( outputstream.toString());
+            log("StdErr:" );
+            if( errorstream!=null )
+                log( errorstream.toString());
+
+            throw new BuildException("Link failed " + soFile);
+        }
+        //         linkOpt.delete();
+        //         linkDef.delete();
+        closeStreamHandler();
+        return true;
+    }
+}
+
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/compilers/MwccCompiler.java b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/MwccCompiler.java
new file mode 100644
index 0000000..34613f6
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/MwccCompiler.java
@@ -0,0 +1,164 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant.compilers;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+
+import org.apache.jk.ant.Def;
+import org.apache.jk.ant.SoTask;
+import org.apache.jk.ant.Source;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ *  Compile using MetroWerks.
+ * 
+ *  It extends SoTask so we can debug it or use it independently of <so>.
+ *  For normal use you should use the generic task, and system-specific
+ *  properties to choose the right compiler plugin ( like we select
+ *  jikes ).
+ *
+ * @author Mike Anderson
+ */
+public class MwccCompiler extends CcCompiler {
+    
+    public MwccCompiler() {
+        super();
+    };
+
+    public void setSoTask(SoTask so ) {
+        this.so=so;
+        so.setExtension(".nlm");
+        so.duplicateTo( this );
+        project.setProperty("netware", "true");
+    }
+
+    /** Compile  using mwccnlm.
+     */
+    public void compileSingleFile(Source sourceObj) throws BuildException {
+	File f=sourceObj.getFile();
+	String source=f.toString();
+        String [] includeList = ( includes==null ) ?
+            new String[] {} : includes.getIncludePatterns(project);
+
+        Commandline cmd = new Commandline();
+
+        String cc=project.getProperty("build.compiler.cc");
+        if(cc==null) cc="mwccnlm";
+        
+        cmd.setExecutable( cc );
+        addCCArgs( cmd, source, includeList );
+
+        int result=execute( cmd );
+        if( result!=0 ) {
+            log("Compile failed " + result + " " +  source );
+            log("Output:" );
+            if( outputstream!=null ) 
+                log( outputstream.toString());
+            log("StdErr:" );
+            if( errorstream!=null ) 
+                log( errorstream.toString());
+            
+            throw new BuildException("Compile failed " + source);
+        }
+        if (null == project.getProperty("save.optionFiles"))
+        {
+            File ccOpt = new File(buildDir, "cc.opt");
+            ccOpt.delete();
+        }
+        closeStreamHandler();
+
+    }
+
+    /** common compiler args
+     */
+    private void addCCArgs(Commandline cmd, String source, String includeList[]) {
+        String extra_cflags=project.getProperty("build.native.extra_cflags");
+        String localCflags=cflags;
+        File ccOpt = new File(buildDir, "cc.opt");
+        boolean useLibC = false;
+
+        // create a cc.opt file 
+        PrintWriter ccpw = null;
+        try
+        {
+            ccpw = new PrintWriter(new FileWriter(ccOpt));
+
+            for( int i=0; i<includeList.length; i++ ) {
+                ccpw.print("-I");
+                ccpw.println(includeList[i] );
+            }
+
+            if( defines.size() > 0 ) {
+                Enumeration defs=defines.elements();
+                while( defs.hasMoreElements() ) {
+                    Def d=(Def)defs.nextElement();
+                    String name=d.getName();
+                    String val=d.getValue();
+                    if( name==null ) continue;
+                    
+                    String arg="-D" + name;
+                    if( val!=null )
+                        arg+= "=" + val;
+                    ccpw.println(arg);
+
+                    // check to see if we are building using LibC
+                    if (name.equals("__NOVELL_LIBC__"))
+                        useLibC = true;
+                }
+            }
+
+            // finalize the cflags
+            if( localCflags==null ) {
+                localCflags=new String("-nosyspath -c -w nocmdline -bool on");
+                if (useLibC)
+                    localCflags += " -align 4";
+                else
+                    localCflags += " -align 1";
+
+                if( extra_cflags!=null )
+                    localCflags+=" " + extra_cflags;
+            }
+
+            if (optG)
+                localCflags += " -g";
+
+            // write the compilation flags out
+            ccpw.println(localCflags);
+        }
+        catch (IOException ioe)
+        {
+            System.out.println("Caught IOException");
+        }
+        finally
+        {
+            if (ccpw != null)
+            {
+                ccpw.close();
+            }
+        }
+
+        project.log( "Compiling " + source);
+        cmd.createArgument().setValue( source );
+        cmd.createArgument().setValue( "@cc.opt" );
+    }
+}
+
diff --git a/connectors/jk/jkant/java/org/apache/jk/ant/compilers/MwldLinker.java b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/MwldLinker.java
new file mode 100644
index 0000000..f80986d
--- /dev/null
+++ b/connectors/jk/jkant/java/org/apache/jk/ant/compilers/MwldLinker.java
@@ -0,0 +1,222 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.jk.ant.compilers;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.jk.ant.JkData;
+import org.apache.jk.ant.SoTask;
+import org.apache.jk.ant.Source;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.GlobPatternMapper;
+
+/**
+ * Link using libtool.
+ * 
+ * @author Costin Manolache
+ */
+public class MwldLinker extends LinkerAdapter {
+    GlobPatternMapper lo_mapper=new GlobPatternMapper();
+    
+    public MwldLinker() {
+        super();
+        lo_mapper.setFrom("*.c");
+	lo_mapper.setTo("*.o");
+    }
+
+    public void setSoTask(SoTask so ) {
+        this.so=so;
+        so.setExtension(".nlm");
+        so.duplicateTo( this );
+        project.setProperty("netware", "true");
+
+        Enumeration e=altSoFiles.elements();
+        while (e.hasMoreElements())
+        {
+            JkData data = (JkData) e.nextElement();
+            String altSo = data.getValue();
+            if (altSo == null) 
+                continue;
+            else
+            {
+                so.setTarget(altSo);    // set it on the master copy
+                setTarget(altSo);       // set it on ourself
+                break;
+            }
+        }
+    }
+
+    public void execute() throws BuildException {
+        findSourceFiles();
+        link(this.srcList);
+    }
+
+    /** Link using libtool.
+     */
+    public boolean link(Vector srcList) throws BuildException {
+        Commandline cmd = new Commandline();
+        File linkOpt = new File(buildDir, "link.opt");
+        File linkDef = new File(buildDir, "link.def");
+        boolean useLibC = false;
+
+        String libtool=project.getProperty("build.compiler.ld");
+        if(libtool==null) libtool="mwldnlm";
+
+        cmd.setExecutable( libtool );
+        
+        // All .obj files must be included
+        project.log( "Linking " + buildDir + "/" + soFile + ".nlm");
+
+        // create a .opt file and a .def file
+        PrintWriter linkOptPw = null;
+        PrintWriter linkDefPw = null;
+        try
+        {
+            String libBase = project.getProperty("build.compiler.base");
+            if (libBase == null) libBase = "\\tools\\mw\\5.3";
+            linkOptPw = new PrintWriter(new FileWriter(linkOpt));
+            linkDefPw = new PrintWriter(new FileWriter(linkDef));
+
+            // write the link flags out
+            linkOptPw.println("-warnings off");
+            linkOptPw.println("-zerobss");
+            linkOptPw.println("-o " + soFile + ".nlm");
+            linkOptPw.println("-map " + soFile + ".map");
+            linkOptPw.println("-nodefaults");
+
+            // add debug information in if requested
+            if (optG)
+            {
+                linkOptPw.println("-g");
+                linkOptPw.println("-sym internal");
+                linkOptPw.println("-sym codeview4");
+                linkOptPw.println("-osym " + soFile + ".NCV");
+            }
+
+            // write out any additional link options
+            Enumeration opts = linkOpts.elements();
+            while( opts.hasMoreElements() ) {
+                JkData opt = (JkData) opts.nextElement();
+                String option = opt.getValue();
+                if( option == null ) continue;
+
+                linkOptPw.println( option );
+                option = option.toLowerCase();
+
+                // check to see if we are building using LibC
+                if (option.indexOf("libc") > 0)
+                    useLibC = true;
+            }
+
+            // add the default startup code to the list of objects
+            if (useLibC)
+                linkOptPw.println("-llibcpre.o");
+            else
+                linkOptPw.println(libBase + "\\lib\\nwpre.obj");
+
+            // write the objects to link with to the .opt file
+            for( int i=0; i<srcList.size(); i++ ) {
+                Source source=(Source)srcList.elementAt(i);
+                File srcF = source.getFile();
+                String name=srcF.getName();
+                String targetNA[]=lo_mapper.mapFileName( name );
+                if( targetNA!=null )
+                    linkOptPw.println( targetNA[0] );
+            }
+            linkOptPw.println("-commandfile link.def");
+
+            // write the dependant modules to the .def file
+            Enumeration mods = modules.elements();
+            while( mods.hasMoreElements() ) {
+                JkData mod = (JkData) mods.nextElement();
+                String name = mod.getValue();
+                if( name==null ) continue;
+                linkDefPw.println("module " + name);
+            }
+
+            // write the imports to link with to the .def file
+            Enumeration imps = imports.elements();
+            while( imps.hasMoreElements() ) {
+                JkData imp = (JkData) imps.nextElement();
+                String name = imp.getValue();
+                if( name==null ) continue;
+                if (imp.isFile())
+                    linkDefPw.println("Import @" + name);
+                else
+                    linkDefPw.println("Import " + name);
+            }
+
+            // write the exports to link with to the .def file
+            Enumeration exps = exports.elements();
+            while( exps.hasMoreElements() ) {
+                JkData exp = (JkData) exps.nextElement();
+                String name = exp.getValue();
+                if( name==null ) continue;
+                if (exp.isFile())
+                    linkDefPw.println("Export @" + name);
+                else
+                    linkDefPw.println("Export " + name);
+            }
+        }
+        catch (IOException ioe)
+        {
+            System.out.println("Caught IOException");
+        }
+        finally
+        {
+            if (linkOptPw != null)
+            {
+                linkOptPw.close();
+            }
+
+            if (linkDefPw != null)
+            {
+                linkDefPw.close();
+            }
+        }
+
+
+        cmd.createArgument().setValue( "@link.opt" );
+        int result=execute( cmd );
+        if( result!=0 ) {
+            log("Link failed " + result );
+            log("Command:" + cmd.toString());
+            log("Output:" );
+            if( outputstream!=null ) 
+                log( outputstream.toString());
+            log("StdErr:" );
+            if( errorstream!=null ) 
+                log( errorstream.toString());
+            
+            throw new BuildException("Link failed " + soFile);
+        }
+        if (null == project.getProperty("save.optionFiles"))
+        {
+            linkOpt.delete();
+            linkDef.delete();
+        }
+        closeStreamHandler();
+        return true;
+    }
+}
+
diff --git a/connectors/jk/native/.cvsignore b/connectors/jk/native/.cvsignore
new file mode 100644
index 0000000..c3d6218
--- /dev/null
+++ b/connectors/jk/native/.cvsignore
@@ -0,0 +1,9 @@
+Makefile.in
+aclocal.m4
+configure
+config.log
+Makefile
+config.cache
+libtool
+config.status
+build.properties
diff --git a/connectors/jk/native/BUILDING b/connectors/jk/native/BUILDING
new file mode 100644
index 0000000..f0f5b18
--- /dev/null
+++ b/connectors/jk/native/BUILDING
@@ -0,0 +1,143 @@
+ 
+  Building from the cvs tree: (for developers only).
+  -----------------------------------
+  When using a cvs tree buildconf.sh must be run before configure.
+  The today version of buildconf.sh build the following files:
+  libtool files in scripts/build/unix (should copy them?).
+  Makefile.in from Makefile.am
+  aclocal.m4 from different m4 files located in the local machine.
+  configure from configure.in and aclocal.m4.
+  If you see error message from automake, don't care about them.
+
+
+  Building using configure
+  ------------------------
+ 
+  It is possible to use autoconf for configuration and installation.
+  To create jakarta-tomcat-connectors's autoconf script, you will need libtool
+  1.3.3 or higher, and autoconf 2.13 or newer.
+  Those tools will not be required if you are just
+  using a package downloaded from apache.org, they are only required for
+  developers.
+ 
+  To configure jakarta-tomcat-connectors run the following commands.
+ 
+  ./buildconf.sh  (not required unless you are a developer)
+  ./configure [autoconf arguments] [jakarta-tomcat-connectors arguments]
+  make
+
+  It is possible to set CFLAGS and LDFLAGS to add some platform specifics:
+  LDFLAGS=-lc \
+  ./configure -with-apxs=/home2/local/apache/bin/apxs
+
+  
+  Build for both Apache 1.3 and 2.0
+  ---------------------------------
+
+  If you want to build mod_jk for Apache 1.3 and 2.0, you should :
+
+  use configure and indicate Apache 1.3 apxs location (--with-apxs)
+  use make
+  copy the mod_jk binary to the apache modules location
+
+  make clean (to remove all previously compiled modules)
+  use configure and indicate Apache 2.0 apxs location,
+  then make.
+
+  ./configure --with-apxs=/usr/sbin/apxs
+  make
+  cp ./apache-1.3/mod_jk.so /usr/lib/apache
+  make clean
+  ./configure --with-apxs=/usr/sbin/apxs2
+  make 
+  cp ./apache-2.0/mod_jk.so /usr/lib/apache2
+   
+
+  Examples
+  --------
+
+  Apache2.0, JNI support:
+
+  ./configure --with-apxs=/opt/apache2/bin/apxs --with-java-home=${JAVA_HOME} --with-java-platform=2 -enable-jni
+
+  Apache 1.3, no JNI support:
+
+  ./configure --with-apxs=/usr/sbin/apxs 
+
+  jakarta-tomcat-connectors arguments
+  -----------------------------------
+  JVM related parameters:
+  --with-java-home=DIR
+  DIR is the  patch to the JDK root directory. Something like: /opt/java/jdk12
+  --with-os-type[=SUBDIR] 
+  SUBDIR is the os-type subdirectory, normaly configure should guess it
+  correctly.
+  --with-arch-type[=SUBDIR]
+  SUBDIR is the arch subdirectory, normaly configure should guess it correctly. 
+  --with-java-platform=VAL
+  VAL is the Java platform 1 is 1.1.x and 2 is 1.2.x. It is guessed correctly.
+  
+  Apache related parameters:
+  --with-apxs[=FILE]
+  FILE is the location of the apxs tool. Default is finding apxs in PATH.
+  It builds a shared Apache module. It detects automaticly the Apache version.
+  (2.0 and 1.3)
+* --with-apache=DIR
+  DIR is the path where apache sources are located.
+  The apache sources should have been configured before configuring mod_jk.
+  DIR is something like: /home/apache/apache_1.3.19
+  It builds a static Apache module.
+  --enable-EAPI
+  This parameter is needed when using Apache-1.3 and mod_ssl, otherwise you
+  will get the error message: "this module might crash under EAPI!" when
+  loading libjk.so in httpd.
+
+  JNI support:
+  --enable-jni
+  Build the jni_connect.so and the JNI worker.
+
+* Static build need more tests, and we strongly recommand dynamic build
+  using DSO/APXS.
+
+
+  Installation
+  ------------
+
+  The mod_jk binary will be in :
+
+  ./apache-1.3/mod_jk.so if you select to build mod_jk for apache 1.3
+
+  ./apache-2.0/mod_jk.so if you select to build mod_jk for apache 2.0
+
+
+
+  Misc notes 
+  ----------
+
+  HP-UX build notes :
+
+  If you plan to use GCC on HP-UX to build mod_jk, be sure to 
+  have -DHPUX11GCC defined (usually by setting CLAGS before configure)
+
+  Reports are that the stripped down cc version that ships with
+  HP-UX won't be able to works, you should habe the HP add-on ANSI C 
+  Compiler package.
+
+  A good repository for HP-UX gnu tools is :
+
+  http://gatekeep.cs.utah.edu/
+
+
+  Solaris build notes :
+
+  the build should works with the GNU gcc compiler, on both
+  SPARC and x86 architecture systems.
+
+  A good repository for Solaris gnu tools is :
+
+  http://www.sunfreeware.com/
+
+  Be carefull when mixing native compiler and gnu compiler, and
+  ensure that apache and mod_jk will be compiled and linked with
+  the same tools (ie full Solaris or full GNU)
+
diff --git a/connectors/jk/native/CHANGES.txt b/connectors/jk/native/CHANGES.txt
new file mode 100644
index 0000000..16bde85
--- /dev/null
+++ b/connectors/jk/native/CHANGES.txt
@@ -0,0 +1,102 @@
+JAKARTA TOMCAT CONNECTORS (JK) CHANGELOG:			-*-text-*-
+Last modified at [$Date$]
+
+Changes with JK 1.2.6
+    * Fix POST Recovery problems in LB mode [hgomez]
+    * Add CPING/CPONG support to avoid problems with hang tomcats [hgomez]
+    * Make POST recovery in LB configurable [hgomez]
+    * Update to Apache License 2.0 [hgomez]
+    * For Apache 2.0, when the env var no-jk is present, mod_jk didn't handle request (declined)
+      and as such dont forward requests to tomcats even if URL match.
+      To be used with SetEnvIf or BrowserMatch directives for example to exclude some URL/URI or
+      Browser [hgomez].
+    * Add a fix for iSeries (AS/400) which use XOPEN/Unix98 APIs and need sa_len to be set when
+      calling connect(), it will resolve the error EINVAL in jk_connect [hgomez].
+      
+Changes with JK 1.2.5
+    * Fix a thread safe bug when mapping URI's.
+      [billbarker]
+    * Fix a thread safe bug when resolving worker host name
+      when using mod_jk with Apache 2 and the worker MPM.
+      [hgomez]
+    * Remove an unnecessary error message when connections to
+      all load balanced workers fail.
+      [glenn]
+    * When mod_jk cannot connect to a worker include the name of
+      the worker in the error message.  This is especially helpful
+      when you are using load balanced workers.
+      [glenn]
+    * Fix problem with mod_jk.log getting opened multiple times for
+      Apache 2. Only one mod_jk.log can be configured.
+      [glenn]
+    * Fix Apache 2 connector so that DirectoryIndex works for an
+      index.jsp page if JkOptions ForwardDirectories was configured.
+      [hgomez]
+    * Fix exposure of JSP source if a //path/to.jsp URL was requested
+      in Apache 1.3 and Apache 2.0 connector.
+      [billbarker]
+
+Changes with JK 1.2.4
+    * Fix use of libtool for Apache mod_jk builds with more recent
+      versions of Apache 2.
+      [jfclere]
+    * Use reentrant version of strtok() for web server's which use
+      threads. This fixes a thread safe bug under Apache 2 and the
+      worker MPM.
+      [glenn]
+    * Fix the Apache 2 mod_jk hook priority so that mod_jk works
+      well with both mod_alias and mod_dir.
+      [glenn]
+
+Changes with JK 1.2.3:
+    * Add the ability to configure JkLog to pipe its log output to an
+      executable such as Apache rotatelogs or cronolog.  Apache 2.0 only.
+      [glenn]
+    * Add JkAutoAlias to Apache 2.0
+      [glenn]
+    * Apache 2/1.3, if Tomcat returns an error but not content,
+      let Apache handle processing the error returned by Tomcat.
+    * Added the load balancer sticky_session property. If set to 0
+      requests with servlet SESSION ID's can be routed to any Tomcat
+      worker. Default is 1, sessions are sticky.
+      [glenn]
+    * Cleaned up detection and reporting of aborted client connections.
+      This cleanup also makes sure that mod_jk does not pass any requests
+      on to Tomcat if the remote client aborted its connection.
+      [glenn]
+    * Fixed a bug in Apache 2.0 which caused a POST request forwarded to
+      Tomcat to fail if it generated SSI directives which were post
+      processed by mod_include.
+      [glenn]
+    * Fixed a bug in JkRequestLogFormat when printing the request URI that
+      could cause a URI with hex escapes sequences to be formatted wrong.
+      [glenn]
+      
+Changes with JK 1.2.2:
+    * tomcat_trend.pl updated script to support changed logging of 
+      aborted requests
+      [glenn]
+    * jk set correctly the content-type in Apache 2.0,
+      making it ready to works with mod_deflate and AddOutputFilterByType 
+      [hgomez]
+    * jk will check result of get_endpoint and handle a failure.
+      This call can fail if the allocation for the endpoint fails because of low memory conditions 
+      causing a dereference of NULL when we try and access the endpoint
+      [mmanders]
+      
+Changes with JK 1.2.1:
+    * Don't send initial chunk for chunked encoding, fix #14282
+      [costin]
+    * Add perl scripts for analyzing mod_jk logs and generating graphs/reports  
+      [glenn]
+    * Make JK honor the CanonicalHost directive.
+      [hgomez]
+    * Log cleanup
+      [costin]
+    * Fix typos in jk xdocs/docs
+      [hgomez]
+    * Add JkRequestLogFormat to Apache 2.0
+      [hgomez]
+    * Final patches to make JK iSeries compliant
+      [hgomez]
+      
diff --git a/connectors/jk/native/Makefile.am b/connectors/jk/native/Makefile.am
new file mode 100644
index 0000000..0c600b5
--- /dev/null
+++ b/connectors/jk/native/Makefile.am
@@ -0,0 +1,21 @@
+#
+# Tell automake what it should do
+AUTOMAKE_OPTIONS = foreign
+MAINTAINERCLEANFILES=config.cache config.status config.log \
+Makefile.in configure
+
+SUBDIRS = @WEBSERVER@
+
+all:
+	target="all"; \
+	list='$(SUBDIRS)'; \
+	for i in $$list; do \
+	    echo "Making $$target in $$i"; \
+	    if test "$$i" != "."; then \
+	       (cd $$i && $(MAKE) $$target) || exit 1; \
+	    fi; \
+	done;
+
+apidocs: common/*.h
+	../../scandoc/scandoc.pl -i ../../scandoc/template.pl -p \
+	./docs/api/ -dproject="mod_jk Library" common/*.h common/*.c
diff --git a/connectors/jk/native/README b/connectors/jk/native/README
new file mode 100644
index 0000000..5c8555f
--- /dev/null
+++ b/connectors/jk/native/README
@@ -0,0 +1,30 @@
+README for jakarta-tomcat-connector
+
+$Id$
+
+Please see doc/mod_jk-howto.html for more verbose instructions
+
+* What is jakarta-tomcat-connector ?
+
+jakarta-tomcat-connector is a new project to release web-servers connector
+for the jakarta Tomcat servlet engine.
+
+This project didn't start from null since it reuse the latest code from
+mod_jk for the native parts and tomcat 3.3 for the java side of the 
+force.
+
+From mod_jk we gain :
+
+* A connector (plugin) for many Web Server, including
+  Apache HTTP Server, Netscape/IPLanet NES and Microsoft IIS
+  It also support JNI (and seems to used at least by IBM under 
+  AS/400 for Apache/WebSphere connectivity).
+  
+* Fault-tolerance and load-balancing. mod_jk use the concept of 
+  workers which handle a particular request, and a special worker
+  , the lb worker, is a group (cluster ?) of physical workers.
+
+* Direct access to Tomcat 3.2/3.3/4.0/4.1 servlet engine via ajp13
+  protocol. 
+
+* OK, then how do I build web-connector ? Just take a look at BUILDING
diff --git a/connectors/jk/native/STATUS.txt b/connectors/jk/native/STATUS.txt
new file mode 100644
index 0000000..767d36e
--- /dev/null
+++ b/connectors/jk/native/STATUS.txt
@@ -0,0 +1,30 @@
+JAKARTA TOMCAT CONNECTORS (JK) STATUS:			-*-text-*-
+Last modified at [$Date$]
+
+Release:
+
+    1.2.7   : in progress
+    1.2.6   : released July 23, 2004
+    1.2.5   : released September 30, 2003 
+    1.2.4   : released May 27, 2003
+    1.2.3   : released May 16, 2003
+    1.2.2   : released Dezember 17, 2002
+    
+
+
+RELEASE SHOWSTOPPERS:
+
+    
+    
+ 
+RELEASE NON-SHOWSTOPPERS BUT WOULD BE REAL NICE TO WRAP THESE UP:
+    
+
+
+
+STUFF FOR THE FUTURE:
+
+    * enter mature phase ... ?
+
+
+
diff --git a/connectors/jk/native/apache-1.3/.cvsignore b/connectors/jk/native/apache-1.3/.cvsignore
new file mode 100644
index 0000000..ba30ecc
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/.cvsignore
@@ -0,0 +1,6 @@
+.libs
+Makefile
+Makefile.apxs
+*.o
+*.lo
+*.la
diff --git a/connectors/jk/native/apache-1.3/Makefile.apxs.in b/connectors/jk/native/apache-1.3/Makefile.apxs.in
new file mode 100755
index 0000000..93a7a2c
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/Makefile.apxs.in
@@ -0,0 +1,24 @@
+## configure should make the Makefile out of this file.
+
+APXS=@APXS@
+OS=@OS@
+JAVA_HOME=@JAVA_HOME@
+APXSLDFLAGS=@APXSLDFLAGS@
+APXSCFLAGS=@APXSCFLAGS@
+
+JK=../common/
+JK_INCL=-DUSE_APACHE_MD5 -I ${JK}
+JAVA_INCL=-I ${JAVA_HOME}/include -I ${JAVA_HOME}/include/${OS}
+JAVA_LIB=-L ${JAVA_HOME}/jre/lib/${ARCH} -L ${JAVA_HOME}/lib/${ARCH}/native_threads
+
+## read the object (.c) from the list file.
+OEXT=.c
+include ../common/list.mk
+
+all: mod_jk.so
+
+mod_jk.so: 
+	$(APXS) -c -o $@ -Wc,"${APXSCFLAGS} ${JK_INCL}" ${JAVA_INCL} "${APXSLDFLAGS}" mod_jk.c ${APACHE_OBJECTS} 
+
+clean:
+	rm *.o *.so
diff --git a/connectors/jk/native/apache-1.3/Makefile.in b/connectors/jk/native/apache-1.3/Makefile.in
new file mode 100755
index 0000000..4177a8e
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/Makefile.in
@@ -0,0 +1,96 @@
+
+## configure should make the Makefile out of this file.
+
+srcdir=@srcdir@
+top_srcdir=@top_srcdir@
+top_builddir=@top_builddir@
+VPATH=@srcdir@
+APXS=@APXS@
+OS=@OS@
+JAVA_HOME=@JAVA_HOME@
+CP=@CP@
+APACHE_DIR=@APACHE_DIR@
+MKDIR=@MKDIR@
+DESTDIR=@APACHE_DIR@
+LIBTOOL=@LIBTOOL@
+CP=@CP@
+CC=@CC@
+
+OEXT=.lo
+libexecdir=${APACHE_DIR}/libexec
+JK_DIR := ..
+BUILD_DIR = ${JK_DIR}/../build/jk/apache13
+
+APACHE_FILES = Makefile.tmpl Makefile.libdir libjk.module
+
+JK=../common/
+JK_INCL=-DUSE_APACHE_MD5 -I ${top_srcdir}/common
+JAVA_INCL=-I ${JAVA_HOME}/include -I ${JAVA_HOME}/include/${OS}
+JAVA_LIB=-L ${JAVA_HOME}/jre/lib/${ARCH} -L ${JAVA_HOME}/lib/${ARCH}/native_threads
+APACHE_CFLAGS=@apache_include@ @APXSCFLAGS@ @APXSCPPFLAGS@ -I${top_srcdir}/common
+
+# Compile commands
+JK_CFLAGS  = $(JK_INCL) $(JAVA_INCL) $(APACHE_CFLAGS)
+COMPILE    = $(CC)
+SH_COMPILE = $(LIBTOOL) --mode=compile $(COMPILE) $(JK_CFLAGS)
+MOD_LINK   = $(LIBTOOL) --mode=link $(CC)
+
+include ../common/list.mk
+
+all: @LIB_JK_TYPE@
+
+#
+# install part
+#
+install: @INSTALL_TYPE@
+
+install_static: mod_jk.a
+	@echo ""
+	@echo "Copying files to Apache Modules Directory..."
+	-${MKDIR} ${APACHE_DIR}/src/modules/jk
+	${LIBTOOL} --mode=install ${CP} $< ${APACHE_DIR}/src/modules/jk/mod_jk.a
+	-${CP} Makefile.tmpl ${APACHE_DIR}/src/modules/jk
+	-${CP} Makefile.libdir ${APACHE_DIR}/src/modules/jk
+	-${CP} mod_jk.c ${APACHE_DIR}/src/modules/jk
+	-${MKDIR} ${APACHE_DIR}/src/modules/jk/include
+	-${CP} ../common/*.h ${APACHE_DIR}/src/modules/jk/include
+	@echo ""
+	@echo "Please be sure to re-compile Apache..."
+	@echo ""
+	@echo "cd ${APACHE_DIR}"
+	@echo "./config.status --activate-module=src/modules/jk/libjk.a \\"
+	@echo "                --enable-module=dir \\"
+	@echo "                --disable-shared=dir"
+	@echo "make"
+	@echo ""
+install_dynamic: mod_jk.la
+	@echo ""
+	@echo "Copying files to Apache libexec Directory..."
+	${LIBTOOL} --mode=install ${CP} $< ${libexecdir}/mod_jk.so
+
+#
+# Clean part.
+#
+clean:
+	rm -rf *.lo *.la
+	rm -rf *.o .libs *.so
+
+#
+# Compile part.
+#
+mod_jk.la: mod_jk.lo $(APACHE_OBJECTS)
+	 ${MOD_LINK} -o $@ -module -rpath ${libexecdir} $^
+mod_jk.a: mod_jk.lo $(APACHE_OBJECTS)
+	 ${MOD_LINK} -o $@ $^
+
+mod_jk.so: mod_jk.la
+	$(LIBTOOL) --mode=install cp $< `pwd`/$@
+
+#
+# Common part.
+#
+mod_jk.lo: mod_jk.c
+	${SH_COMPILE} -c $< -o $@
+
+.c.lo:
+	${SH_COMPILE} -c $< -o $@
diff --git a/connectors/jk/native/apache-1.3/Makefile.libdir b/connectors/jk/native/apache-1.3/Makefile.libdir
new file mode 100644
index 0000000..7b52540
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/Makefile.libdir
@@ -0,0 +1,4 @@
+This is a place-holder which indicates to Configure that it shouldn't
+provide the default targets when building the Makefile in this directory.
+Instead it'll just prepend all the important variable definitions, and
+copy the Makefile.tmpl onto the end.
diff --git a/connectors/jk/native/apache-1.3/Makefile.netware b/connectors/jk/native/apache-1.3/Makefile.netware
new file mode 100644
index 0000000..df667d9
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/Makefile.netware
@@ -0,0 +1,263 @@
+#
+# Makefile for mod_jk (NetWare version - gnu make)
+# created by Guenter Knauf <eflash@gmx.net>
+#
+
+# Edit the path below to point to the base of your Apache 1.3 includes.
+ifndef AP_HOME
+AP_HOME	= c:/projects/cw/apache_1.3.31
+endif
+# Edit the path below to point to the base of your NetWare Java SDK.
+ifndef NW_JDK
+NW_JDK	= c:/projects/sdks/java-nw
+endif
+# Edit the path below to point to the base of your Novell NDK.
+ifndef NDKBASE
+NDKBASE	= c:/novell
+endif
+INSTDIR = s:/apache/modules
+
+# Edit the vars below to change NLM target settings.
+TARGET  = mod_jk
+VERSION	= $(JK_VERSION)
+COPYR	= Copyright (c) 2000-2004 The Apache Software Foundation. All rights reserved.
+DESCR	= Apache $(AP_VERSION_STR) plugin for Jakarta/Tomcat $(JK_VERSION_STR)
+MTSAFE	= YES
+STACK	= 49152
+#SCREEN	= NONE
+EXPORTS	= jk_module
+#AP_PRE	= YES
+
+# Edit the var below to point to your lib architecture.
+ifndef LIBARCH
+LIBARCH = CLIB
+# LIBARCH = LIBC
+endif
+
+# must be equal to DEBUG or NDEBUG
+DB	= NDEBUG
+# DB	= DEBUG
+# Optimization: -O<n> or debugging: -g
+ifeq ($(DB),NDEBUG)
+	OPT	= -O2
+	OBJDIR	= release
+else
+	OPT	= -g
+	OBJDIR	= debug
+endif
+
+# Include the version info retrieved from jk_version.h
+-include $(OBJDIR)/version.inc
+
+# The following line defines your compiler.
+ifdef METROWERKS
+	CC = mwccnlm
+else
+	CC = gcc
+endif
+# RM	= rm -f
+#CP	= cp -fv
+
+# Global flags for all compilers
+CFLAGS	= $(OPT) -D$(DB) -DNETWARE -nostdinc
+
+ifeq ($(CC),mwccnlm)
+LD	= mwldnlm
+LDFLAGS	= -nostdlib $(PRELUDE) $(OBJDIR)/*.o -o $(OBJDIR)/$(TARGET).nlm -commandfile
+CFLAGS	+= -gccinc -inline off -opt nointrinsics
+#CFLAGS	+= -w on
+ifeq ($(LIBARCH),LIBC)
+	PRELUDE = $(SDK_LIBC)/imports/libcpre.o
+	CFLAGS += -align 4 -inst mmx -proc 586
+#	CFLAGS += -D__ANSIC__
+else
+#	PRELUDE = $(SDK_CLIB)/imports/clibpre.obj
+	PRELUDE = "$(METROWERKS)/Novell Support/libraries/runtime/prelude.obj"
+#	CFLAGS += -include "$(METROWERKS)/Novell Support/headers/nlm_prefix.h"
+	CFLAGS += -align 1 -proc 586
+endif
+else
+LD	= nlmconv
+LDFLAGS	= -T
+CFLAGS	+= -fno-builtin -fpack-struct -fpcc-struct-return
+CFLAGS	+= -w
+#CFLAGS	+= -Wall -Wno-main # -pedantic
+ifeq ($(LIBARCH),LIBC)
+	PRELUDE = $(SDK_LIBC)/imports/libcpre.gcc.o
+#	CFLAGS += -D__ANSIC__
+else
+#	PRELUDE = $(SDK_CLIB)/imports/clibpre.gcc.o
+	CFLAGS += -include $(NDKBASE)/nlmconv/genlm.h
+endif
+endif
+CFLAGS	+= -include "precomp.h"
+
+ifeq ($(AP_PRE),YES)
+	PRELUDE	= $(OBJDIR)/libpre.o
+endif
+
+
+NDK_ROOT = $(NDKBASE)/ndk
+SDK_CLIB = $(NDK_ROOT)/nwsdk
+SDK_LIBC = $(NDK_ROOT)/libc
+JKCOMMON = ../common
+
+INCLUDES = -I$(AP_HOME)/src/include -I$(AP_HOME)/src/os/netware
+INCLUDES += -I$(JKCOMMON) -I$(NW_JDK)/include -I$(NW_JDK)/include/netware 
+
+ifeq ($(LIBARCH),LIBC)
+	INCLUDES += -I$(SDK_LIBC)/include -I$(SDK_LIBC)/include/nks
+	INCLUDES += -I$(SDK_LIBC)/include/winsock
+else
+	INCLUDES += -I$(SDK_CLIB)/include/nlm -I$(SDK_CLIB)/include
+	INCLUDES += -I$(NDKBASE)/ws295sdk/include
+	CFLAGS += -DNETDB_USE_INTERNET
+endif
+CFLAGS	+= $(INCLUDES)
+
+ifeq ($(MTSAFE),YES)
+	XDCDATA = $(AP_HOME)/src/os/netware/apache.xdc
+endif
+
+ifeq ($(OSTYPE),linux)
+DL	= '
+-include $(NDKBASE)/nlmconv/ncpfs.inc
+endif
+
+OBJS	= \
+	$(OBJDIR)/jk_nwmain.o \
+	$(OBJDIR)/jk_ajp12_worker.o \
+	$(OBJDIR)/jk_ajp13.o \
+	$(OBJDIR)/jk_ajp13_worker.o \
+	$(OBJDIR)/jk_ajp14.o \
+	$(OBJDIR)/jk_ajp14_worker.o \
+	$(OBJDIR)/jk_ajp_common.o \
+	$(OBJDIR)/jk_connect.o \
+	$(OBJDIR)/jk_context.o \
+	$(OBJDIR)/jk_jni_worker.o \
+	$(OBJDIR)/jk_lb_worker.o \
+	$(OBJDIR)/jk_map.o \
+	$(OBJDIR)/jk_md5.o \
+	$(OBJDIR)/jk_msg_buff.o \
+	$(OBJDIR)/jk_pool.o \
+	$(OBJDIR)/jk_sockbuf.o \
+	$(OBJDIR)/jk_uri_worker_map.o \
+	$(OBJDIR)/jk_util.o \
+	$(OBJDIR)/jk_worker.o \
+	$(OBJDIR)/$(TARGET).o
+
+
+all: $(OBJDIR) $(OBJDIR)/version.inc $(OBJDIR)/$(TARGET).nlm 
+
+$(OBJDIR)/%.o: %.c
+	@echo Compiling $<
+	@$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJDIR)/%.o: $(JKCOMMON)/%.c
+	@echo Compiling $<
+	@$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJDIR)/%.o: $(AP_HOME)/src/os/netware/%.c
+	@echo Compiling $<
+	@$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJDIR)/version.inc: $(JKCOMMON)/jk_version.h $(AP_HOME)/src/include/httpd.h $(OBJDIR)
+	@echo Creating $@
+	@awk -f ../../../common/build/get_ver.awk $< $(AP_HOME)/src/include/httpd.h > $@
+
+dist: all
+	-$(RM) $(OBJDIR)/*.o $(OBJDIR)/$(TARGET).map $(OBJDIR)/$(TARGET).ncv
+	-$(RM) $(OBJDIR)/$(TARGET).def $(OBJDIR)/version.inc
+#	-$(CP) ../changes.txt $(OBJDIR)/
+
+install: all
+	@[ -d $(INSTDIR) ] || mkdir $(INSTDIR)
+	@$(CP) $(TARGET).nlm $(INSTDIR)
+
+clean:
+	-$(RM) -r $(OBJDIR)
+
+$(OBJDIR):
+	@mkdir $(OBJDIR)
+
+$(OBJDIR)/$(TARGET).nlm: $(OBJS) $(OBJDIR)/$(TARGET).def $(XDCDATA)
+	@echo Linking $@
+	@-$(RM) $@
+	@$(LD) $(LDFLAGS) $(OBJDIR)/$(TARGET).def
+
+$(OBJDIR)/%.xdc: Makefile.netware
+	@echo Creating $@
+	@$(MPKXDC) $(XDCOPT) $@
+
+$(OBJDIR)/%.def: Makefile.netware
+	@echo $(DL)# DEF file for linking with $(LD)$(DL) > $@
+	@echo $(DL)# Do not edit this file - it is created by make!$(DL) >> $@
+	@echo $(DL)# All your changes will be lost!!$(DL) >> $@
+	@echo $(DL)#$(DL) >> $@
+	@echo $(DL)copyright "$(COPYR)"$(DL) >> $@
+	@echo $(DL)description "$(DESCR)"$(DL) >> $@
+	@echo $(DL)version $(VERSION)$(DL) >> $@
+ifdef NLMTYPE
+	@echo $(DL)type $(NLMTYPE)$(DL) >> $@
+endif
+ifdef STACK
+	@echo $(DL)stack $(STACK)$(DL) >> $@
+endif
+ifdef SCREEN
+	@echo $(DL)screenname "$(SCREEN)"$(DL) >> $@
+else
+	@echo $(DL)screenname "DEFAULT"$(DL) >> $@
+endif
+ifeq ($(DB),DEBUG)
+	@echo $(DL)debug$(DL) >> $@
+endif
+	@echo $(DL)threadname "$(TARGET)"$(DL) >> $@
+ifdef XDCDATA
+	@echo $(DL)xdcdata $(XDCDATA)$(DL) >> $@
+endif
+ifeq ($(LIBARCH),CLIB)
+ifeq ($(AP_PRE),YES)
+	@echo $(DL)start _lib_start$(DL) >> $@
+	@echo $(DL)exit _lib_stop$(DL) >> $@
+else
+	@echo $(DL)start _Prelude$(DL) >> $@
+	@echo $(DL)exit _Stop$(DL) >> $@
+endif
+	@echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/clib.imp$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/threads.imp$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/nlmlib.imp$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/socklib.imp$(DL) >> $@
+#	@echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/ws2nlm.imp$(DL) >> $@
+	@echo $(DL)import @$(AP_HOME)/src/os/netware/apachecore.imp$(DL) >> $@
+	@echo $(DL)module clib$(DL) >> $@
+else
+ifeq ($(LD),nlmconv)
+	@echo $(DL)flag_on 64$(DL) >> $@
+else
+	@echo $(DL)autounload$(DL) >> $@
+endif
+	@echo $(DL)pseudopreemption$(DL) >> $@
+	@echo $(DL)start _LibCPrelude$(DL) >> $@
+	@echo $(DL)exit _LibCPostlude$(DL) >> $@
+	@echo $(DL)check _LibCCheckUnload$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/libc/imports/libc.imp$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/libc/imports/netware.imp$(DL) >> $@
+#	@echo $(DL)import @$(NDK_ROOT)/libc/imports/ws2nlm.imp$(DL) >> $@
+	@echo $(DL)import @$(AP_HOME)/src/os/netware/apachecore.imp$(DL) >> $@
+	@echo $(DL)module libc$(DL) >> $@
+endif
+ifdef MODULES
+	@echo $(DL)module $(MODULES)$(DL) >> $@
+endif
+ifdef EXPORTS
+	@echo $(DL)export $(EXPORTS)$(DL) >> $@
+endif
+ifdef IMPORTS
+	@echo $(DL)import $(IMPORTS)$(DL) >> $@
+endif
+ifeq ($(LD),nlmconv)
+	@echo $(DL)input $(OBJS)$(DL) >> $@
+	@echo $(DL)input $(PRELUDE)$(DL) >> $@
+	@echo $(DL)output $(TARGET).nlm$(DL) >> $@
+endif
+
diff --git a/connectors/jk/native/apache-1.3/Makefile.tmpl b/connectors/jk/native/apache-1.3/Makefile.tmpl
new file mode 100644
index 0000000..3d591ef
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/Makefile.tmpl
@@ -0,0 +1,38 @@
+##
+## Apache 1.3 Makefile template for mod_jk
+##
+LIB=libjk.$(LIBEXT)
+JK_INCLUDES=$(INCLUDES) -I./include
+
+OBJS=mod_jk.o
+OBJS_LIB=mod_jk.a
+
+SHLIB_OBJS=mod_jk.so-o
+SHLIB_OBJS_LIB=mod_jk.a
+
+all: ${LIB}
+
+#   build the static library by merging the object files
+libjk.a: $(OBJS) $(OBJS_LIB)
+	cp $(OBJS_LIB) $@
+	ar r $@ $(OBJS)
+	${RANLIB} $@
+#   build the shared object library by linking the object files
+libjk.so: $(SHLIB_OBJS) $(SHLIB_OBJS_LIB)
+	rm -f $@
+	$(LD_SHLIB) $(LDFLAGS_SHLIB) -o $@ $(SHLIB_OBJS) $(SHLIB_OBJS_LIB) $(LIBS)
+
+.SUFFIXES: .o .so-o
+.c.o:
+	$(CC) -c $(JK_INCLUDES) $(CFLAGS) $(CPPFLAGS) $(SPACER) $<
+.c.so-o:
+	$(CC) -c $(JK_INCLUDES) $(CFLAGS) $(CFLAGS_SHLIB) $(CPPFLAGS) $(SPACER) $< && mv $*.o $*.so-o
+
+clean:
+	-rm -f $(OBJS) $(SHLIB_OBJS) $(LIB)
+
+distclean: clean
+	-rm -f Makefile
+ 
+depend:
+	echo "No depend"
diff --git a/connectors/jk/native/apache-1.3/libjk.module b/connectors/jk/native/apache-1.3/libjk.module
new file mode 100644
index 0000000..5472647
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/libjk.module
@@ -0,0 +1,5 @@
+Name: jk_module
+ConfigStart
+	LIBS="-Lmodules/jk -L../modules/jk -L../../modules/jk -ljk $LIBS"
+	RULE_HIDE=yes
+ConfigEnd
diff --git a/connectors/jk/native/apache-1.3/mod_jk.c b/connectors/jk/native/apache-1.3/mod_jk.c
new file mode 100644
index 0000000..ebb67ae
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/mod_jk.c
@@ -0,0 +1,2027 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Apache 1.3 plugin for Jakarta/Tomcat                       *
+ *              See ../common/jk_service.h for general mod_jk info         *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ *              Dan Milstein <danmil@shore.net>                            *
+ *              Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+/*
+ * mod_jk: keeps all servlet/jakarta related ramblings together.
+ */
+
+/* #include "ap_config.h" */
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "util_script.h"
+#include "util_date.h"
+#include "http_conf_globals.h"
+
+/*
+ * Jakarta (jk_) include files
+ */
+#ifdef NETWARE
+#define _SYS_TYPES_H_
+#define _NETDB_H_INCLUDED
+#define _IN_
+#define _INET_
+#define _SYS_TIMEVAL_H_
+#define _SYS_SOCKET_H_
+#endif
+#include "jk_global.h"
+#include "jk_util.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+#include "jk_uri_worker_map.h"
+
+#define JK_WORKER_ID        ("jakarta.worker")
+#define JK_HANDLER          ("jakarta-servlet")
+#define JK_DURATION         ("jakarta.worker.duration")
+#define JK_MAGIC_TYPE       ("application/x-jakarta-servlet")
+#define NULL_FOR_EMPTY(x)   ((x && !strlen(x)) ? NULL : x) 
+
+/*
+ * If you are not using SSL, comment out the following line. It will make
+ * apache run faster.  
+ *
+ * Personally, I (DM), think this may be a lie.
+ */
+#define ADD_SSL_INFO    
+
+module MODULE_VAR_EXPORT jk_module;
+extern module dir_module;
+
+/*
+ * Configuration object for the mod_jk module.
+ */
+typedef struct {
+
+    /*
+     * Log stuff
+     */
+    char        *log_file;
+    int         log_level;
+    jk_logger_t *log;
+
+    /*
+     * Worker stuff
+     */
+    jk_map_t *worker_properties;
+    char     *worker_file;
+    jk_map_t *uri_to_context;
+
+    int      mountcopy;
+	char     *secret_key;
+    jk_map_t *automount;
+
+    jk_uri_worker_map_t *uw_map;
+
+    /*
+     * Automatic context path apache alias
+     */
+    char *alias_dir;
+
+    /*
+     * Request Logging
+     */
+
+    char *format_string;
+    array_header *format;
+
+    /*
+     * SSL Support
+     */
+    int  ssl_enable;
+    char *https_indicator;
+    char *certs_indicator;
+    char *cipher_indicator;
+    char *session_indicator;
+	char *key_size_indicator;
+
+    /*
+     * Jk Options
+     */
+    int options;
+
+    /*
+     * Environment variables support
+     */
+    int envvars_in_use;
+    table *envvars; 
+
+    server_rec *s;
+} jk_server_conf_t;
+
+
+/*
+ * The "private", or subclass portion of the web server service class for
+ * Apache 1.3.  An instance of this class is created for each request
+ * handled.  See jk_service.h for details about the ws_service object in
+ * general.  
+ */
+struct apache_private_data {
+    /* 
+     * For memory management for this request.  Aliased to be identical to
+     * the pool in the superclass (jk_ws_service).  
+     */
+    jk_pool_t p;    
+    
+    /* True iff response headers have been returned to client */
+    int response_started;  
+
+    /* True iff request body data has been read from Apache */
+    int read_body_started;
+
+    /* Apache request structure */
+    request_rec *r; 
+};
+typedef struct apache_private_data apache_private_data_t;
+
+typedef struct dir_config_struct {
+    array_header *index_names;
+} dir_config_rec;
+
+static jk_logger_t 		*main_log = NULL;
+static jk_worker_env_t	 worker_env;
+
+static int JK_METHOD ws_start_response(jk_ws_service_t *s,
+                                       int status,
+                                       const char *reason,
+                                       const char * const *header_names,
+                                       const char * const *header_values,
+                                       unsigned num_of_headers);
+
+static int JK_METHOD ws_read(jk_ws_service_t *s,
+                             void *b,
+                             unsigned l,
+                             unsigned *a);
+
+static int JK_METHOD ws_write(jk_ws_service_t *s,
+                              const void *b,
+                              unsigned l);
+/* srevilak - new function prototypes */
+static void jk_server_cleanup(void *data);
+static void jk_generic_cleanup(server_rec *s);
+
+
+
+/* ====================================================================== */
+/* JK Service step callbacks                                              */
+/* ====================================================================== */
+
+
+/*
+ * Send the HTTP response headers back to the browser.
+ *
+ * Think of this function as a method of the apache1.3-specific subclass of
+ * the jk_ws_service class.  Think of the *s param as a "this" or "self"
+ * pointer.  
+ */
+static int JK_METHOD ws_start_response(jk_ws_service_t *s,
+                                       int status,
+                                       const char *reason,
+                                       const char * const *header_names,
+                                       const char * const *header_values,
+                                       unsigned num_of_headers)
+{
+    if(s && s->ws_private) {
+        unsigned h;
+
+        /* Obtain a subclass-specific "this" pointer */
+        apache_private_data_t *p = s->ws_private;
+        request_rec *r = p->r;
+        
+        if(!reason) {
+            reason = "";
+        }
+        r->status = status;
+        r->status_line = ap_psprintf(r->pool, "%d %s", status, reason);
+
+        for(h = 0 ; h < num_of_headers ; h++) {
+            if(!strcasecmp(header_names[h], "Content-type")) {
+                char *tmp = ap_pstrdup(r->pool, header_values[h]);
+                ap_content_type_tolower(tmp);
+                r->content_type = tmp;
+            } else if(!strcasecmp(header_names[h], "Location")) {
+                ap_table_set(r->headers_out, header_names[h], header_values[h]);
+            } else if(!strcasecmp(header_names[h], "Content-Length")) {
+                ap_table_set(r->headers_out, header_names[h], header_values[h]);
+            } else if(!strcasecmp(header_names[h], "Transfer-Encoding")) {
+                ap_table_set(r->headers_out, header_names[h], header_values[h]);
+            } else if(!strcasecmp(header_names[h], "Last-Modified")) {
+                /*
+                 * If the script gave us a Last-Modified header, we can't just
+                 * pass it on blindly because of restrictions on future values.
+                 */
+                ap_update_mtime(r, ap_parseHTTPdate(header_values[h]));
+                ap_set_last_modified(r);
+            } else {                
+                ap_table_add(r->headers_out, header_names[h], header_values[h]);
+            }
+        }
+
+        ap_send_http_header(r);
+        p->response_started = JK_TRUE;
+        
+        return JK_TRUE;
+    }
+    return JK_FALSE;
+}
+
+/*
+ * Read a chunk of the request body into a buffer.  Attempt to read len
+ * bytes into the buffer.  Write the number of bytes actually read into
+ * actually_read.  
+ *
+ * Think of this function as a method of the apache1.3-specific subclass of
+ * the jk_ws_service class.  Think of the *s param as a "this" or "self"
+ * pointer.  
+ */
+static int JK_METHOD ws_read(jk_ws_service_t *s,
+                             void *b,
+                             unsigned len,
+                             unsigned *actually_read)
+{
+    if(s && s->ws_private && b && actually_read) {
+        apache_private_data_t *p = s->ws_private;
+        if(!p->read_body_started) {
+           if(ap_should_client_block(p->r)) { 
+                p->read_body_started = JK_TRUE; 
+            }
+        }
+
+        if(p->read_body_started) {
+            long rv;
+            if ((rv = ap_get_client_block(p->r, b, len)) < 0) {
+                *actually_read = 0;
+            } else {
+                *actually_read = (unsigned) rv;
+            }
+            return JK_TRUE;
+        }
+    }
+    return JK_FALSE;
+}
+
+/*
+ * Write a chunk of response data back to the browser.  If the headers
+ * haven't yet been sent over, send over default header values (Status =
+ * 200, basically).
+ *
+ * Write len bytes from buffer b.
+ *
+ * Think of this function as a method of the apache1.3-specific subclass of
+ * the jk_ws_service class.  Think of the *s param as a "this" or "self"
+ * pointer.  
+ */
+static int JK_METHOD ws_write(jk_ws_service_t *s,
+                              const void *b,
+                              unsigned len)
+{
+    if(s && s->ws_private && b) {
+        apache_private_data_t *p = s->ws_private;
+
+        if(len) {
+            BUFF *bf = p->r->connection->client;
+            char *buf = (char *)b;
+            int w = (int)len;
+            int r = 0;
+
+            if(!p->response_started) {
+                if(!s->start_response(s, 200, NULL, NULL, NULL, 0)) {
+                    return JK_FALSE;
+                }
+            }
+
+            if (p->r->header_only) {
+                ap_bflush(bf);
+                return JK_TRUE;
+            }
+
+            while(len && !p->r->connection->aborted) {
+                w = ap_bwrite(p->r->connection->client, &buf[r], len);
+                if (w > 0) {
+                    /* reset timeout after successful write */
+                    ap_reset_timeout(p->r); 
+                    r += w;
+                    len -= w;
+                } else if (w < 0) {
+                    /* Error writing data -- abort */
+                    if(!p->r->connection->aborted) {
+                        ap_bsetflag(p->r->connection->client, B_EOUT, 1);
+                        p->r->connection->aborted = 1;
+                    }
+                    return JK_FALSE;
+                }
+                
+            }
+           
+            /*
+             * To allow server push.
+             */
+            ap_bflush(bf);
+        }
+
+        return JK_TRUE;
+    }
+    return JK_FALSE;
+}
+
+/* ====================================================================== */
+/* Utility functions                                                      */
+/* ====================================================================== */
+
+/* Log something to JK log file then exit */
+static void jk_error_exit(const char *file, 
+                          int line, 
+                          int level, 
+                          const server_rec *s,
+                          ap_pool *p,
+                          const char *fmt, ...) 
+{
+    va_list ap;
+    char *res;
+
+    va_start(ap, fmt);
+    res = ap_pvsprintf(p, fmt, ap);
+    va_end(ap);
+
+    ap_log_error(file, line, level, s, res);
+
+    /* Exit process */
+    exit(1);
+}
+
+/* Return the content length associated with an Apache request structure */
+static int get_content_length(request_rec *r)
+{
+    if(r->clength > 0) {
+        return r->clength;
+    } else {
+        char *lenp = (char *)ap_table_get(r->headers_in, "Content-Length");
+
+        if(lenp) {
+            int rc = atoi(lenp);
+            if(rc > 0) {
+                return rc;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * Set up an instance of a ws_service object for a single request.  This
+ * particular instance will be of the Apache 1.3-specific subclass.  Copies
+ * all of the important request information from the Apache request object
+ * into the jk_ws_service_t object.
+ *
+ * Params 
+ *
+ * private_data: The subclass-specific data structure, already initialized
+ * (with a pointer to the Apache request_rec structure, among other things)
+ *
+ * s: The base class object.
+ *
+ * conf: Configuration information
+ *  
+ * Called from jk_handler().  See jk_service.h for explanations of what most
+ * of these fields mean.  
+ */
+static int init_ws_service(apache_private_data_t *private_data,
+                           jk_ws_service_t *s,
+                           jk_server_conf_t *conf)
+{
+    request_rec *r      = private_data->r;
+    char *ssl_temp      = NULL;
+    s->jvm_route        = NULL; /* Used for sticky session routing */
+    
+    /* Copy in function pointers (which are really methods) */
+    s->start_response   = ws_start_response;
+    s->read             = ws_read;
+    s->write            = ws_write;
+
+    /* Clear RECO status */
+    s->reco_status  = RECO_NONE;
+
+    s->auth_type    = NULL_FOR_EMPTY(r->connection->ap_auth_type);
+    s->remote_user  = NULL_FOR_EMPTY(r->connection->user);
+
+    s->protocol     = r->protocol;
+    s->remote_host  = (char *)ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_HOST);
+    s->remote_host  = NULL_FOR_EMPTY(s->remote_host);
+
+    s->remote_addr  = NULL_FOR_EMPTY(r->connection->remote_ip);
+
+    /* get server name */
+    /* s->server_name  = (char *)(r->hostname ? r->hostname : r->server->server_hostname); */
+    /* XXX : à la jk2 */
+    s->server_name  = (char *) ap_get_server_name(r);
+    
+    /* get the real port (otherwise redirect failed) */
+    /* s->server_port     = htons( r->connection->local_addr.sin_port ); */
+    /* XXX : à la jk2 */
+    s->server_port  = ap_get_server_port(r);
+
+    s->server_software = (char *)ap_get_server_version();
+
+    s->method         = (char *)r->method;
+    s->content_length = get_content_length(r);
+    s->is_chunked     = r->read_chunked;
+    s->no_more_chunks = 0;
+    s->query_string   = r->args;
+
+    /*
+     * The 2.2 servlet spec errata says the uri from
+     * HttpServletRequest.getRequestURI() should remain encoded.
+     * [http://java.sun.com/products/servlet/errata_042700.html]
+     *
+     * We use JkOptions to determine which method to be used 
+     * 
+     * ap_escape_uri is the latest recommanded but require 
+     *               some java decoding (in TC 3.3 rc2)
+     *
+     * unparsed_uri is used for strict compliance with spec and
+     *              old Tomcat (3.2.3 for example)
+     *
+     * uri is use for compatibilty with mod_rewrite with old Tomcats
+     */
+
+    switch (conf->options & JK_OPT_FWDURIMASK) {
+   
+        case JK_OPT_FWDURICOMPATUNPARSED :
+            s->req_uri      = r->unparsed_uri;
+            if (s->req_uri != NULL) {
+                char *query_str = strchr(s->req_uri, '?');
+                if (query_str != NULL) {
+                    *query_str = 0;
+                }
+            }
+
+        break;
+
+        case JK_OPT_FWDURICOMPAT :
+            s->req_uri = r->uri;
+        break;
+
+        case JK_OPT_FWDURIESCAPED :
+            s->req_uri      = ap_escape_uri(r->pool, r->uri);
+        break;
+
+        default :
+            return JK_FALSE;
+    }
+
+    s->is_ssl       = JK_FALSE;
+    s->ssl_cert     = NULL;
+    s->ssl_cert_len = 0;
+    s->ssl_cipher   = NULL;		/* required by Servlet 2.3 Api, allready in original ajp13 */
+    s->ssl_session  = NULL;
+	s->ssl_key_size = -1;		/* required by Servlet 2.3 Api, added in jtc */
+
+    if(conf->ssl_enable || conf->envvars_in_use) {
+        ap_add_common_vars(r);
+
+        if(conf->ssl_enable) {
+            ssl_temp = (char *)ap_table_get(r->subprocess_env, conf->https_indicator);
+            if(ssl_temp && !strcasecmp(ssl_temp, "on")) {
+                s->is_ssl       = JK_TRUE;
+                s->ssl_cert     = (char *)ap_table_get(r->subprocess_env, conf->certs_indicator);
+                if(s->ssl_cert) {
+                    s->ssl_cert_len = strlen(s->ssl_cert);
+                }
+				/* Servlet 2.3 API */
+                s->ssl_cipher   = (char *)ap_table_get(r->subprocess_env, conf->cipher_indicator);
+                s->ssl_session  = (char *)ap_table_get(r->subprocess_env, conf->session_indicator);
+
+                if (conf->options & JK_OPT_FWDKEYSIZE) {
+				    /* Servlet 2.3 API */
+                    ssl_temp = (char *)ap_table_get(r->subprocess_env, conf->key_size_indicator);
+				    if (ssl_temp) 
+            		    s->ssl_key_size = atoi(ssl_temp);
+                }
+            }
+        }
+
+        if(conf->envvars_in_use) {
+            array_header *t = ap_table_elts(conf->envvars);        
+            if(t && t->nelts) {
+                int i;
+                table_entry *elts = (table_entry *)t->elts;
+                s->attributes_names = ap_palloc(r->pool, sizeof(char *) * t->nelts);
+                s->attributes_values = ap_palloc(r->pool, sizeof(char *) * t->nelts);
+
+                for(i = 0 ; i < t->nelts ; i++) {
+                    s->attributes_names[i] = elts[i].key;
+                    s->attributes_values[i] = (char *)ap_table_get(r->subprocess_env, elts[i].key);
+                    if(!s->attributes_values[i]) {
+                        s->attributes_values[i] = elts[i].val;
+                    }
+                }
+
+                s->num_attributes = t->nelts;
+            }
+        }
+    }
+
+    s->headers_names    = NULL;
+    s->headers_values   = NULL;
+    s->num_headers      = 0;
+    if(r->headers_in && ap_table_elts(r->headers_in)) {
+        int need_content_length_header = (!s->is_chunked && s->content_length == 0) ? JK_TRUE : JK_FALSE;
+        array_header *t = ap_table_elts(r->headers_in);        
+        if(t && t->nelts) {
+            int i;
+            table_entry *elts = (table_entry *)t->elts;
+            s->num_headers = t->nelts;
+            /* allocate an extra header slot in case we need to add a content-length header */
+            s->headers_names  = ap_palloc(r->pool, sizeof(char *) * (t->nelts + 1));
+            s->headers_values = ap_palloc(r->pool, sizeof(char *) * (t->nelts + 1));
+            if(!s->headers_names || !s->headers_values)
+                return JK_FALSE;
+            for(i = 0 ; i < t->nelts ; i++) {
+                char *hname = ap_pstrdup(r->pool, elts[i].key);
+                s->headers_values[i] = ap_pstrdup(r->pool, elts[i].val);
+                s->headers_names[i] = hname;
+                while(*hname) {
+                    *hname = tolower(*hname);
+                    hname++;
+                }
+                if(need_content_length_header &&
+                        !strncmp(s->headers_values[i],"content-length",14)) {
+                    need_content_length_header = JK_FALSE;
+                }
+            }
+            /* Add a content-length = 0 header if needed.
+             * Ajp13 assumes an absent content-length header means an unknown,
+             * but non-zero length body.
+             */
+            if(need_content_length_header) {
+                s->headers_names[s->num_headers] = "content-length";
+                s->headers_values[s->num_headers] = "0";
+                s->num_headers++;
+            }
+        }
+        /* Add a content-length = 0 header if needed.*/
+        else if (need_content_length_header) {
+            s->headers_names  = ap_palloc(r->pool, sizeof(char *));
+            s->headers_values = ap_palloc(r->pool, sizeof(char *));
+            if(!s->headers_names || !s->headers_values)
+                return JK_FALSE;
+            s->headers_names[0] = "content-length";
+            s->headers_values[0] = "0";
+            s->num_headers++;
+        }
+    }
+
+    return JK_TRUE;
+}
+
+/*
+ * The JK module command processors
+ *
+ * The below are all installed so that Apache calls them while it is
+ * processing its config files.  This allows configuration info to be
+ * copied into a jk_server_conf_t object, which is then used for request
+ * filtering/processing.  
+ *
+ * See jk_cmds definition below for explanations of these options.
+ */
+
+/*
+ * JkMountCopy directive handling
+ *
+ * JkMountCopy On/Off
+ */
+
+static const char *jk_set_mountcopy(cmd_parms *cmd, 
+                                    void *dummy, 
+                                    int flag) 
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+    
+    /* Set up our value */
+    conf->mountcopy = flag ? JK_TRUE : JK_FALSE;
+
+    return NULL;
+}
+
+/*
+ * JkMount directive handling 
+ *
+ * JkMount URI(context) worker
+ */
+
+static const char *jk_mount_context(cmd_parms *cmd, 
+                                    void *dummy, 
+                                    char *context,
+                                    char *worker,
+                                    char *maybe_cookie)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+    char *old;
+    if (context[0]!='/')
+        return "Context should start with /";
+
+    /*
+     * Add the new worker to the alias map.
+     */
+    map_put(conf->uri_to_context, context, worker, (void **)&old);
+    return NULL;
+}
+
+/*
+ * JkAutoMount directive handling 
+ *
+ * JkAutoMount worker [virtualhost]
+ */
+
+static const char *jk_automount_context(cmd_parms *cmd,
+                                        void *dummy,
+										char *worker,
+										char *virtualhost)
+{
+	server_rec *s = cmd->server;
+	jk_server_conf_t *conf =
+		(jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+	/*
+	 * Add the new automount to the auto map.
+	 */
+	char * old;
+	map_put(conf->automount, worker, virtualhost, (void **)&old);
+	return NULL;
+}
+
+/*
+ * JkWorkersFile Directive Handling
+ *
+ * JkWorkersFile file
+ */
+
+static const char *jk_set_worker_file(cmd_parms *cmd, 
+                                      void *dummy, 
+                                      char *worker_file)
+{
+    server_rec *s = cmd->server;
+    struct stat statbuf;
+
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    /* we need an absolut path */
+    conf->worker_file = ap_server_root_relative(cmd->pool,worker_file);
+
+#ifdef CHROOTED_APACHE
+    ap_server_strip_chroot(conf->worker_file,0);
+#endif
+
+    if (conf->worker_file == worker_file)
+        conf->worker_file = ap_pstrdup(cmd->pool,worker_file);
+ 
+    if (conf->worker_file == NULL)
+        return "JkWorkersFile file_name invalid";
+
+    if (stat(conf->worker_file, &statbuf) == -1)
+        return "Can't find the workers file specified";
+
+    return NULL;
+}
+
+/*
+ * JkLogFile Directive Handling
+ *
+ * JkLogFile file
+ */
+
+static const char *jk_set_log_file(cmd_parms *cmd, 
+                                   void *dummy, 
+                                   char *log_file)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    /* we need an absolut path */
+    conf->log_file = ap_server_root_relative(cmd->pool,log_file);
+
+#ifdef CHROOTED_APACHE
+    ap_server_strip_chroot(conf->log_file,0);
+#endif
+
+    if ( conf->log_file == log_file)
+        conf->log_file = ap_pstrdup(cmd->pool,log_file);
+ 
+    if (conf->log_file == NULL)
+        return "JkLogFile file_name invalid";
+
+    return NULL;
+}
+
+/*
+ * JkLogLevel Directive Handling
+ *
+ * JkLogLevel debug/info/request/error/emerg
+ */
+
+static const char *jk_set_log_level(cmd_parms *cmd,
+                                    void *dummy,
+                                    char *log_level)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+    
+    conf->log_level = jk_parse_log_level(log_level);
+
+    return NULL;
+}
+
+/*
+ * JkLogStampFormat Directive Handling
+ *
+ * JkLogStampFormat "[%a %b %d %H:%M:%S %Y] " 
+ */
+
+static const char * jk_set_log_fmt(cmd_parms *cmd,
+                   void *dummy,
+                   char * log_format)
+{
+    jk_set_log_format(log_format);
+    return NULL;
+}
+
+/*
+ * JkAutoAlias Directive Handling
+ * 
+ * JkAutoAlias application directory
+ */
+
+static const char *jk_set_auto_alias(cmd_parms *cmd,
+                                    void *dummy,
+                                    char *directory)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->alias_dir = directory;
+
+    if (conf->alias_dir == NULL)
+        return "JkAutoAlias directory invalid";
+
+    return NULL;
+}
+
+/*****************************************************************
+ *
+ * Actually logging.
+ */
+
+typedef const char *(*item_key_func) (request_rec *, char *);
+                                                             
+typedef struct {                                             
+    item_key_func func;
+    char *arg;         
+} request_log_format_item;
+
+static const char *process_item(request_rec *r,
+                          request_log_format_item *item)
+{
+    const char *cp;
+
+    cp = (*item->func) (r,item->arg);
+    return cp ? cp : "-";
+}
+
+static void request_log_transaction(request_rec *r,
+                                  jk_server_conf_t *conf)
+{
+    request_log_format_item *items;
+    char *str, *s;
+    int i;
+    int len = 0;
+    const char **strs;
+    int *strl;
+    array_header *format = conf->format;
+
+    strs = ap_palloc(r->pool, sizeof(char *) * (format->nelts));
+    strl = ap_palloc(r->pool, sizeof(int) * (format->nelts));
+    items = (request_log_format_item *) format->elts;
+    for (i = 0; i < format->nelts; ++i) {
+        strs[i] = process_item(r, &items[i]);
+    }
+    for (i = 0; i < format->nelts; ++i) {
+        len += strl[i] = strlen(strs[i]);
+    }
+    str = ap_palloc(r->pool, len + 1);
+    for (i = 0, s = str; i < format->nelts; ++i) {
+        memcpy(s, strs[i], strl[i]);
+        s += strl[i];
+    }
+    *s = 0;
+    jk_log(conf->log ? conf->log : main_log, JK_LOG_REQUEST, "%s", str);
+}
+
+/*****************************************************************
+ *
+ * Parsing the log format string
+ */
+
+static char *format_integer(pool *p, int i)
+{
+    return ap_psprintf(p, "%d", i);
+}
+ 
+static char *pfmt(pool *p, int i)
+{
+    if (i <= 0) {
+        return "-";
+    }
+    else {
+        return format_integer(p, i);
+    }
+}
+
+static const char *constant_item(request_rec *dummy, char *stuff)
+{
+    return stuff;
+}
+
+static const char *log_worker_name(request_rec *r, char *a)
+{
+    return ap_table_get(r->notes, JK_WORKER_ID);
+}
+
+
+static const char *log_request_duration(request_rec *r, char *a)
+{
+    return ap_table_get(r->notes, JK_DURATION);
+}
+ 
+static const char *log_request_line(request_rec *r, char *a)
+{
+            /* NOTE: If the original request contained a password, we
+             * re-write the request line here to contain XXXXXX instead:
+             * (note the truncation before the protocol string for HTTP/0.9 requests)
+             * (note also that r->the_request contains the unmodified request)
+             */
+    return (r->parsed_uri.password) ? ap_pstrcat(r->pool, r->method, " ",
+                                         ap_unparse_uri_components(r->pool, &r->parsed_uri, 0),
+                                         r->assbackwards ? NULL : " ", r->protocol, NULL)
+                                        : r->the_request;
+}
+
+/* These next two routines use the canonical name:port so that log
+ * parsers don't need to duplicate all the vhost parsing crud.
+ */
+static const char *log_virtual_host(request_rec *r, char *a)
+{
+    return r->server->server_hostname;
+}
+ 
+static const char *log_server_port(request_rec *r, char *a)
+{
+    return ap_psprintf(r->pool, "%u",
+        r->server->port ? r->server->port : ap_default_port(r));
+}
+ 
+/* This respects the setting of UseCanonicalName so that
+ * the dynamic mass virtual hosting trick works better.
+ */
+static const char *log_server_name(request_rec *r, char *a)
+{
+    return ap_get_server_name(r);
+}
+
+static const char *log_request_uri(request_rec *r, char *a)
+{
+    return r->uri;
+}
+static const char *log_request_method(request_rec *r, char *a)
+{
+    return r->method;
+}
+
+static const char *log_request_protocol(request_rec *r, char *a)
+{
+    return r->protocol;
+}
+static const char *log_request_query(request_rec *r, char *a)
+{
+    return (r->args != NULL) ? ap_pstrcat(r->pool, "?", r->args, NULL)
+                             : "";
+}                  
+static const char *log_status(request_rec *r, char *a)
+{                  
+    return pfmt(r->pool,r->status);
+}                  
+                   
+static const char *clf_log_bytes_sent(request_rec *r, char *a)
+{                  
+    if (!r->sent_bodyct) {
+        return "-";
+    }              
+    else {        
+        long int bs;
+        ap_bgetopt(r->connection->client, BO_BYTECT, &bs);
+        return ap_psprintf(r->pool, "%ld", bs);
+    }              
+}                  
+    
+static const char *log_bytes_sent(request_rec *r, char *a)
+{                  
+    if (!r->sent_bodyct) {
+        return "0";
+    }              
+    else {        
+        long int bs;
+        ap_bgetopt(r->connection->client, BO_BYTECT, &bs);
+        return ap_psprintf(r->pool, "%ld", bs);
+    }              
+}
+
+static struct log_item_list {
+    char ch;
+    item_key_func func;
+} log_item_keys[] = {
+
+    {
+        'T', log_request_duration
+    },
+    {
+        'r', log_request_line
+    },
+    {
+        'U', log_request_uri
+    },
+    {
+        's', log_status
+    },
+    {
+        'b', clf_log_bytes_sent
+    },
+    {
+        'B', log_bytes_sent
+    },
+    {
+        'V', log_server_name
+    },
+    {
+        'v', log_virtual_host
+    },
+    {
+        'p', log_server_port
+    },
+    {
+        'H', log_request_protocol
+    },
+    {
+        'm', log_request_method
+    },
+    {
+        'q', log_request_query
+    },
+    {
+        'w', log_worker_name
+    },
+    {
+        '\0'
+    }
+};
+
+static struct log_item_list *find_log_func(char k)
+{
+    int i;
+
+    for (i = 0; log_item_keys[i].ch; ++i)
+        if (k == log_item_keys[i].ch) {
+            return &log_item_keys[i];
+        }
+
+    return NULL;
+}
+
+static char *parse_request_log_misc_string(pool *p,
+                                           request_log_format_item *it,
+                                           const char **sa)
+{
+    const char *s;
+    char *d;
+
+    it->func = constant_item;
+
+    s = *sa;
+    while (*s && *s != '%') {
+        s++;
+    }
+    /*
+     * This might allocate a few chars extra if there's a backslash
+     * escape in the format string.
+     */
+    it->arg = ap_palloc(p, s - *sa + 1);
+
+    d = it->arg;
+    s = *sa;
+    while (*s && *s != '%') {
+        if (*s != '\\') {
+            *d++ = *s++;
+        }
+        else {
+            s++;
+            switch (*s) {
+            case '\\':
+                *d++ = '\\';
+                s++;
+                break;
+            case 'n':
+                *d++ = '\n';
+                s++;
+                break;
+            case 't':
+                *d++ = '\t';
+                s++;
+                break;
+            default:
+                /* copy verbatim */
+                *d++ = '\\';
+                /*
+                 * Allow the loop to deal with this *s in the normal
+                 * fashion so that it handles end of string etc.
+                 * properly.
+                 */
+                break;
+            }
+        }
+    }
+    *d = '\0';
+
+    *sa = s;
+    return NULL;
+}
+
+static char *parse_request_log_item(pool *p,
+                                    request_log_format_item *it,
+                                    const char **sa)
+{
+    const char *s = *sa;
+    struct log_item_list *l;
+
+    if (*s != '%') {
+        return parse_request_log_misc_string(p, it, sa);
+    }
+    
+    ++s;
+    it->arg = "";               /* For safety's sake... */
+    
+    l = find_log_func(*s++);
+    if (!l) {
+        char dummy[2];
+
+        dummy[0] = s[-1];
+        dummy[1] = '\0';
+        return ap_pstrcat(p, "Unrecognized JkRequestLogFormat directive %",
+                          dummy, NULL);
+    }
+    it->func = l->func;
+    *sa = s;
+    return NULL;
+}
+
+static array_header *parse_request_log_string(pool *p, const char *s,
+                                              const char **err)
+{
+    array_header *a = ap_make_array(p, 15, sizeof(request_log_format_item));
+    char *res;
+
+    while (*s) {
+        if ((res = parse_request_log_item(p, (request_log_format_item *) ap_push_array(a), &s))) {
+            *err = res;
+            return NULL;
+        }
+    }    
+     
+    s = "\n";
+    parse_request_log_item(p, (request_log_format_item *) ap_push_array(a), &s);
+    return a;
+}
+
+/*
+ * JkRequestLogFormat Directive Handling
+ *
+ * JkRequestLogFormat format string
+ *
+ * %b - Bytes sent, excluding HTTP headers. In CLF format
+ * %B - Bytes sent, excluding HTTP headers.             
+ * %H - The request protocol              
+ * %m - The request method
+ * %p - The canonical Port of the server serving the request
+ * %q - The query string (prepended with a ? if a query string exists,
+ *      otherwise an empty string)
+ * %r - First line of request
+ * %s - request HTTP status code
+ * %T - Requset duration, elapsed time to handle request in seconds '.' micro seconds
+ * %U - The URL path requested, not including any query string.
+ * %v - The canonical ServerName of the server serving the request.
+ * %V - The server name according to the UseCanonicalName setting.
+ * %w - Tomcat worker name
+ */
+
+static const char *jk_set_request_log_format(cmd_parms *cmd,
+                                             void *dummy,    
+                                             char *format)
+{
+    const char *err_string = NULL;
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->format_string = ap_pstrdup(cmd->pool,format);
+    if( format != NULL ) {
+        conf->format = parse_request_log_string(cmd->pool, format, &err_string);
+    }
+    if( conf->format == NULL )
+        return "JkRequestLogFormat format array NULL";
+
+    return err_string;
+}
+
+/*
+ * JkExtractSSL Directive Handling
+ *
+ * JkExtractSSL On/Off
+ */
+
+static const char *jk_set_enable_ssl(cmd_parms *cmd, 
+                                     void *dummy, 
+                                     int flag) 
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+    
+    /* Set up our value */
+    conf->ssl_enable = flag ? JK_TRUE : JK_FALSE;
+    return NULL;
+}
+
+/*
+ * JkHTTPSIndicator Directive Handling
+ *
+ * JkHTTPSIndicator HTTPS
+ */
+
+static const char *jk_set_https_indicator(cmd_parms *cmd, 
+                                          void *dummy, 
+                                          char *indicator)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->https_indicator = ap_pstrdup(cmd->pool,indicator);
+    return NULL;
+}
+
+/*
+ * JkCERTSIndicator Directive Handling
+ *
+ * JkCERTSIndicator SSL_CLIENT_CERT
+ */
+
+static const char *jk_set_certs_indicator(cmd_parms *cmd, 
+                                          void *dummy, 
+                                          char *indicator)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->certs_indicator = ap_pstrdup(cmd->pool,indicator);
+    return NULL;
+}
+
+/*
+ * JkCIPHERIndicator Directive Handling
+ *
+ * JkCIPHERIndicator SSL_CIPHER
+ */
+
+static const char *jk_set_cipher_indicator(cmd_parms *cmd, 
+                                           void *dummy, 
+                                           char *indicator)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->cipher_indicator = ap_pstrdup(cmd->pool,indicator);
+    return NULL;
+}
+
+/*
+ * JkSESSIONIndicator Directive Handling
+ *
+ * JkSESSIONIndicator SSL_SESSION_ID
+ */
+
+static const char *jk_set_session_indicator(cmd_parms *cmd, 
+                                           void *dummy, 
+                                           char *indicator)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->session_indicator = ap_pstrdup(cmd->pool,indicator);
+    return NULL;
+}
+
+/*
+ * JkKEYSIZEIndicator Directive Handling
+ *
+ * JkKEYSIZEIndicator SSL_CIPHER_USEKEYSIZE
+ */
+
+static const char *jk_set_key_size_indicator(cmd_parms *cmd,
+                                           void *dummy,
+                                           char *indicator)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->key_size_indicator = ap_pstrdup(cmd->pool,indicator);
+    return NULL;
+}
+
+/*
+ * JkOptions Directive Handling
+ *
+ *
+ * +ForwardSSLKeySize        => Forward SSL Key Size, to follow 2.3 specs but may broke old TC 3.2
+ * -ForwardSSLKeySize        => Don't Forward SSL Key Size, will make mod_jk works with all TC release
+ *  ForwardURICompat         => Forward URI normally, less spec compliant but mod_rewrite compatible (old TC)
+ *  ForwardURICompatUnparsed => Forward URI as unparsed, spec compliant but broke mod_rewrite (old TC)
+ *  ForwardURIEscaped        => Forward URI escaped and Tomcat (3.3 rc2) stuff will do the decoding part
+ *  ForwardDirectories       => Forward all directory requests with no index files to Tomcat
+ */
+
+const char *jk_set_options(cmd_parms *cmd,
+                           void *dummy,
+                           const char *line)
+{
+    int  opt = 0; 
+    int  mask = 0;
+    char action;
+    char *w;
+
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    while (line[0] != 0) {
+        w = ap_getword_conf(cmd->pool, &line);
+        action = 0;
+
+        if (*w == '+' || *w == '-') {
+            action = *(w++);
+        }
+
+        mask = 0;
+
+        if (!strcasecmp(w, "ForwardKeySize")) {
+            opt = JK_OPT_FWDKEYSIZE;
+        }
+        else if (!strcasecmp(w, "ForwardURICompat")) {
+            opt = JK_OPT_FWDURICOMPAT;
+            mask = JK_OPT_FWDURIMASK;
+        }
+        else if (!strcasecmp(w, "ForwardURICompatUnparsed")) {
+            opt = JK_OPT_FWDURICOMPATUNPARSED;
+            mask = JK_OPT_FWDURIMASK;
+        }
+        else if (!strcasecmp(w, "ForwardURIEscaped")) {
+            opt = JK_OPT_FWDURIESCAPED;
+            mask = JK_OPT_FWDURIMASK;
+        }
+        else if (!strcasecmp(w, "ForwardDirectories")) {
+            opt = JK_OPT_FWDDIRS;
+        }
+        else
+            return ap_pstrcat(cmd->pool, "JkOptions: Illegal option '", w, "'", NULL);
+
+        conf->options &= ~mask;
+
+        if (action == '-') {
+            conf->options &= ~opt;
+        }
+        else if (action == '+') {
+            conf->options |=  opt;
+        }
+        else {            /* for now +Opt == Opt */
+            conf->options |=  opt;
+        }
+    }
+    return NULL;
+}
+
+/*
+ * JkEnvVar Directive Handling
+ *
+ * JkEnvVar MYOWNDIR
+ */
+
+static const char *jk_add_env_var(cmd_parms *cmd, 
+                                  void *dummy, 
+                                  char *env_name,
+                                  char *default_value)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->envvars_in_use = JK_TRUE;
+    
+    ap_table_add(conf->envvars, env_name, default_value);
+
+    return NULL;
+}
+
+static const command_rec jk_cmds[] =
+{
+    /*
+     * JkWorkersFile specifies a full path to the location of the worker 
+     * properties file.
+     *
+     * This file defines the different workers used by apache to redirect 
+     * servlet requests.
+     */
+    {"JkWorkersFile", jk_set_worker_file, NULL, RSRC_CONF, TAKE1,
+     "the name of a worker file for the Jakarta servlet containers"},
+
+	/*
+	 * JkAutoMount specifies that the list of handled URLs must be 	
+	 * asked to the servlet engine (autoconf feature)
+	 */
+	{"JkAutoMount", jk_automount_context, NULL, RSRC_CONF, TAKE12,
+     "automatic mount points to a servlet-engine worker"},
+
+    /*
+     * JkMount mounts a url prefix to a worker (the worker need to be
+     * defined in the worker properties file.
+     */
+    {"JkMount", jk_mount_context, NULL, RSRC_CONF, TAKE23,
+     "A mount point from a context to a servlet-engine worker"},
+
+    /*
+     * JkMountCopy specifies if mod_jk should copy the mount points
+     * from the main server to the virtual servers.
+     */
+    {"JkMountCopy", jk_set_mountcopy, NULL, RSRC_CONF, FLAG,
+     "Should the base server mounts be copied to the virtual server"},
+
+    /*
+     * JkLogFile & JkLogLevel specifies to where should the plugin log
+     * its information and how much.
+     * JkLogStampFormat specify the time-stamp to be used on log
+     */
+    {"JkLogFile", jk_set_log_file, NULL, RSRC_CONF, TAKE1,
+     "Full path to the Jakarta mod_jk module log file"},
+    {"JkLogLevel", jk_set_log_level, NULL, RSRC_CONF, TAKE1,
+     "The Jakarta mod_jk module log level, can be debug, info, request, error, or emerg"},
+    {"JkLogStampFormat", jk_set_log_fmt, NULL, RSRC_CONF, TAKE1,
+     "The Jakarta mod_jk module log format, follow strftime synthax"},
+    {"JkRequestLogFormat", jk_set_request_log_format, NULL, RSRC_CONF, TAKE1,
+     "The Jakarta mod_jk module request log format string"},
+
+    /*
+     * Automatically Alias webapp context directories into the Apache
+     * document space.
+     */
+    {"JkAutoAlias", jk_set_auto_alias, NULL, RSRC_CONF, TAKE1,
+     "The Jakarta mod_jk module automatic context apache alias directory"},
+
+    /*
+     * Apache has multiple SSL modules (for example apache_ssl, stronghold
+     * IHS ...). Each of these can have a different SSL environment names
+     * The following properties let the administrator specify the envoiroment
+     * variables names.
+     *
+     * HTTPS - indication for SSL 
+     * CERTS - Base64-Der-encoded client certificates.
+     * CIPHER - A string specifing the ciphers suite in use.
+     * SESSION - A string specifing the current SSL session.
+     * KEYSIZE - Size of Key used in dialogue (#bits are secure)
+     */
+    {"JkHTTPSIndicator", jk_set_https_indicator, NULL, RSRC_CONF, TAKE1,
+     "Name of the Apache environment that contains SSL indication"},
+    {"JkCERTSIndicator", jk_set_certs_indicator, NULL, RSRC_CONF, TAKE1,
+     "Name of the Apache environment that contains SSL client certificates"},
+    {"JkCIPHERIndicator", jk_set_cipher_indicator, NULL, RSRC_CONF, TAKE1,
+     "Name of the Apache environment that contains SSL client cipher"},
+    {"JkSESSIONIndicator", jk_set_session_indicator, NULL, RSRC_CONF, TAKE1,
+     "Name of the Apache environment that contains SSL session"},
+    {"JkKEYSIZEIndicator", jk_set_key_size_indicator, NULL, RSRC_CONF, TAKE1,
+     "Name of the Apache environment that contains SSL key size in use"},
+    {"JkExtractSSL", jk_set_enable_ssl, NULL, RSRC_CONF, FLAG,
+     "Turns on SSL processing and information gathering by mod_jk"},     
+
+    /*
+     * Options to tune mod_jk configuration
+     * for now we understand :
+     * +ForwardSSLKeySize        => Forward SSL Key Size, to follow 2.3 specs but may broke old TC 3.2
+     * -ForwardSSLKeySize        => Don't Forward SSL Key Size, will make mod_jk works with all TC release
+     *  ForwardURICompat         => Forward URI normally, less spec compliant but mod_rewrite compatible (old TC)
+     *  ForwardURICompatUnparsed => Forward URI as unparsed, spec compliant but broke mod_rewrite (old TC)
+     *  ForwardURIEscaped        => Forward URI escaped and Tomcat (3.3 rc2) stuff will do the decoding part
+     */
+    {"JkOptions", jk_set_options, NULL, RSRC_CONF, RAW_ARGS,
+     "Set one of more options to configure the mod_jk module"},
+
+	/*
+	 * JkEnvVar let user defines envs var passed from WebServer to 
+	 * Servlet Engine
+	 */
+    {"JkEnvVar", jk_add_env_var, NULL, RSRC_CONF, TAKE2,
+     "Adds a name of environment variable that should be sent to servlet-engine"},     
+
+    {NULL}
+};
+
+/* ====================================================================== */
+/* The JK module handlers                                                 */
+/* ====================================================================== */
+
+/*
+ * Called to handle a single request.
+ */ 
+static int jk_handler(request_rec *r)
+{   
+    /* Retrieve the worker name stored by jk_translate() */
+    const char *worker_name = ap_table_get(r->notes, JK_WORKER_ID);
+    int rc;
+
+    if(r->proxyreq) {
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    /* Set up r->read_chunked flags for chunked encoding, if present */
+    if(rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) {
+	return rc;
+    }
+      
+    if(worker_name) {
+        jk_server_conf_t *conf =
+            (jk_server_conf_t *)ap_get_module_config(r->server->module_config, &jk_module);
+        jk_logger_t *l = conf->log ? conf->log : main_log;
+
+        jk_worker_t *worker = wc_get_worker_for_name(worker_name, l);
+
+        if(worker) {
+            struct timeval tv_begin,tv_end;
+            int rc = JK_FALSE;
+            apache_private_data_t private_data;
+            jk_ws_service_t s;
+            jk_pool_atom_t buf[SMALL_POOL_SIZE];
+            jk_open_pool(&private_data.p, buf, sizeof(buf));
+
+            private_data.response_started = JK_FALSE;
+            private_data.read_body_started = JK_FALSE;
+            private_data.r = r;
+
+            jk_init_ws_service(&s);
+
+            s.ws_private = &private_data;
+            s.pool = &private_data.p;            
+#ifndef NO_GETTIMEOFDAY
+            if(conf->format != NULL) {
+                gettimeofday(&tv_begin, NULL);
+            }
+#endif
+
+            if(init_ws_service(&private_data, &s, conf)) {
+                jk_endpoint_t *end = NULL;
+                if(worker->get_endpoint(worker, &end, l)) {
+                    int is_recoverable_error = JK_FALSE;
+                    rc = end->service(end, &s, l, &is_recoverable_error);
+                
+                    if (s.content_read < s.content_length ||
+                        (s.is_chunked && ! s.no_more_chunks)) {
+                        /*
+                         * If the servlet engine didn't consume all of the
+                         * request data, consume and discard all further
+                         * characters left to read from client 
+                         */
+                        char *buff = ap_palloc(r->pool, 2048);
+                        if (buff != NULL) {
+                            int rd;
+                            while ((rd = ap_get_client_block(r, buff, 2048)) > 0) {
+                                s.content_read += rd;
+                            }
+                        }
+                    }
+                    end->done(&end, l);
+                }
+#ifndef NO_GETTIMEOFDAY
+                if(conf->format != NULL) {
+                    char *duration = NULL;
+                    char *status = NULL;
+                    long micro,seconds;
+                    gettimeofday(&tv_end, NULL);
+                    if( tv_end.tv_usec < tv_begin.tv_usec ) {
+                        tv_end.tv_usec += 1000000;
+                        tv_end.tv_sec--;
+                    }
+                    micro = tv_end.tv_usec - tv_begin.tv_usec;
+                    seconds = tv_end.tv_sec - tv_begin.tv_sec;
+                    duration = ap_psprintf(r->pool,"%.1d.%.6d",seconds,micro);
+                    ap_table_setn(r->notes, JK_DURATION, duration);
+                    request_log_transaction(r,conf);
+                }
+#endif
+            }
+
+            jk_close_pool(&private_data.p);
+
+            if(rc) {
+                /* If tomcat returned no body and the status is not OK,
+                   let apache handle the error code */
+                if( !r->sent_bodyct && r->status >= HTTP_BAD_REQUEST ) {
+                    return r->status;
+                }
+                return OK;  /* NOT r->status, even if it has changed. */
+            }
+        }
+    }
+
+    return HTTP_INTERNAL_SERVER_ERROR;
+}
+
+/*
+ * Create a default config object.
+ */
+static void *create_jk_config(ap_pool *p, server_rec *s)
+{
+    jk_server_conf_t *c =
+        (jk_server_conf_t *) ap_pcalloc(p, sizeof(jk_server_conf_t));
+
+    c->worker_properties = NULL;
+    map_alloc(&c->worker_properties);
+    c->worker_file   = NULL;
+    c->log_file      = NULL;
+    c->log_level     = -1;
+    c->log           = NULL;
+    c->alias_dir     = NULL;
+    c->format_string = NULL;
+    c->format        = NULL;
+    c->mountcopy     = JK_FALSE;
+    c->options       = JK_OPT_FWDURIDEFAULT;
+
+    /*
+     * By default we will try to gather SSL info. 
+     * Disable this functionality through JkExtractSSL
+     */
+    c->ssl_enable  = JK_TRUE;
+    /*
+     * The defaults ssl indicators match those in mod_ssl (seems 
+     * to be in more use).
+     */
+    c->https_indicator  = "HTTPS";
+    c->certs_indicator  = "SSL_CLIENT_CERT";
+    
+    /*
+     * The following (comented out) environment variables match apache_ssl! 
+     * If you are using apache_sslapache_ssl uncomment them (or use the 
+     * configuration directives to set them.
+     *
+    c->cipher_indicator = "HTTPS_CIPHER";
+    c->session_indicator = NULL;
+	c->key_size_indicator = NULL;	
+     */
+
+    /*
+     * The following environment variables match mod_ssl! If you
+     * are using another module (say apache_ssl) comment them out.
+     */
+    c->cipher_indicator = "SSL_CIPHER";
+    c->session_indicator = "SSL_SESSION_ID";
+	c->key_size_indicator = "SSL_CIPHER_USEKEYSIZE";
+
+    if(!map_alloc(&(c->uri_to_context))) {
+        jk_error_exit(APLOG_MARK, APLOG_EMERG, s, p, "Memory error");
+    }
+    if(!map_alloc(&(c->automount))) {
+        jk_error_exit(APLOG_MARK, APLOG_EMERG, s, p, "Memory error");
+    }
+    c->uw_map = NULL;
+	c->secret_key = NULL;
+
+    c->envvars_in_use = JK_FALSE;
+    c->envvars = ap_make_table(p, 0);
+
+    c->s = s;
+
+    return c;
+}
+
+
+static void copy_jk_map(ap_pool *p, server_rec * s, jk_map_t * src, jk_map_t * dst)
+{
+	int sz = map_size(src);
+        int i;
+        for(i = 0 ; i < sz ; i++) {
+            void *old;
+            char *name = map_name_at(src, i);
+            if(map_get(src, name, NULL) == NULL) {
+                if(!map_put(dst, name, ap_pstrdup(p, map_get_string(src, name, NULL)), &old)) {
+                    jk_error_exit(APLOG_MARK, APLOG_EMERG, s, p, "Memory error");
+                }
+            }
+        }
+}
+
+static void *merge_jk_config(ap_pool *p, 
+                             void *basev, 
+                             void *overridesv)
+{
+    jk_server_conf_t *base = (jk_server_conf_t *) basev;
+    jk_server_conf_t *overrides = (jk_server_conf_t *)overridesv;
+
+    if(base->ssl_enable) {
+        overrides->ssl_enable         = base->ssl_enable;
+        overrides->https_indicator    = base->https_indicator;
+        overrides->certs_indicator    = base->certs_indicator;
+        overrides->cipher_indicator   = base->cipher_indicator;
+        overrides->session_indicator  = base->session_indicator;
+        overrides->key_size_indicator = base->key_size_indicator;
+    }
+    
+    overrides->options = base->options;
+
+    if(overrides->mountcopy) {
+		copy_jk_map(p, overrides->s, base->uri_to_context, overrides->uri_to_context);
+		copy_jk_map(p, overrides->s, base->automount, overrides->automount);
+    }
+
+    if(base->envvars_in_use) {
+        overrides->envvars_in_use = JK_TRUE;
+        overrides->envvars = ap_overlay_tables(p, overrides->envvars, base->envvars);
+    }
+
+    if(overrides->log_file && overrides->log_level >= 0) {
+        if(!jk_open_file_logger(&(overrides->log), overrides->log_file, overrides->log_level)) {
+            overrides->log = NULL;
+        }
+    }
+    if(!uri_worker_map_alloc(&(overrides->uw_map), overrides->uri_to_context, overrides->log)) {
+        jk_error_exit(APLOG_MARK, APLOG_EMERG, overrides->s, p, "Memory error");
+    }
+
+	if (base->secret_key)
+		overrides->secret_key = base->secret_key;
+   
+    return overrides;
+}
+
+static void jk_init(server_rec *s, ap_pool *p)
+{
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+    jk_map_t *init_map = conf->worker_properties;
+
+    /* Open up log file */
+    if(conf->log_file && conf->log_level >= 0) {
+        if(!jk_open_file_logger(&(conf->log), conf->log_file, conf->log_level)) {
+#ifdef CHROOTED_APACHE
+            conf->log = main_log;
+#else
+            conf->log = NULL;
+#endif
+        } else {
+            main_log = conf->log;
+        }
+    }
+
+    /* SREVILAK -- register cleanup handler to clear resources on restart,
+     * to make sure log file gets closed in the parent process  */
+    ap_register_cleanup(p, s, jk_server_cleanup, ap_null_cleanup);
+    
+/*
+{ int i;
+jk_log(conf->log, JK_LOG_DEBUG, "default secret key = %s\n", conf->secret_key);
+for (i = 0; i < map_size(conf->automount); i++)
+{
+            char *name = map_name_at(conf->automount, i);
+			jk_log(conf->log, JK_LOG_DEBUG, "worker = %s and virtualhost = %s\n", name, map_get_string(conf->automount, name, NULL));
+}
+}
+*/
+
+    /* Create mapping from uri's to workers, and start up all the workers */
+    if(!uri_worker_map_alloc(&(conf->uw_map), conf->uri_to_context, conf->log)) {
+        jk_error_exit(APLOG_MARK, APLOG_EMERG, s, p, "Memory error");
+    }
+
+    /*if(map_alloc(&init_map)) {*/
+
+    if(map_read_properties(init_map, conf->worker_file)) {
+
+#if MODULE_MAGIC_NUMBER >= 19980527
+        /* Tell apache we're here */
+        ap_add_version_component(JK_EXPOSED_VERSION);
+#endif
+            
+        /* we add the URI->WORKER MAP since workers using AJP14 will feed it */
+        worker_env.uri_to_worker = conf->uw_map;
+        worker_env.virtual       = "*";     /* for now */
+        worker_env.server_name   = (char *)ap_get_server_version();
+        if(wc_open(init_map, &worker_env, conf->log)) {
+            /* we don't need this any more so free it */
+            return;
+        }            
+    }
+    
+    ap_log_error(APLOG_MARK, APLOG_ERR, NULL,
+                 "Error while opening the workers, jk will not work\n");
+}
+
+/*
+ * Perform uri to worker mapping, and store the name of the relevant worker
+ * in the notes fields of the request_rec object passed in.  This will then
+ * get picked up in jk_handler().
+ */
+static int jk_translate(request_rec *r)
+{    
+    if(!r->proxyreq) {        
+        jk_server_conf_t *conf =
+            (jk_server_conf_t *)ap_get_module_config(r->server->module_config,
+                                                     &jk_module);
+
+        if(conf) {
+            jk_logger_t *l = conf->log ? conf->log : main_log;
+            char *uri = ap_pstrdup(r->pool, r->uri);
+            char *worker = map_uri_to_worker(conf->uw_map, uri, l);
+
+            /* Don't know the worker, ForwardDirectories is set, there is a
+             * previous request for which the handler is JK_HANDLER (as set by
+             * jk_fixups) and the request is for a directory:
+             * --> forward to Tomcat, via default worker */
+            if(!worker && (conf->options & JK_OPT_FWDDIRS) &&
+               r->prev && !strcmp(r->prev->handler,JK_HANDLER) &&
+               r->uri[strlen(r->uri)-1] == '/'){
+
+                /* Nothing here to do but assign the first worker since we
+                 * already tried mapping and it didn't work out */
+                worker=worker_env.first_worker;
+
+                jk_log(l, JK_LOG_DEBUG, "Manual configuration for %s %s\n",
+                       r->uri, worker_env.first_worker);
+            }
+
+            if(worker) {
+                r->handler = ap_pstrdup(r->pool, JK_HANDLER);
+                ap_table_setn(r->notes, JK_WORKER_ID, worker);
+            } else if(conf->alias_dir != NULL) {
+                char *clean_uri = uri;
+                ap_no2slash(clean_uri);
+                /* Automatically map uri to a context static file */
+                jk_log(l, JK_LOG_DEBUG,
+                    "mod_jk::jk_translate, check alias_dir: %s\n",conf->alias_dir);
+                if (strlen(clean_uri) > 1) {
+                    /* Get the context directory name */
+                    char *context_dir = NULL;
+                    char *context_path = NULL;
+                    char *child_dir = NULL;
+                    char *index = clean_uri;
+                    char *suffix = strchr(index+1,'/');
+                    if( suffix != NULL ) {
+                        int size = suffix - index;
+                        context_dir = ap_pstrndup(r->pool,index,size);
+                        /* Get the context child directory name */
+                        index = index + size + 1;
+                        suffix = strchr(index,'/');
+                        if( suffix != NULL ) {
+                            size = suffix - index;
+                            child_dir = ap_pstrndup(r->pool,index,size);
+                        } else {
+                            child_dir = index;
+                        }
+                        /* Deny access to WEB-INF and META-INF directories */
+                        if( child_dir != NULL ) {
+                            jk_log(l, JK_LOG_DEBUG,             
+                                "mod_jk::jk_translate, AutoAlias child_dir: %s\n",
+                                 child_dir);
+                            if( !strcasecmp(child_dir,"WEB-INF") ||
+                                !strcasecmp(child_dir,"META-INF") ) {
+                                jk_log(l, JK_LOG_DEBUG,
+                                    "mod_jk::jk_translate, AutoAlias FORBIDDEN for URI: %s\n",
+                                    r->uri);
+                                return FORBIDDEN;
+                            }
+                        }
+                    } else {
+                        context_dir = ap_pstrdup(r->pool,index);
+                    }
+
+                    context_path = ap_pstrcat(r->pool,conf->alias_dir,
+                                              ap_os_escape_path(r->pool,context_dir,1),
+                                              NULL);
+                    if( context_path != NULL ) {
+                        DIR *dir = ap_popendir(r->pool,context_path);
+                        if( dir != NULL ) {
+                            char *escurl = ap_os_escape_path(r->pool, clean_uri, 1);
+                            char *ret = ap_pstrcat(r->pool,conf->alias_dir,escurl,NULL);
+                            ap_pclosedir(r->pool,dir);
+                            /* Add code to verify real path ap_os_canonical_name */
+                            if( ret != NULL ) {
+                                jk_log(l, JK_LOG_DEBUG,
+                                    "mod_jk::jk_translate, AutoAlias OK for file: %s\n",ret);
+                                r->filename = ret;
+                                return OK;
+                            }
+                        } else {
+                            /* Deny access to war files in web app directory */
+                            int size = strlen(context_dir);
+                            if( size > 4 && !strcasecmp(context_dir+(size-4),".war") ) {
+                                jk_log(l, JK_LOG_DEBUG,
+                                    "mod_jk::jk_translate, AutoAlias FORBIDDEN for URI: %s\n",
+                                    r->uri);
+                                return FORBIDDEN;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    return DECLINED;
+}
+
+/* In case ForwardDirectories option is set, we need to know if all files
+ * mentioned in DirectoryIndex have been exhausted without success. If yes, we
+ * need to let mod_dir know that we want Tomcat to handle the directory
+ */
+static int jk_fixups(request_rec *r)
+{
+    /* This is a sub-request, probably from mod_dir */
+    if(r->main){
+        jk_server_conf_t *conf = (jk_server_conf_t *)
+            ap_get_module_config(r->server->module_config, &jk_module);
+        char *worker = (char *) ap_table_get(r->notes, JK_WORKER_ID);
+
+        /* Only if we have no worker and ForwardDirectories is set */
+        if(!worker && (conf->options & JK_OPT_FWDDIRS)){
+            char *dummy_ptr[1], **names_ptr, *idx;
+            int num_names;
+            dir_config_rec *d = (dir_config_rec *)
+                ap_get_module_config(r->per_dir_config, &dir_module);
+            jk_logger_t *l = conf->log ? conf->log : main_log;
+
+            /* Direct lift from mod_dir */
+            if (d->index_names) {
+                names_ptr = (char **)d->index_names->elts;
+                num_names = d->index_names->nelts;
+            } else {
+                dummy_ptr[0] = DEFAULT_INDEX;
+                names_ptr = dummy_ptr;
+                num_names = 1;
+            }
+
+            /* Where the index file would start within the filename */
+            idx=r->filename + strlen(r->filename) -
+                              strlen(names_ptr[num_names - 1]);
+
+            /* The requested filename has the last index file at the end */
+            if(idx >= r->filename && !strcmp(idx,names_ptr[num_names - 1])){
+                r->uri=r->main->uri;       /* Trick mod_dir with URI */
+                r->finfo.st_mode=S_IFREG;  /* Trick mod_dir with file stat */
+
+                /* We'll be checking for handler in r->prev later on */
+                r->main->handler=ap_pstrdup(r->pool, JK_HANDLER);
+
+                jk_log(l, JK_LOG_DEBUG, "ForwardDirectories on: %s\n", r->uri);
+             }
+        }
+    }
+
+    return DECLINED;
+}
+
+static void exit_handler (server_rec *s, ap_pool *p)
+{
+    /* srevilak - refactor cleanup body to jk_generic_cleanup() */
+    jk_generic_cleanup(s);
+}
+ 
+
+/** srevilak -- registered as a cleanup handler in jk_init */
+static void jk_server_cleanup(void *data) 
+{
+    jk_generic_cleanup((server_rec *) data);
+}
+
+
+/** BEGIN SREVILAK 
+ * body taken from exit_handler()
+ */
+static void jk_generic_cleanup(server_rec *s) 
+{
+
+    server_rec *tmp = s;
+
+	/* loop through all available servers to clean up all configuration
+	 * records we've created
+	 */
+    while (NULL != tmp)
+    {
+        jk_server_conf_t *conf =
+            (jk_server_conf_t *)ap_get_module_config(tmp->module_config, &jk_module);
+
+        if (NULL != conf)
+        {
+            wc_close(conf->log);
+            uri_worker_map_free(&(conf->uw_map), conf->log);
+            map_free(&(conf->uri_to_context));
+            map_free(&(conf->worker_properties));
+            map_free(&(conf->automount));
+            if (conf->log)
+                jk_close_file_logger(&(conf->log));
+        }
+        tmp = tmp->next;
+    }
+}
+/** END SREVILAK **/
+
+
+static const handler_rec jk_handlers[] =
+{
+    { JK_MAGIC_TYPE, jk_handler },
+    { JK_HANDLER, jk_handler },    
+    { NULL }
+};
+
+module MODULE_VAR_EXPORT jk_module = {
+    STANDARD_MODULE_STUFF,
+    jk_init,                    /* module initializer */
+    NULL,                       /* per-directory config creator */
+    NULL,                       /* dir config merger */
+    create_jk_config,           /* server config creator */
+    merge_jk_config,            /* server config merger */
+    jk_cmds,                    /* command table */
+    jk_handlers,                /* [7] list of handlers */
+    jk_translate,               /* [2] filename-to-URI translation */
+    NULL,                       /* [5] check/validate user_id */
+    NULL,                       /* [6] check user_id is valid *here* */
+    NULL,                       /* [4] check access by host address */
+    NULL,                       /* XXX [7] MIME type checker/setter */
+    jk_fixups,                  /* [8] fixups */
+    NULL,                       /* [10] logger */
+    NULL,                       /* [3] header parser */
+    NULL,                       /* apache child process initializer */
+    exit_handler,               /* apache child process exit/cleanup */
+    NULL                        /* [1] post read_request handling */
+#ifdef EAPI
+    /*
+     * Extended module APIs, needed when using SSL.
+     * STDC say that we do not have to have them as NULL but
+     * why take a chance
+     */
+    ,NULL,                      /* add_module */
+    NULL,                       /* remove_module */
+    NULL,                       /* rewrite_command */
+    NULL,                       /* new_connection */
+    NULL                       /* close_connection */
+#endif /* EAPI */
+
+};
diff --git a/connectors/jk/native/apache-1.3/mod_jk.dsp b/connectors/jk/native/apache-1.3/mod_jk.dsp
new file mode 100644
index 0000000..94e55af
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/mod_jk.dsp
@@ -0,0 +1,273 @@
+# Microsoft Developer Studio Project File - Name="mod_jk" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_jk - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_jk.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_jk.mak" CFG="mod_jk - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_jk - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_jk - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "mod_jk - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MOD_JK_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\common" /I "$(APACHE1_HOME)\src\include" /I "$(APACHE1_HOME)\src\os\win32" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MOD_JK_EXPORTS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 ApacheCore.lib wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /libpath:"$(APACHE1_HOME)\src\CoreR" /libpath:"$(APACHE1_HOME)\src\Release"
+
+!ELSEIF  "$(CFG)" == "mod_jk - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MOD_JK_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\common" /I "$(APACHE1_HOME)\src\include" /I "$(APACHE1_HOME)\src\os\win32" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MOD_JK_EXPORTS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 ApacheCore.lib wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept /libpath:"$(APACHE1_HOME)\src\CoreD" /libpath:"$(APACHE1_HOME)\src\Debug"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_jk - Win32 Release"
+# Name "mod_jk - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\common\jk_ajp12_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_connect.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_jni_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_lb_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_msg_buff.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_sockbuf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_uri_worker_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_util.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_jk.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_context.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp_common.c
+# End Source File
+# End Group
+
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\common\jk_ajp12_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_connect.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_global.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_jni_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_lb_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_msg_buff.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_mt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_service.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_sockbuf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_uri_worker_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_util.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker_list.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_context.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp_common.h
+# End Source File
+# End Group
+
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/connectors/jk/native/apache-1.3/mod_jk.exp b/connectors/jk/native/apache-1.3/mod_jk.exp
new file mode 100644
index 0000000..a4b90e5
--- /dev/null
+++ b/connectors/jk/native/apache-1.3/mod_jk.exp
@@ -0,0 +1 @@
+mod_jk.exp
diff --git a/connectors/jk/native/apache-2.0/.cvsignore b/connectors/jk/native/apache-2.0/.cvsignore
new file mode 100644
index 0000000..b5bf28f
--- /dev/null
+++ b/connectors/jk/native/apache-2.0/.cvsignore
@@ -0,0 +1,7 @@
+.libs
+Makefile
+Makefile.apxs
+*.o
+*.lo
+*.la
+*.slo
diff --git a/connectors/jk/native/apache-2.0/Makefile.apxs.in b/connectors/jk/native/apache-2.0/Makefile.apxs.in
new file mode 100644
index 0000000..081d239
--- /dev/null
+++ b/connectors/jk/native/apache-2.0/Makefile.apxs.in
@@ -0,0 +1,28 @@
+## configure should make the Makefile out of this file.
+
+APXS=@APXS@
+OS=@OS@
+JAVA_HOME=@JAVA_HOME@
+APXSLDFLAGS=@APXSLDFLAGS@
+APXSCFLAGS=@APXSCFLAGS@
+
+JK=../common/
+JK_INCL=-DUSE_APACHE_MD5 -I ${JK}
+JAVA_INCL=-I ${JAVA_HOME}/include -I ${JAVA_HOME}/include/${OS}
+JAVA_LIB=-L ${JAVA_HOME}/jre/lib/${ARCH} -L ${JAVA_HOME}/lib/${ARCH}/native_threads
+
+## read the object (.c) from the list file.
+OEXT=.c
+include ../common/list.mk
+
+all: mod_jk.la
+
+mod_jk.la: 
+	$(APXS)  -c -o $@ -Wc,"${APXSCFLAGS} ${JK_INCL}" "${JAVA_INCL}" "${APXSLDFLAGS}" mod_jk.c ${APACHE_OBJECTS} 
+
+install: mod_jk.la
+	$(APXS) -i mod_jk.la
+ 
+clean:
+	rm -f *.o *.so *.lo *.la *.slo
+	rm -rf .libs
diff --git a/connectors/jk/native/apache-2.0/Makefile.in b/connectors/jk/native/apache-2.0/Makefile.in
new file mode 100755
index 0000000..1c8dfc4
--- /dev/null
+++ b/connectors/jk/native/apache-2.0/Makefile.in
@@ -0,0 +1,80 @@
+## 
+
+APXS=@APXS@
+OS=@OS@
+JAVA_HOME=@JAVA_HOME@
+CP=@CP@
+APACHE_DIR=@APACHE_DIR@
+MKDIR=@MKDIR@
+APXSCFLAGS=@APXSCFLAGS@
+APXSCPPFLAGS=@APXSCPPFLAGS@
+
+# Defaults
+libexecdir=${APACHE_DIR}/modules
+
+JK=../common/
+# Defines APACHE_OBJECTS - the list of all common files
+include ../common/list.mk
+
+# Apache2 settings, values guessed by Apache config and used to build it
+# Will define libexecdir, LIBTOOL, etc
+include @APACHE_CONFIG_VARS@
+
+# Local settings ( overriding/appending to Apache's ) 
+COMMON=../common
+JK_INCL=-DUSE_APACHE_MD5 -I ${COMMON} 
+JAVA_INCL=-I ${JAVA_HOME}/include -I ${JAVA_HOME}/include/${OS}
+JAVA_LIB=-L ${JAVA_HOME}/jre/lib/${ARCH} -L ${JAVA_HOME}/lib/${ARCH}/native_threads
+CFLAGS=@apache_include@ @CFLAGS@ ${JK_INCL} ${JAVA_INCL} ${APXSCPPFLAGS} ${APXSCFLAGS} ${EXTRA_CFLAGS} ${EXTRA_CPPFLAGS}
+
+
+# Implicit rules
+include ../scripts/build/rules.mk
+
+OEXT=.lo
+
+all: Makefile @LIB_JK_TYPE@ 
+install: @INSTALL_TYPE@
+
+Makefile: Makefile.in
+	echo Regenerating Makefile
+	( cd ..; ./config.status )
+
+lib_jk.la: mod_jk.lo ${APACHE_OBJECTS}
+	$(LIBTOOL) --mode=link $(CC) -o lib_jk.la -static -rpath ${libexecdir}/jk mod_jk.lo $(APACHE_OBJECTS)
+
+install_static:
+	@echo ""
+	@echo "Copying files to Apache Modules Directory..."
+	-${MKDIR} ${APACHE_DIR}/modules/jk
+	${CP} config.m4 ${APACHE_DIR}/modules/jk
+	${LIBTOOL} --mode=install cp lib_jk.la ${APACHE_DIR}/modules/jk
+	@echo ""
+	@echo "Please be sure to re-compile Apache..."
+	@echo ""
+	@echo "cd ${APACHE_DIR}"
+	@echo "./buildconf"
+	@echo "./configure --with-mod_jk"
+	@echo "make"
+	@echo ""
+
+#################### Dynamic .so file ####################
+# APXS will compile every file, this is derived from apxs
+
+mod_jk.la: mod_jk.lo $(APACHE_OBJECTS)
+	$(LIBTOOL) --mode=link ${COMPILE} -o $@ -module -rpath ${libexecdir} -avoid-version mod_jk.lo $(APACHE_OBJECTS)
+
+mod_jk.so: mod_jk.la
+	$(LIBTOOL) --mode=install cp $< `pwd`/$@
+
+install_dynamic:
+	@echo ""
+	@echo "Installing files to Apache Modules Directory..."
+	$(APXS) -i mod_jk.la
+	@echo ""
+	@echo "Please be sure to arrange ${APACHE_DIR}/conf/httpd.conf..."
+	@echo ""
+
+clean:
+	rm -f *.o *.so *.lo *.la *.slo
+	rm -rf .libs
diff --git a/connectors/jk/native/apache-2.0/NWGNUmakefile b/connectors/jk/native/apache-2.0/NWGNUmakefile
new file mode 100644
index 0000000..ce4f1c3
--- /dev/null
+++ b/connectors/jk/native/apache-2.0/NWGNUmakefile
@@ -0,0 +1,313 @@
+#
+# Makefile for mod_jk (uses the build system of Apache2 - gnu make)
+# created by Guenter Knauf <eflash@gmx.net>
+#
+
+#ifeq ($(strip $(JAVA_HOME)),)
+#@echo You must set the JAVA_HOME environment var pointing to the NetWare Java SDK!
+#endif
+
+#
+# Declare the sub-directories to be built here
+#
+
+SUBDIRS = \
+	$(EOLIST)
+
+#
+# Get the 'head' of the build environment.  This includes default targets and
+# paths to tools
+#
+
+include $(AP_WORK)\build\NWGNUhead.inc
+
+#
+# build this level's files
+
+#
+# Make sure all needed macro's are defined
+#
+
+JKCOMMON = ../common
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS	+= \
+			$(JKCOMMON) \
+			$(AP_WORK)/include \
+			$(NWOS) \
+			$(AP_WORK)/modules/arch/netware \
+			$(AP_WORK)/srclib/apr/include \
+			$(AP_WORK)/srclib/apr-util/include \
+			$(AP_WORK)/srclib/apr \
+			$(JAVA_HOME)/include \
+			$(JAVA_HOME)/include/netware \
+			$(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS		+= \
+			$(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES	+= \
+			-D__NOVELL_LIBC__ \
+			$(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS		+= \
+			$(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+		   	$(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+		   	$(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+			$(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm.  If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME		= mod_jk
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION	= Apache $(VERSION_STR) plugin for Jakarta/Tomcat $(JK_VERSION_STR)
+
+#
+# This is used by the link '-copy ' directive.
+# If left blank, the ASF copyright defined in NWGNUtail.inc will be used.
+#
+NLM_COPYRIGHT	=
+
+#
+# This is used by the '-threadname' directive.  If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME	= JK Module
+
+#
+# If this is specified, it will override VERSION value in
+# $(AP_WORK)\build\NWGNUenvironment.inc
+#
+NLM_VERSION	= $(JK_VERSION)
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE	= 49152
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM	= _LibCPrelude
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM	= _LibCPostlude
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM	=
+
+#
+# If these are specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS		=  AUTOUNLOAD, PSEUDOPREEMPTION
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(NWOS)/apache.xdc.  XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA         =
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+	$(OBJDIR)/mod_jk.nlm \
+	$(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+	$(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+	$(OBJDIR)/jk_nwmain.o \
+	$(OBJDIR)/jk_ajp12_worker.o \
+	$(OBJDIR)/jk_ajp13.o \
+	$(OBJDIR)/jk_ajp13_worker.o \
+	$(OBJDIR)/jk_ajp14.o \
+	$(OBJDIR)/jk_ajp14_worker.o \
+	$(OBJDIR)/jk_ajp_common.o \
+	$(OBJDIR)/jk_connect.o \
+	$(OBJDIR)/jk_context.o \
+	$(OBJDIR)/jk_jni_worker.o \
+	$(OBJDIR)/jk_lb_worker.o \
+	$(OBJDIR)/jk_map.o \
+	$(OBJDIR)/jk_md5.o \
+	$(OBJDIR)/jk_msg_buff.o \
+	$(OBJDIR)/jk_pool.o \
+	$(OBJDIR)/jk_sockbuf.o \
+	$(OBJDIR)/jk_uri_worker_map.o \
+	$(OBJDIR)/jk_util.o \
+	$(OBJDIR)/jk_worker.o \
+	$(OBJDIR)/mod_jk.o \
+	$(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+   	libcpre.o \
+	$(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+	aprlib \
+	libc \
+	$(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+	@$(APR)/aprlib.imp \
+	@$(NWOS)/httpd.imp \
+	@libc.imp \
+	@ws2nlm.imp \
+	$(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+	jk_module \
+	$(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+	$(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place.  (See $(AP_WORK)\build\NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+	copy $(OBJDIR)\*.nlm $(INSTALL)\Apache2\modules\*.*
+
+#
+# Any specialized rules here
+#
+
+vpath %.c $(JKCOMMON)
+
+$(OBJDIR)/version.inc: $(JKCOMMON)/jk_version.h $(OBJDIR)
+	@echo Creating $@
+	@awk -f ../../../common/build/get_ver.awk $< > $@
+
+
+#
+# Include the version info retrieved from jk_version.h
+#
+
+-include $(OBJDIR)/version.inc
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(AP_WORK)\build\NWGNUtail.inc
+
+
diff --git a/connectors/jk/native/apache-2.0/bldjk.qclsrc b/connectors/jk/native/apache-2.0/bldjk.qclsrc
new file mode 100644
index 0000000..f14a544
--- /dev/null
+++ b/connectors/jk/native/apache-2.0/bldjk.qclsrc
@@ -0,0 +1,289 @@
+PGM

+CRTCMOD MODULE(MOD_JK/MOD_JK) +

+	SRCSTMF('/home/apache/jk/native/apache-2.0/mod_jk.c') +

+	DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' + 

+	       'USE_APACHE_MD5' '_REENTRANT') +

+	TEXT('mod_jk.c') +

+	OPTIMIZE(40) +

+	SYSIFCOPT(*IFSIO) +

+	TGTCCSID(*JOB) +

+	OPTION(*LOGMSG) +

+	TERASPACE(*YES *TSIFC) +

+	STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_AJP_COM) +

+    SRCSTMF('/home/apache/jk/native/common/jk_ajp_common.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_ajp_common.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_AJP12_W) +

+    SRCSTMF('/home/apache/jk/native/common/jk_ajp12_worker.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_ajp12_worker.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_AJP13) +

+    SRCSTMF('/home/apache/jk/native/common/jk_ajp13.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +   

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_ajp13.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_AJP13_W) +

+    SRCSTMF('/home/apache/jk/native/common/jk_ajp13_worker.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_ajp13_worker.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_AJP14) +

+    SRCSTMF('/home/apache/jk/native/common/jk_ajp14.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_ajp14.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_AJP14_W) +

+    SRCSTMF('/home/apache/jk/native/common/jk_ajp14_worker.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_ajp14_worker.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_CONNECT) +

+    SRCSTMF('/home/apache/jk/native/common/jk_connect.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_connect.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_CONTEXT) +

+    SRCSTMF('/home/apache/jk/native/common/jk_context.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_context.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_JNI_WOR) +

+    SRCSTMF('/home/apache/jk/native/common/jk_jni_worker.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'OS400_JVM_12' + 

+           '_XOPEN_SOURCE=520' +  'USE_APACHE_MD5' +

+    '_REENTRANT') +

+    TEXT('jk_jni_worker.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG ) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_LB_WORK) +

+    SRCSTMF('/home/apache/jk/native/common/jk_lb_worker.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_lb_worker.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_MAP) +

+    SRCSTMF('/home/apache/jk/native/common/jk_map.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_map.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_MD5) +

+    SRCSTMF('/home/apache/jk/native/common/jk_md5.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_md5.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_MSG_BUF) +

+    SRCSTMF('/home/apache/jk/native/common/jk_msg_buff.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_msg_buff.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_POOL) +

+    SRCSTMF('/home/apache/jk/native/common/jk_pool.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_pool.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_SOCKBUF) +

+    SRCSTMF('/home/apache/jk/native/common/jk_sockbuf.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_sockbuf.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_URI_W_M) +

+    SRCSTMF('/home/apache/jk/native/common/jk_uri_worker_map.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_uri_worker_map.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_UTIL) +

+    SRCSTMF('/home/apache/jk/native/common/jk_util.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_util.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK/JK_WORKER) +

+    SRCSTMF('/home/apache/jk/native/common/jk_worker.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_worker.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native/common' '/QIBM/ProdData/HTTPA/Include')

+

+CRTSRVPGM SRVPGM(MOD_JK/MOD_JK) +

+      MODULE(MOD_JK/MOD_JK +

+             MOD_JK/JK_AJP_COM MOD_JK/JK_AJP12_W +

+             MOD_JK/JK_AJP13 MOD_JK/JK_AJP13_W +

+             MOD_JK/JK_AJP14 MOD_JK/JK_AJP14_W +

+             MOD_JK/JK_CONNECT MOD_JK/JK_CONTEXT +

+             MOD_JK/JK_JNI_WOR MOD_JK/JK_LB_WORK +

+             MOD_JK/JK_MAP MOD_JK/JK_MD5 +

+             MOD_JK/JK_MSG_BUF MOD_JK/JK_POOL +

+             MOD_JK/JK_SOCKBUF MOD_JK/JK_URI_W_M +

+             MOD_JK/JK_UTIL MOD_JK/JK_WORKER) +

+      EXPORT(*SRCFILE) +

+      BNDDIR() +

+      SRCFILE(MOD_JK/QSRVSRC) +

+      SRCMBR(MOD_JK) +

+      DETAIL(*BASIC) +

+      STGMDL(*INHERIT) +

+      BNDSRVPGM(QHTTPSVR/QZSRAPR QHTTPSVR/QZSRCORE +

+                QHTTPSVR/QZSRXMLP QHTTPSVR/QZSRSDBM) +

+      TEXT('Apache mod_jk tomcat connector module')

+

+ENDPGM

diff --git a/connectors/jk/native/apache-2.0/config.m4 b/connectors/jk/native/apache-2.0/config.m4
new file mode 100644
index 0000000..111fb50
--- /dev/null
+++ b/connectors/jk/native/apache-2.0/config.m4
@@ -0,0 +1,18 @@
+AC_MSG_CHECKING(for mod_jk module)
+AC_ARG_WITH(mod_jk,
+  [  --with-mod_jk  Include the mod_jk (static).],
+  [
+    modpath_current=modules/jk
+    module=jk
+    libname=lib_jk.la
+    BUILTIN_LIBS="$BUILTIN_LIBS $modpath_current/$libname"
+    cat >>$modpath_current/modules.mk<<EOF
+DISTCLEAN_TARGETS = modules.mk
+static = $libname
+shared =
+EOF
+  MODLIST="$MODLIST $module"
+  AC_MSG_RESULT(added $withval)
+  ],
+  [ AC_MSG_RESULT(no mod_jk added) 
+  ])
diff --git a/connectors/jk/native/apache-2.0/mod_jk.c b/connectors/jk/native/apache-2.0/mod_jk.c
new file mode 100644
index 0000000..733f052
--- /dev/null
+++ b/connectors/jk/native/apache-2.0/mod_jk.c
@@ -0,0 +1,2487 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Apache 2 plugin for Jakarta/Tomcat                         *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ *              Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+/*
+ * mod_jk: keeps all servlet/jakarta related ramblings together.
+ */
+
+#include "ap_config.h"
+#include "apr_lib.h"
+#include "apr_date.h"
+#include "apr_file_info.h"
+#include "apr_file_io.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "util_script.h"
+
+#ifdef AS400
+#include "ap_charset.h"
+#include "util_charset.h"              /* ap_hdrs_from_ascii */
+#endif
+
+/* moved to apr since http-2.0.19-dev */
+#if (MODULE_MAGIC_NUMBER_MAJOR < 20010523)
+#define apr_date_parse_http ap_parseHTTPdate
+#include "util_date.h"
+#endif
+
+/* deprecated with apr 0.9.3 */
+
+/* 
+   The latest Apache 2.0.47 for iSeries didn't export apr_filepath_name_get
+   but apr_filename_of_pathname, even if includes seems right and the APR
+   in use is 0.9.4
+*/
+
+#include "apr_version.h"
+#if (APR_MAJOR_VERSION == 0) && \
+    (APR_MINOR_VERSION <= 9) && \
+    (APR_PATCH_VERSION < 3) || defined(AS400) 
+#define apr_filepath_name_get apr_filename_of_pathname
+#endif
+
+#include "apr_strings.h"
+/*
+ * Jakarta (jk_) include files
+ */
+#ifdef NETWARE
+#define __sys_types_h__
+#define __sys_socket_h__
+#define __netdb_h__
+#define __netinet_in_h__
+#define __arpa_inet_h__
+#define __sys_timeval_h__
+#endif
+
+#include "jk_ajp13.h"
+#include "jk_global.h"
+#include "jk_logger.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_service.h"
+#include "jk_uri_worker_map.h"
+#include "jk_util.h"
+#include "jk_worker.h"
+
+#define JK_WORKER_ID        ("jakarta.worker")
+#define JK_HANDLER          ("jakarta-servlet")
+#define JK_DURATION         ("jakarta.worker.duration")
+#define JK_MAGIC_TYPE       ("application/x-jakarta-servlet")
+#define NULL_FOR_EMPTY(x)   ((x && !strlen(x)) ? NULL : x) 
+
+/*
+ * If you are not using SSL, comment out the following line. It will make
+ * apache run faster.  
+ *
+ * Personally, I (DM), think this may be a lie.
+ */
+#define ADD_SSL_INFO    
+
+/* module MODULE_VAR_EXPORT jk_module; */
+AP_MODULE_DECLARE_DATA module jk_module;
+
+typedef struct {
+
+    /*
+     * Log stuff
+     */
+    char *log_file;
+    int  log_level;
+    jk_logger_t *log;
+    apr_file_t *jklogfp;
+
+    /*
+     * Worker stuff
+     */
+    jk_map_t *worker_properties;
+    char     *worker_file;
+    jk_map_t *uri_to_context;
+
+    int      mountcopy;
+    char     *secret_key;
+    jk_map_t *automount;
+
+    jk_uri_worker_map_t *uw_map;
+
+    int was_initialized;
+
+    /*
+     * Automatic context path apache alias
+     */
+    char *alias_dir;
+
+    /*
+     * Request Logging
+     */
+
+    char *format_string;
+    apr_array_header_t *format;
+
+    /*
+     * SSL Support
+     */
+    int  ssl_enable;
+    char *https_indicator;
+    char *certs_indicator;
+    char *cipher_indicator;
+    char *session_indicator;  /* Servlet API 2.3 requirement */
+    char *key_size_indicator; /* Servlet API 2.3 requirement */
+
+    /*
+     * Jk Options
+     */
+    int options;
+
+    /*
+     * Environment variables support
+     */
+    int envvars_in_use;
+    apr_table_t * envvars;
+
+    server_rec *s;
+} jk_server_conf_t;
+
+struct apache_private_data {
+    jk_pool_t p;
+    
+    int response_started;
+    int read_body_started;
+    request_rec *r;
+};
+typedef struct apache_private_data apache_private_data_t;
+
+static jk_logger_t *     main_log = NULL;
+static jk_worker_env_t   worker_env;
+static apr_global_mutex_t *jk_log_lock = NULL;
+
+static int JK_METHOD ws_start_response(jk_ws_service_t *s,
+                                       int status,
+                                       const char *reason,
+                                       const char * const *header_names,
+                                       const char * const *header_values,
+                                       unsigned num_of_headers);
+
+static int JK_METHOD ws_read(jk_ws_service_t *s,
+                             void *b,
+                             unsigned len,
+                             unsigned *actually_read);
+
+static void init_jk( apr_pool_t *pconf,jk_server_conf_t *conf, server_rec *s );
+
+static int JK_METHOD ws_write(jk_ws_service_t *s,
+                              const void *b,
+                              unsigned l);
+
+
+/* ========================================================================= */
+/* JK Service step callbacks                                                 */
+/* ========================================================================= */
+
+static int JK_METHOD ws_start_response(jk_ws_service_t *s,
+                                       int status,
+                                       const char *reason,
+                                       const char * const *header_names,
+                                       const char * const *header_values,
+                                       unsigned num_of_headers)
+{
+    unsigned h;
+    apache_private_data_t *p = s->ws_private;
+    request_rec *r = p->r;
+    
+    if(!reason) {
+        reason = "";
+    }
+    r->status = status;
+    r->status_line = apr_psprintf(r->pool, "%d %s", status, reason);
+
+    for(h = 0 ; h < num_of_headers ; h++) {
+        if(!strcasecmp(header_names[h], "Content-type")) {
+            char *tmp = apr_pstrdup(r->pool, header_values[h]);
+            ap_content_type_tolower(tmp);
+            /* It should be done like this in Apache 2.0 */
+            /* This way, Apache 2.0 will be able to set the output filter */
+            /* and it make jk useable with deflate using */
+            /* AddOutputFilterByType DEFLATE text/html */
+            ap_set_content_type(r, tmp);
+        } else if(!strcasecmp(header_names[h], "Location")) {
+#ifdef AS400 
+            /* Fix escapes in Location Header URL*/
+            ap_fixup_escapes((char *)header_values[h], 
+            strlen(header_values[h]), ap_hdrs_from_ascii);
+#endif 
+            apr_table_set(r->headers_out, 
+                          header_names[h], header_values[h]);
+        } else if(!strcasecmp(header_names[h], "Content-Length")) {
+            apr_table_set(r->headers_out, 
+                          header_names[h], header_values[h]);
+        } else if(!strcasecmp(header_names[h], "Transfer-Encoding")) {
+            apr_table_set(r->headers_out, 
+                          header_names[h], header_values[h]);
+        } else if(!strcasecmp(header_names[h], "Last-Modified")) {
+            /*
+             * If the script gave us a Last-Modified header, we can't just
+             * pass it on blindly because of restrictions on future values.
+             */
+            ap_update_mtime(r, apr_date_parse_http(header_values[h]));
+            ap_set_last_modified(r);
+        } else {                
+            apr_table_add(r->headers_out, 
+                          header_names[h], header_values[h]);
+        }
+    }
+
+    /* this NOP function was removed in apache 2.0 alpha14 */
+    /* ap_send_http_header(r); */
+    p->response_started = JK_TRUE;
+    
+    return JK_TRUE;
+}
+
+/*
+ * Read a chunk of the request body into a buffer.  Attempt to read len
+ * bytes into the buffer.  Write the number of bytes actually read into
+ * actually_read.
+ *
+ * Think of this function as a method of the apache1.3-specific subclass of
+ * the jk_ws_service class.  Think of the *s param as a "this" or "self"
+ * pointer.
+ */
+static int JK_METHOD ws_read(jk_ws_service_t *s,
+                             void *b,
+                             unsigned len,
+                             unsigned *actually_read)
+{
+    if(s && s->ws_private && b && actually_read) {
+        apache_private_data_t *p = s->ws_private;
+        if(!p->read_body_started) {
+           if(ap_should_client_block(p->r)) {
+                p->read_body_started = JK_TRUE;
+            }
+        }
+
+        if(p->read_body_started) {
+#ifdef AS400
+    int long rv = OK;
+    if (rv = ap_change_request_body_xlate(p->r, 65535, 65535)) /* turn off request body translation*/
+    {        
+        ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+                     "mod_jk: Error on ap_change_request_body_xlate, rc=%d \n",
+                     rv);
+        return JK_FALSE;
+    }
+#else
+    long rv;
+#endif
+
+            if ((rv = ap_get_client_block(p->r, b, len)) < 0) {
+                *actually_read = 0;
+            } else {
+                *actually_read = (unsigned) rv;
+            }
+            return JK_TRUE;
+        }
+    }
+    return JK_FALSE;
+}
+
+/*
+ * Write a chunk of response data back to the browser.  If the headers
+ * haven't yet been sent over, send over default header values (Status =
+ * 200, basically).
+ *
+ * Write len bytes from buffer b.
+ *
+ * Think of this function as a method of the apache1.3-specific subclass of
+ * the jk_ws_service class.  Think of the *s param as a "this" or "self"
+ * pointer.
+ */
+/* Works with 4096, fails with 8192 */
+#ifndef CHUNK_SIZE
+#define CHUNK_SIZE 4096
+#endif
+
+static int JK_METHOD ws_write(jk_ws_service_t *s,
+                              const void *b,
+                              unsigned l)
+{
+#ifdef AS400
+    int rc;
+#endif
+
+    if(s && s->ws_private && b) {
+        apache_private_data_t *p = s->ws_private;
+
+        if(l) {
+            /* BUFF *bf = p->r->connection->client; */
+            size_t w = (size_t)l;
+            size_t r = 0;
+            long ll=l;
+            char *bb=(char *)b;
+            
+            if(!p->response_started) {
+                jk_log(main_log, JK_LOG_DEBUG, 
+                       "Write without start, starting with defaults\n");
+                if(!s->start_response(s, 200, NULL, NULL, NULL, 0)) {
+                    return JK_FALSE;
+                }
+            }
+            if (p->r->header_only) {
+#ifndef AS400
+                ap_rflush(p->r);
+#endif
+                return JK_TRUE;
+            }
+#ifdef AS400
+            rc = ap_change_response_body_xlate(p->r, 65535, 65535); /* turn off response body translation*/
+        if(rc){
+                ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+                             "mod_jk: Error on ap_change_response_body_xlate, rc=%d \n", rc);
+                 return JK_FALSE;
+            }
+#endif
+
+            /* Debug - try to get around rwrite */
+            while( ll > 0 ) {
+                size_t toSend=(ll>CHUNK_SIZE) ? CHUNK_SIZE : ll;
+                r = ap_rwrite((const char *)bb, toSend, p->r );
+                jk_log(main_log, JK_LOG_DEBUG, 
+                       "writing %ld (%ld) out of %ld \n",toSend, r, ll );
+                ll-=CHUNK_SIZE;
+                bb+=CHUNK_SIZE;
+                
+                if(toSend != r) { 
+                    return JK_FALSE; 
+                } 
+                
+            }
+
+            /*
+             * To allow server push. After writing full buffers
+             */
+#ifndef AS400
+            if(ap_rflush(p->r) != APR_SUCCESS) {
+                ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, 
+                             NULL, "mod_jk: Error flushing \n"  );
+                return JK_FALSE;
+            }
+#endif
+
+        }
+        
+        return JK_TRUE;
+    }
+    return JK_FALSE;
+}
+
+/* ========================================================================= */
+/* Utility functions                                                         */
+/* ========================================================================= */
+
+/* ========================================================================= */
+/* Log something to Jk log file then exit */
+static void jk_error_exit(const char *file, 
+                          int line, 
+                          int level, 
+                          const server_rec *s, 
+                          apr_pool_t *p, 
+                          const char *fmt, ...)
+{
+    va_list ap;
+    char *res;
+
+    va_start(ap, fmt);
+    res = apr_pvsprintf(s->process->pool, fmt, ap);
+    va_end(ap);
+
+    ap_log_error(file, line, level, 0, s, res);
+
+    /* Exit process */
+    exit(1);
+}
+
+static int get_content_length(request_rec *r)
+{
+    if(r->clength > 0) {
+        return (int)r->clength;
+    } else if(r->main == NULL || r->main == r) {
+        char *lenp = (char *)apr_table_get(r->headers_in, "Content-Length");
+
+        if(lenp) {
+            int rc = atoi(lenp);
+            if(rc > 0) {
+                return rc;
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int init_ws_service(apache_private_data_t *private_data,
+                           jk_ws_service_t *s,
+                           jk_server_conf_t *conf)
+{
+    request_rec *r      = private_data->r;
+
+    char *ssl_temp      = NULL;
+    s->jvm_route        = NULL;    /* Used for sticky session routing */
+
+    /* Copy in function pointers (which are really methods) */
+    s->start_response   = ws_start_response;
+    s->read             = ws_read;
+    s->write            = ws_write;
+
+    /* Clear RECO status */
+    s->reco_status  = RECO_NONE;
+
+    s->auth_type    = NULL_FOR_EMPTY(r->ap_auth_type);
+    s->remote_user  = NULL_FOR_EMPTY(r->user);
+
+    s->protocol     = r->protocol;
+    s->remote_host  = (char *)ap_get_remote_host(r->connection, 
+                                                 r->per_dir_config, 
+                                                 REMOTE_HOST, NULL);
+    s->remote_host  = NULL_FOR_EMPTY(s->remote_host);
+    s->remote_addr  = NULL_FOR_EMPTY(r->connection->remote_ip);
+
+    jk_log(conf->log, JK_LOG_DEBUG, 
+           "agsp=%u agsn=%s hostn=%s shostn=%s cbsport=%d sport=%d \n",
+           ap_get_server_port(r),
+           ap_get_server_name(r) != NULL ? ap_get_server_name(r) : "",
+           r->hostname != NULL ? r->hostname : "",
+           r->server->server_hostname != NULL ? r->server->server_hostname : "",
+           r->connection->base_server->port,
+           r->server->port
+           );
+
+    /* get server name */
+    /* s->server_name= (char *)(r->hostname ? r->hostname : r->server->server_hostname); */
+    /* XXX : à la jk2 */
+    s->server_name  = (char *)ap_get_server_name(r);
+
+    /* get the real port (otherwise redirect failed) */
+    s->server_port = r->connection->local_addr->port;
+    /* XXX : à la jk2 ???*/
+    /* s->server_port  = ap_get_server_port(r); */
+
+    s->server_software = (char *)ap_get_server_version();
+
+    s->method         = (char *)r->method;
+    s->content_length = get_content_length(r);
+    s->is_chunked     = r->read_chunked;
+    s->no_more_chunks = 0;
+#ifdef AS400
+    /* Get the query string that is not translated to EBCDIC  */
+    s->query_string   = ap_get_original_query_string(r); 
+#else
+    s->query_string   = r->args;
+#endif
+
+    /*
+     * The 2.2 servlet spec errata says the uri from
+     * HttpServletRequest.getRequestURI() should remain encoded.
+     * [http://java.sun.com/products/servlet/errata_042700.html]
+     *
+     * We use JkOptions to determine which method to be used
+     *
+     * ap_escape_uri is the latest recommanded but require
+     *               some java decoding (in TC 3.3 rc2)
+     *
+     * unparsed_uri is used for strict compliance with spec and
+     *              old Tomcat (3.2.3 for example)
+     *
+     * uri is use for compatibilty with mod_rewrite with old Tomcats
+     */
+
+    switch (conf->options & JK_OPT_FWDURIMASK) {
+
+        case JK_OPT_FWDURICOMPATUNPARSED :
+            s->req_uri      = r->unparsed_uri;
+            if (s->req_uri != NULL) {
+                char *query_str = strchr(s->req_uri, '?');
+                if (query_str != NULL) {
+                    *query_str = 0;
+                }
+            }
+
+        break;
+
+        case JK_OPT_FWDURICOMPAT :
+            s->req_uri = r->uri;
+        break;
+
+        case JK_OPT_FWDURIESCAPED :
+            s->req_uri      = ap_escape_uri(r->pool, r->uri);
+        break;
+
+        default :
+            return JK_FALSE;
+    }
+
+    s->is_ssl       = JK_FALSE;
+    s->ssl_cert     = NULL;
+    s->ssl_cert_len = 0;
+    s->ssl_cipher   = NULL;        /* required by Servlet 2.3 Api, 
+                                   allready in original ajp13 */
+    s->ssl_session  = NULL;
+    s->ssl_key_size = -1;        /* required by Servlet 2.3 Api, added in jtc */
+
+    if(conf->ssl_enable || conf->envvars_in_use) {
+        ap_add_common_vars(r);
+
+        if(conf->ssl_enable) {
+            ssl_temp = 
+                (char *)apr_table_get(r->subprocess_env, 
+                                      conf->https_indicator);
+            if(ssl_temp && !strcasecmp(ssl_temp, "on")) {
+                s->is_ssl       = JK_TRUE;
+                s->ssl_cert     = 
+                    (char *)apr_table_get(r->subprocess_env, 
+                                          conf->certs_indicator);
+                if(s->ssl_cert) {
+                    s->ssl_cert_len = strlen(s->ssl_cert);
+                }
+                /* Servlet 2.3 API */
+                s->ssl_cipher   = 
+                    (char *)apr_table_get(r->subprocess_env, 
+                                          conf->cipher_indicator);
+                s->ssl_session  = 
+                    (char *)apr_table_get(r->subprocess_env, 
+                                          conf->session_indicator);
+
+                if (conf->options & JK_OPT_FWDKEYSIZE) {
+                    /* Servlet 2.3 API */
+                    ssl_temp = (char *)apr_table_get(r->subprocess_env, 
+                                                 conf->key_size_indicator);
+                    if (ssl_temp)
+                        s->ssl_key_size = atoi(ssl_temp);
+                }
+            }
+        }
+
+        if(conf->envvars_in_use) {
+            const apr_array_header_t *t = apr_table_elts(conf->envvars);
+            if(t && t->nelts) {
+                int i;
+                apr_table_entry_t *elts = (apr_table_entry_t *)t->elts;
+                s->attributes_names = apr_palloc(r->pool, 
+                                                 sizeof(char *) * t->nelts);
+                s->attributes_values = apr_palloc(r->pool, 
+                                                  sizeof(char *) * t->nelts);
+
+                for(i = 0 ; i < t->nelts ; i++) {
+                    s->attributes_names[i] = elts[i].key;
+                    s->attributes_values[i] = 
+                        (char *)apr_table_get(r->subprocess_env, elts[i].key);
+                    if(!s->attributes_values[i]) {
+                        s->attributes_values[i] = elts[i].val;
+                    }
+                }
+
+                s->num_attributes = t->nelts;
+            }
+        }
+    }
+
+    s->headers_names    = NULL;
+    s->headers_values   = NULL;
+    s->num_headers      = 0;
+    if(r->headers_in && apr_table_elts(r->headers_in)) {
+        int need_content_length_header = (!s->is_chunked && s->content_length == 0) ? JK_TRUE : JK_FALSE;
+        const apr_array_header_t *t = apr_table_elts(r->headers_in);
+        if(t && t->nelts) {
+            int i;
+            apr_table_entry_t *elts = (apr_table_entry_t *)t->elts;
+            s->num_headers = t->nelts;
+            /* allocate an extra header slot in case we need to add a content-length header */
+            s->headers_names  = apr_palloc(r->pool, sizeof(char *) * (t->nelts + 1));
+            s->headers_values = apr_palloc(r->pool, sizeof(char *) * (t->nelts + 1));
+            if(!s->headers_names || !s->headers_values)
+                return JK_FALSE;
+            for(i = 0 ; i < t->nelts ; i++) {
+                char *hname = apr_pstrdup(r->pool, elts[i].key);
+                s->headers_values[i] = apr_pstrdup(r->pool, elts[i].val);
+                s->headers_names[i] = hname;
+                while(*hname) {
+                    *hname = tolower(*hname);
+                    hname++;
+                }
+                if(need_content_length_header &&
+                        !strncmp(s->headers_values[i],"content-length",14)) {
+                    need_content_length_header = JK_FALSE;
+                }
+            }
+            /* Add a content-length = 0 header if needed.
+             * Ajp13 assumes an absent content-length header means an unknown,
+             * but non-zero length body.
+             */
+            if(need_content_length_header) {
+                s->headers_names[s->num_headers] = "content-length";
+                s->headers_values[s->num_headers] = "0";
+                s->num_headers++;
+            }
+        }
+        /* Add a content-length = 0 header if needed.*/
+        else if (need_content_length_header) {
+            s->headers_names  = apr_palloc(r->pool, sizeof(char *));
+            s->headers_values = apr_palloc(r->pool, sizeof(char *));
+            if(!s->headers_names || !s->headers_values)
+                return JK_FALSE;
+            s->headers_names[0] = "content-length";
+            s->headers_values[0] = "0";
+            s->num_headers++;
+        }
+    }
+
+    return JK_TRUE;
+}
+
+/*
+ * The JK module command processors
+ *
+ * The below are all installed so that Apache calls them while it is
+ * processing its config files.  This allows configuration info to be
+ * copied into a jk_server_conf_t object, which is then used for request
+ * filtering/processing.
+ *
+ * See jk_cmds definition below for explanations of these options.
+ */
+
+/*
+ * JkMountCopy directive handling
+ *
+ * JkMountCopy On/Off
+ */
+
+static const char *jk_set_mountcopy(cmd_parms *cmd, 
+                                    void *dummy, 
+                                    int flag) 
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+    
+    /* Set up our value */
+    conf->mountcopy = flag ? JK_TRUE : JK_FALSE;
+
+    return NULL;
+}
+
+/*
+ * JkMount directive handling
+ *
+ * JkMount URI(context) worker
+ */
+
+static const char *jk_mount_context(cmd_parms *cmd, 
+                                    void *dummy, 
+                                    const char *context,
+                                    const char *worker,
+                                    const char *maybe_cookie)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+    char *old;
+    if (context[0]!='/')
+        return "Context should start with /";
+
+    /*
+     * Add the new worker to the alias map.
+     */
+    map_put(conf->uri_to_context, context, worker, (void **)&old);
+    return NULL;
+}
+
+
+/*
+ * JkAutoMount directive handling
+ *
+ * JkAutoMount worker [virtualhost]
+ * This is an experimental and undocumented extension made in j-t-c/jk.
+ */
+static const char *jk_automount_context(cmd_parms *cmd,
+                                        void *dummy,
+                                        const char *worker,
+                                        const char *virtualhost)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    /*
+     * Add the new automount to the auto map.
+     */
+    char * old;
+    map_put(conf->automount, worker, virtualhost, (void **)&old);
+    return NULL;
+}
+
+/*
+ * JkWorkersFile Directive Handling
+ *
+ * JkWorkersFile file
+ */
+
+static const char *jk_set_worker_file(cmd_parms *cmd, 
+                                      void *dummy, 
+                                      const char *worker_file)
+{
+    server_rec *s = cmd->server;
+    struct stat statbuf;
+
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    /* we need an absolut path (ap_server_root_relative does the ap_pstrdup) */
+    conf->worker_file = ap_server_root_relative(cmd->pool,worker_file);
+ 
+    if (conf->worker_file == NULL)
+        return "JkWorkersFile file_name invalid";
+
+    if (stat(conf->worker_file, &statbuf) == -1)
+        return "Can't find the workers file specified";
+
+    return NULL;
+}
+
+/*
+ * JkWorker name value
+ * This is an experimental and undocumented extension made in j-t-c/jk.
+ */
+static const char *jk_worker_property(cmd_parms *cmd,
+                                      void *dummy,
+                                      const char *name,
+                                      const char *value)
+{
+    server_rec *s = cmd->server;
+    char *oldv;
+
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+    
+    jk_map_t *m=conf->worker_properties;
+    
+    value = map_replace_properties(value, m );
+
+    oldv = map_get_string(m, name, NULL);
+
+    if(oldv) {
+        char *tmpv = apr_palloc(cmd->pool,
+                                strlen(value) + strlen(oldv) + 3);
+        if(tmpv) {
+            char sep = '*';
+            if(jk_is_path_poperty(name)) {
+                sep = PATH_SEPERATOR;
+            } else if(jk_is_cmd_line_poperty(name)) {
+                sep = ' ';
+            }
+            
+            sprintf(tmpv, "%s%c%s", 
+                    oldv, sep, value);
+        }                                
+        value = tmpv;
+    } else {
+        value = apr_pstrdup(cmd->pool, value);
+    }
+    
+    if(value) {
+        void *old = NULL;
+        map_put(m, name, value, &old);
+        /*printf("Setting %s %s\n", name, value);*/
+    } 
+    return NULL;
+}
+
+
+/*
+ * JkLogFile Directive Handling
+ *
+ * JkLogFile file
+ */
+
+static const char *jk_set_log_file(cmd_parms *cmd, 
+                                   void *dummy, 
+                                   const char *log_file)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    /* we need an absolute path */
+    if (*log_file != '|' )
+        conf->log_file = ap_server_root_relative(cmd->pool,log_file);
+    else
+        conf->log_file = apr_pstrdup(cmd->pool, log_file);
+
+    if (conf->log_file == NULL)
+        return "JkLogFile file_name invalid";
+
+    return NULL;
+}
+
+/*
+ * JkLogLevel Directive Handling
+ *
+ * JkLogLevel debug/info/error/emerg
+ */
+
+static const char *jk_set_log_level(cmd_parms *cmd, 
+                                    void *dummy, 
+                                    const char *log_level)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->log_level = jk_parse_log_level(log_level);
+
+    return NULL;
+}
+
+/*
+ * JkLogStampFormat Directive Handling
+ *
+ * JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
+ */
+
+static const char * jk_set_log_fmt(cmd_parms *cmd,
+                      void *dummy,
+                      const char * log_format)
+{
+    jk_set_log_format(log_format);
+    return NULL;
+}
+
+
+/*
+ * JkAutoAlias Directive Handling
+ *
+ * JkAutoAlias application directory
+ */
+
+static const char *jk_set_auto_alias(cmd_parms *cmd,
+                                    void *dummy,
+                                    char *directory)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->alias_dir = directory;
+
+    if (conf->alias_dir == NULL)
+        return "JkAutoAlias directory invalid";
+
+    return NULL;
+}
+
+/*****************************************************************
+ *
+ * Actually logging.
+ */
+
+typedef const char *(*item_key_func) (request_rec *, char *);
+                                                             
+typedef struct {                                             
+    item_key_func func;
+    char *arg;         
+} request_log_format_item;
+
+static const char *process_item(request_rec *r,
+                          request_log_format_item *item)
+{
+    const char *cp;
+
+    cp = (*item->func) (r,item->arg);
+    return cp ? cp : "-";
+}
+
+static void request_log_transaction(request_rec *r,
+                                    jk_server_conf_t *conf)
+{
+    request_log_format_item *items;
+    char *str, *s;
+    int i;
+    int len = 0;
+    const char **strs;
+    int *strl;
+    apr_array_header_t *format = conf->format;
+
+    strs = apr_palloc(r->pool, sizeof(char *) * (format->nelts));
+    strl = apr_palloc(r->pool, sizeof(int) * (format->nelts));
+    items = (request_log_format_item *) format->elts;
+    for (i = 0; i < format->nelts; ++i) {
+        strs[i] = process_item(r, &items[i]);
+    }
+    for (i = 0; i < format->nelts; ++i) {
+        len += strl[i] = strlen(strs[i]);
+    }
+    str = apr_palloc(r->pool, len + 1);
+    for (i = 0, s = str; i < format->nelts; ++i) {
+        memcpy(s, strs[i], strl[i]);
+        s += strl[i];
+    }
+    *s = 0;
+    
+    jk_log(conf->log, JK_LOG_REQUEST, "%s", str);
+    
+}
+
+/*****************************************************************
+ *
+ * Parsing the log format string
+ */
+
+static char *format_integer(apr_pool_t *p, int i)
+{
+    return apr_psprintf(p, "%d", i);
+}
+ 
+static char *pfmt(apr_pool_t *p, int i)
+{
+    if (i <= 0) {
+        return "-";
+    }
+    else {
+        return format_integer(p, i);
+    }
+}
+
+static const char *constant_item(request_rec *dummy, char *stuff)
+{
+    return stuff;
+}
+
+static const char *log_worker_name(request_rec *r, char *a)
+{
+    return apr_table_get(r->notes, JK_WORKER_ID);
+}
+
+
+static const char *log_request_duration(request_rec *r, char *a)
+{
+    return apr_table_get(r->notes, JK_DURATION);
+}
+ 
+static const char *log_request_line(request_rec *r, char *a)
+{
+            /* NOTE: If the original request contained a password, we
+             * re-write the request line here to contain XXXXXX instead:
+             * (note the truncation before the protocol string for HTTP/0.9 requests)
+             * (note also that r->the_request contains the unmodified request)
+             */
+    return (r->parsed_uri.password) ? apr_pstrcat(r->pool, r->method, " ",
+                                         apr_uri_unparse(r->pool, &r->parsed_uri, 0),
+                                         r->assbackwards ? NULL : " ", r->protocol, NULL)
+                                        : r->the_request;
+}
+
+/* These next two routines use the canonical name:port so that log
+ * parsers don't need to duplicate all the vhost parsing crud.
+ */
+static const char *log_virtual_host(request_rec *r, char *a)
+{
+    return r->server->server_hostname;
+}
+ 
+static const char *log_server_port(request_rec *r, char *a)
+{
+    return apr_psprintf(r->pool, "%u",
+        r->server->port ? r->server->port : ap_default_port(r));
+}
+ 
+/* This respects the setting of UseCanonicalName so that
+ * the dynamic mass virtual hosting trick works better.
+ */
+static const char *log_server_name(request_rec *r, char *a)
+{
+    return ap_get_server_name(r);
+}
+
+static const char *log_request_uri(request_rec *r, char *a)
+{
+    return r->uri;
+}
+static const char *log_request_method(request_rec *r, char *a)
+{
+    return r->method;
+}
+
+static const char *log_request_protocol(request_rec *r, char *a)
+{
+    return r->protocol;
+}
+static const char *log_request_query(request_rec *r, char *a)
+{
+    return (r->args != NULL) ? apr_pstrcat(r->pool, "?", r->args, NULL)
+                             : "";
+}                  
+static const char *log_status(request_rec *r, char *a)
+{                  
+    return pfmt(r->pool,r->status);
+}                  
+                   
+static const char *clf_log_bytes_sent(request_rec *r, char *a)
+{                  
+    if (!r->sent_bodyct) {
+        return "-";
+    }              
+    else {        
+    return apr_off_t_toa(r->pool, r->bytes_sent);
+    }              
+}                  
+    
+static const char *log_bytes_sent(request_rec *r, char *a)
+{                  
+    if (!r->sent_bodyct) {
+        return "0";
+    }              
+    else {        
+    return apr_psprintf(r->pool, "%" APR_OFF_T_FMT, r->bytes_sent);
+    }              
+}
+
+static struct log_item_list {
+    char ch;
+    item_key_func func;
+} log_item_keys[] = {
+
+    {
+        'T', log_request_duration
+    },
+    {
+        'r', log_request_line
+    },
+    {
+        'U', log_request_uri
+    },
+    {
+        's', log_status
+    },
+    {
+        'b', clf_log_bytes_sent
+    },
+    {
+        'B', log_bytes_sent
+    },
+    {
+        'V', log_server_name
+    },
+    {
+        'v', log_virtual_host
+    },
+    {
+        'p', log_server_port
+    },
+    {
+        'H', log_request_protocol
+    },
+    {
+        'm', log_request_method
+    },
+    {
+        'q', log_request_query
+    },
+    {
+        'w', log_worker_name
+    },
+    {
+        '\0'
+    }
+};
+
+static struct log_item_list *find_log_func(char k)
+{
+    int i;
+
+    for (i = 0; log_item_keys[i].ch; ++i)
+        if (k == log_item_keys[i].ch) {
+            return &log_item_keys[i];
+        }
+
+    return NULL;
+}
+
+static char *parse_request_log_misc_string(apr_pool_t *p,
+                                           request_log_format_item *it,
+                                           const char **sa)
+{
+    const char *s;
+    char *d;
+
+    it->func = constant_item;
+
+    s = *sa;
+    while (*s && *s != '%') {
+        s++;
+    }
+    /*
+     * This might allocate a few chars extra if there's a backslash
+     * escape in the format string.
+     */
+    it->arg = apr_palloc(p, s - *sa + 1);
+
+    d = it->arg;
+    s = *sa;
+    while (*s && *s != '%') {
+        if (*s != '\\') {
+            *d++ = *s++;
+        }
+        else {
+            s++;
+            switch (*s) {
+            case '\\':
+                *d++ = '\\';
+                s++;
+                break;
+            case 'n':
+                *d++ = '\n';
+                s++;
+                break;
+            case 't':
+                *d++ = '\t';
+                s++;
+                break;
+            default:
+                /* copy verbatim */
+                *d++ = '\\';
+                /*
+                 * Allow the loop to deal with this *s in the normal
+                 * fashion so that it handles end of string etc.
+                 * properly.
+                 */
+                break;
+            }
+        }
+    }
+    *d = '\0';
+
+    *sa = s;
+    return NULL;
+}
+
+static char *parse_request_log_item(apr_pool_t *p,
+                                    request_log_format_item *it,
+                                    const char **sa)
+{
+    const char *s = *sa;
+    struct log_item_list *l;
+
+    if (*s != '%') {
+        return parse_request_log_misc_string(p, it, sa);
+    }
+    
+    ++s;
+    it->arg = "";               /* For safety's sake... */
+    
+    l = find_log_func(*s++);
+    if (!l) {
+        char dummy[2];
+
+        dummy[0] = s[-1];
+        dummy[1] = '\0';
+        return apr_pstrcat(p, "Unrecognized JkRequestLogFormat directive %",
+                          dummy, NULL);
+    }
+    it->func = l->func;
+    *sa = s;
+    return NULL;
+}
+
+static apr_array_header_t *parse_request_log_string(apr_pool_t *p, const char *s,
+                                              const char **err)
+{
+    apr_array_header_t *a = apr_array_make(p, 15, sizeof(request_log_format_item));
+    char *res;
+
+    while (*s) {
+        if ((res = parse_request_log_item(p, (request_log_format_item *) apr_array_push(a), &s))) {
+            *err = res;
+            return NULL;
+        }
+    }    
+     
+    s = "\n";
+    parse_request_log_item(p, (request_log_format_item *) apr_array_push(a), &s);
+    return a;
+}
+
+/*
+ * JkRequestLogFormat Directive Handling
+ *
+ * JkRequestLogFormat format string
+ *
+ * %b - Bytes sent, excluding HTTP headers. In CLF format
+ * %B - Bytes sent, excluding HTTP headers.             
+ * %H - The request protocol              
+ * %m - The request method
+ * %p - The canonical Port of the server serving the request
+ * %q - The query string (prepended with a ? if a query string exists,
+ *      otherwise an empty string)
+ * %r - First line of request
+ * %s - request HTTP status code
+ * %T - Requset duration, elapsed time to handle request in seconds '.' micro seconds
+ * %U - The URL path requested, not including any query string.
+ * %v - The canonical ServerName of the server serving the request.
+ * %V - The server name according to the UseCanonicalName setting.
+ * %w - Tomcat worker name
+ */
+
+static const char *jk_set_request_log_format(cmd_parms *cmd,
+                                             void *dummy,    
+                                             char *format)
+{
+    const char *err_string = NULL;
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->format_string = apr_pstrdup(cmd->pool,format);
+    if( format != NULL ) {
+        conf->format = parse_request_log_string(cmd->pool, format, &err_string);
+    }
+    if( conf->format == NULL )
+        return "JkRequestLogFormat format array NULL";
+
+    return err_string;
+}
+
+
+/*
+ * JkExtractSSL Directive Handling
+ *
+ * JkExtractSSL On/Off
+ */
+
+static const char *jk_set_enable_ssl(cmd_parms *cmd,
+                                     void *dummy,
+                                     int flag)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+   
+    /* Set up our value */
+    conf->ssl_enable = flag ? JK_TRUE : JK_FALSE;
+
+    return NULL;
+}
+
+/*
+ * JkHTTPSIndicator Directive Handling
+ *
+ * JkHTTPSIndicator HTTPS
+ */
+
+static const char *jk_set_https_indicator(cmd_parms *cmd,
+                                          void *dummy,
+                                          const char *indicator)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->https_indicator = apr_pstrdup(cmd->pool,indicator);
+
+    return NULL;
+}
+
+/*
+ * JkCERTSIndicator Directive Handling
+ *
+ * JkCERTSIndicator SSL_CLIENT_CERT
+ */
+
+static const char *jk_set_certs_indicator(cmd_parms *cmd,
+                                          void *dummy,
+                                          const char *indicator)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->certs_indicator = apr_pstrdup(cmd->pool,indicator);
+
+    return NULL;
+}
+
+/*
+ * JkCIPHERIndicator Directive Handling
+ *
+ * JkCIPHERIndicator SSL_CIPHER
+ */
+
+static const char *jk_set_cipher_indicator(cmd_parms *cmd,
+                                           void *dummy,
+                                           const char *indicator)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->cipher_indicator = apr_pstrdup(cmd->pool,indicator);
+
+    return NULL;
+}
+
+/*
+ * JkSESSIONIndicator Directive Handling
+ *
+ * JkSESSIONIndicator SSL_SESSION_ID
+ */
+
+static const char *jk_set_session_indicator(cmd_parms *cmd,
+                                           void *dummy,
+                                           const char *indicator)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->session_indicator = apr_pstrdup(cmd->pool,indicator);
+
+    return NULL;
+}
+
+/*
+ * JkKEYSIZEIndicator Directive Handling
+ *
+ * JkKEYSIZEIndicator SSL_CIPHER_USEKEYSIZE
+ */
+
+static const char *jk_set_key_size_indicator(cmd_parms *cmd,
+                                           void *dummy,
+                                           const char *indicator)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->key_size_indicator = apr_pstrdup(cmd->pool,indicator);
+
+    return NULL;
+}
+
+/*
+ * JkOptions Directive Handling
+ *
+ *
+ * +ForwardSSLKeySize        => Forward SSL Key Size, to follow 2.3 specs but may broke old TC 3.2
+ * -ForwardSSLKeySize        => Don't Forward SSL Key Size, will make mod_jk works with all TC release
+ *  ForwardURICompat         => Forward URI normally, less spec compliant but mod_rewrite compatible (old TC)
+ *  ForwardURICompatUnparsed => Forward URI as unparsed, spec compliant but broke mod_rewrite (old TC)
+ *  ForwardURIEscaped        => Forward URI escaped and Tomcat (3.3 rc2) stuff will do the decoding part
+ *  ForwardDirectories       => Forward all directory requests with no index files to Tomcat 
+ */
+
+const char *jk_set_options(cmd_parms *cmd,
+                           void *dummy,
+                           const char *line)
+{
+    int  opt = 0;
+    int  mask = 0;
+    char action;
+    char *w;
+
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    while (line[0] != 0) {
+        w = ap_getword_conf(cmd->pool, &line);
+        action = 0;
+
+        if (*w == '+' || *w == '-') {
+            action = *(w++);
+        }
+
+        mask = 0;
+
+        if (!strcasecmp(w, "ForwardKeySize")) {
+            opt = JK_OPT_FWDKEYSIZE;
+        }
+        else if (!strcasecmp(w, "ForwardURICompat")) {
+            opt = JK_OPT_FWDURICOMPAT;
+            mask = JK_OPT_FWDURIMASK;
+        }
+        else if (!strcasecmp(w, "ForwardURICompatUnparsed")) {
+            opt = JK_OPT_FWDURICOMPATUNPARSED;
+            mask = JK_OPT_FWDURIMASK;
+        }
+        else if (!strcasecmp(w, "ForwardURIEscaped")) {
+            opt = JK_OPT_FWDURIESCAPED;
+            mask = JK_OPT_FWDURIMASK;
+        }
+        else if (!strcasecmp(w, "ForwardDirectories")) {
+            opt = JK_OPT_FWDDIRS;
+        }
+        else
+            return apr_pstrcat(cmd->pool, "JkOptions: Illegal option '", w, "'", NULL);
+
+        conf->options &= ~mask;
+
+        if (action == '-') {
+            conf->options &= ~opt;
+        }
+        else if (action == '+') {
+            conf->options |=  opt;
+        }
+        else {            /* for now +Opt == Opt */
+            conf->options |=  opt;
+        }
+    }
+    return NULL;
+}
+
+/*
+ * JkEnvVar Directive Handling
+ *
+ * JkEnvVar MYOWNDIR
+ */
+
+static const char *jk_add_env_var(cmd_parms *cmd,
+                                  void *dummy,
+                                  const char *env_name,
+                                  const char *default_value)
+{
+    server_rec *s = cmd->server;
+    jk_server_conf_t *conf =
+        (jk_server_conf_t *)ap_get_module_config(s->module_config, &jk_module);
+
+    conf->envvars_in_use = JK_TRUE;
+
+    apr_table_add(conf->envvars, env_name, default_value);
+
+    return NULL;
+}
+    
+static const command_rec jk_cmds[] =
+    {
+    /*
+     * JkWorkersFile specifies a full path to the location of the worker
+     * properties file.
+     *
+     * This file defines the different workers used by apache to redirect
+     * servlet requests.
+     */
+    AP_INIT_TAKE1(
+        "JkWorkersFile", jk_set_worker_file, NULL, RSRC_CONF,
+        "the name of a worker file for the Jakarta servlet containers"),
+
+    /*
+     * JkWorker allows you to specify worker properties in server.xml.
+     * They are added before any property in JkWorkersFile ( if any ), 
+     * as a more convenient way to configure
+     */
+    AP_INIT_TAKE2(
+        "JkWorker", jk_worker_property, NULL, RSRC_CONF,
+        "worker property"),
+
+    /*
+     * JkAutoMount specifies that the list of handled URLs must be
+     * asked to the servlet engine (autoconf feature)
+     */
+    AP_INIT_TAKE12(
+        "JkAutoMount", jk_automount_context, NULL, RSRC_CONF,
+        "automatic mount points to a Tomcat worker"),
+
+    /*
+     * JkMount mounts a url prefix to a worker (the worker need to be
+     * defined in the worker properties file.
+     */
+    AP_INIT_TAKE23(
+        "JkMount", jk_mount_context, NULL, RSRC_CONF,
+        "A mount point from a context to a Tomcat worker"),
+
+    /*
+     * JkMountCopy specifies if mod_jk should copy the mount points
+     * from the main server to the virtual servers.
+     */
+    AP_INIT_FLAG(
+        "JkMountCopy", jk_set_mountcopy, NULL, RSRC_CONF,
+        "Should the base server mounts be copied to the virtual server"),
+
+    /*
+     * JkLogFile & JkLogLevel specifies to where should the plugin log
+     * its information and how much.
+     * JkLogStampFormat specify the time-stamp to be used on log
+     */
+    AP_INIT_TAKE1(
+        "JkLogFile", jk_set_log_file, NULL, RSRC_CONF,
+        "Full path to the Jakarta Tomcat module log file"),
+    AP_INIT_TAKE1(
+        "JkLogLevel", jk_set_log_level, NULL, RSRC_CONF,
+        "The Jakarta Tomcat module log level, can be debug, "
+        "info, error or emerg"),
+    AP_INIT_TAKE1(
+        "JkLogStampFormat", jk_set_log_fmt, NULL, RSRC_CONF,
+        "The Jakarta Tomcat module log format, follow strftime synthax"),
+    AP_INIT_TAKE1(
+        "JkRequestLogFormat", jk_set_request_log_format, NULL, RSRC_CONF,
+        "The Jakarta mod_jk module request log format string"),
+
+    /*
+     * Automatically Alias webapp context directories into the Apache
+     * document space.
+     */
+    AP_INIT_TAKE1(
+        "JkAutoAlias", jk_set_auto_alias, NULL, RSRC_CONF,
+        "The Jakarta mod_jk module automatic context apache alias directory"),
+
+    /*
+     * Apache has multiple SSL modules (for example apache_ssl, stronghold
+     * IHS ...). Each of these can have a different SSL environment names
+     * The following properties let the administrator specify the envoiroment
+     * variables names.
+     *
+     * HTTPS - indication for SSL
+     * CERTS - Base64-Der-encoded client certificates.
+     * CIPHER - A string specifing the ciphers suite in use.
+     * KEYSIZE - Size of Key used in dialogue (#bits are secure)
+     * SESSION - A string specifing the current SSL session.
+     */
+    AP_INIT_TAKE1(
+        "JkHTTPSIndicator", jk_set_https_indicator, NULL, RSRC_CONF,
+        "Name of the Apache environment that contains SSL indication"),
+    AP_INIT_TAKE1(
+        "JkCERTSIndicator", jk_set_certs_indicator, NULL, RSRC_CONF,
+        "Name of the Apache environment that contains SSL client certificates"),
+    AP_INIT_TAKE1(
+        "JkCIPHERIndicator", jk_set_cipher_indicator, NULL, RSRC_CONF,
+        "Name of the Apache environment that contains SSL client cipher"),
+    AP_INIT_TAKE1(
+        "JkSESSIONIndicator", jk_set_session_indicator, NULL, RSRC_CONF,
+        "Name of the Apache environment that contains SSL session"),
+    AP_INIT_TAKE1(
+        "JkKEYSIZEIndicator", jk_set_key_size_indicator, NULL, RSRC_CONF,
+        "Name of the Apache environment that contains SSL key size in use"),
+    AP_INIT_FLAG(
+        "JkExtractSSL", jk_set_enable_ssl, NULL, RSRC_CONF,
+        "Turns on SSL processing and information gathering by mod_jk"),
+
+    /*
+     * Options to tune mod_jk configuration
+     * for now we understand :
+     * +ForwardSSLKeySize        => Forward SSL Key Size, to follow 2.3 specs but may broke old TC 3.2
+     * -ForwardSSLKeySize        => Don't Forward SSL Key Size, will make mod_jk works with all TC release
+     *  ForwardURICompat         => Forward URI normally, less spec compliant but mod_rewrite compatible (old TC)
+     *  ForwardURICompatUnparsed => Forward URI as unparsed, spec compliant but broke mod_rewrite (old TC)
+     *  ForwardURIEscaped        => Forward URI escaped and Tomcat (3.3 rc2) stuff will do the decoding part
+     */
+    AP_INIT_RAW_ARGS(
+        "JkOptions", jk_set_options, NULL, RSRC_CONF, 
+        "Set one of more options to configure the mod_jk module"),
+
+    /*
+     * JkEnvVar let user defines envs var passed from WebServer to
+     * Servlet Engine
+     */
+    AP_INIT_TAKE2(
+        "JkEnvVar", jk_add_env_var, NULL, RSRC_CONF,
+        "Adds a name of environment variable that should be sent "
+        "to servlet-engine"),
+
+    {NULL}
+};
+
+/* ========================================================================= */
+/* The JK module handlers                                                    */
+/* ========================================================================= */
+
+/** Util - cleanup endpoint.
+ */
+apr_status_t jk_cleanup_endpoint( void *data ) {
+    jk_endpoint_t *end = (jk_endpoint_t *)data;    
+    /*     printf("XXX jk_cleanup1 %ld\n", data); */
+    end->done(&end, NULL);  
+    return 0;
+}
+
+/** Main service method, called to forward a request to tomcat
+ */
+static int jk_handler(request_rec *r)
+{   
+    const char       *worker_name;
+    jk_server_conf_t *xconf;
+    jk_server_conf_t *conf;
+    int              rc,dmt=1;
+
+    /* We do DIR_MAGIC_TYPE here to make sure TC gets all requests, even
+     * if they are directory requests, in case there are no static files
+     * visible to Apache and/or DirectoryIndex was not used. This is only
+     * used when JkOptions has ForwardDirectories set. */
+
+    /* Not for me, try next handler */
+    if(strcmp(r->handler,JK_HANDLER) && (dmt=strcmp(r->handler,DIR_MAGIC_TYPE)))
+        return DECLINED;
+
+    xconf = (jk_server_conf_t *)ap_get_module_config(r->server->module_config, 
+                                                     &jk_module);
+    if (apr_table_get(r->subprocess_env, "no-jk")) {
+        jk_log(xconf->log, JK_LOG_DEBUG, "Into handler no-jk env var detected for uri=%s, declined\n",
+           r->uri);
+
+        return DECLINED;
+    }
+
+    /* Was the option to forward directories to Tomcat set? */
+    if(!dmt && !(xconf->options & JK_OPT_FWDDIRS))
+        return DECLINED;
+
+    worker_name = apr_table_get(r->notes, JK_WORKER_ID);
+
+    /* Set up r->read_chunked flags for chunked encoding, if present */
+    if(rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) {
+        return rc;
+    }
+
+    if( worker_name == NULL ) {
+      /* we may be here because of a manual directive ( that overrides 
+         translate and
+         sets the handler directly ). We still need to know the worker.
+      */
+      if( worker_env.num_of_workers == 1 ) {
+          /** We have a single worker ( the common case ). 
+              ( lb is a bit special, it should count as a single worker but 
+              I'm not sure how ). We also have a manual config directive that
+              explicitely give control to us. */
+          worker_name=  worker_env.first_worker;
+          jk_log(xconf->log, JK_LOG_DEBUG, 
+                 "Manual configuration for %s %s %d\n",
+                 r->uri, worker_env.first_worker, worker_env.num_of_workers); 
+      } else {
+          char *uri = apr_pstrdup(r->pool, r->uri);
+          worker_name = map_uri_to_worker(xconf->uw_map, uri, xconf->log);
+          if( worker_name == NULL ) 
+              worker_name=  worker_env.first_worker;
+          jk_log(xconf->log, JK_LOG_DEBUG, 
+                 "Manual configuration for %s %d\n",
+                 r->uri, worker_env.first_worker); 
+      }
+    }
+
+    jk_log(xconf->log, JK_LOG_DEBUG, "Into handler r->proxyreq=%d "
+           "r->handler=%s r->notes=%d worker=%s\n", 
+           r->proxyreq, r->handler, r->notes, worker_name); 
+
+    conf=(jk_server_conf_t *)ap_get_module_config(r->server->module_config, 
+                                                  &jk_module);
+
+    /* If this is a proxy request, we'll notify an error */
+    if(r->proxyreq) {
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    if(conf && ! worker_name ) {
+        /* Direct mapping ( via setHandler ). Try overrides */
+        char *uri = apr_pstrdup(r->pool, r->uri);
+        worker_name = map_uri_to_worker(conf->uw_map, uri, conf->log);
+        if( ! worker_name ) {
+            /* Since we are here, an explicit (native) mapping has been used */
+            /* Use default worker */
+            worker_name="ajp14"; /* XXX add a directive for default */
+        }
+        if(worker_name) {
+            apr_table_setn(r->notes, JK_WORKER_ID, worker_name);
+        }
+    }
+      
+    if(worker_name) {
+        jk_worker_t *worker = wc_get_worker_for_name(worker_name, xconf->log);
+
+        /* If the remote client has aborted, just ignore the request */
+        if (r->connection->aborted) {
+            return OK;
+        }
+
+        if(worker) {
+            struct timeval tv_begin,tv_end;
+            int rc = JK_FALSE;
+            apache_private_data_t private_data;
+            jk_ws_service_t s;
+            jk_pool_atom_t buf[SMALL_POOL_SIZE];
+            jk_open_pool(&private_data.p, buf, sizeof(buf));
+
+            private_data.response_started = JK_FALSE;
+            private_data.read_body_started = JK_FALSE;
+            private_data.r = r;
+
+            jk_init_ws_service(&s);
+
+            s.ws_private = &private_data;
+            s.pool = &private_data.p;            
+#ifndef NO_GETTIMEOFDAY
+            if(conf->format != NULL) {
+                gettimeofday(&tv_begin, NULL);
+            }
+#endif
+            
+            if(init_ws_service(&private_data, &s, conf)) {
+                jk_endpoint_t *end = NULL;
+
+        /* Use per/thread pool ( or "context" ) to reuse the 
+           endpoint. It's a bit faster, but I don't know 
+           how to deal with load balancing - but it's usefull for JNI
+        */
+
+/* Disable "re-use" for now since the following code is currently platform specific */
+/* #ifdef REUSE_WORKER
+        apr_pool_t *rpool=r->pool;
+        apr_pool_t *parent_pool= apr_pool_get_parent( rpool );
+        apr_pool_t *tpool= apr_pool_get_parent( parent_pool );
+        
+        apr_pool_userdata_get( (void **)&end, "jk_thread_endpoint", tpool );
+        if(end==NULL ) {
+            worker->get_endpoint(worker, &end, xconf->log);
+            apr_pool_userdata_set( end , "jk_thread_endpoint", 
+                                   &jk_cleanup_endpoint,  tpool );
+        }
+#else */
+        /* worker->get_endpoint might fail if we are out of memory so check */
+        /* and handle it */
+        if (worker->get_endpoint(worker, &end, xconf->log))
+/* #endif */
+        {   
+            int is_recoverable_error = JK_FALSE;
+            rc = end->service(end, &s, xconf->log, &is_recoverable_error);
+
+            if (s.content_read < s.content_length ||
+                (s.is_chunked && ! s.no_more_chunks)) {
+
+                /*
+                * If the servlet engine didn't consume all of the
+                * request data, consume and discard all further
+                * characters left to read from client
+                */
+                char *buff = apr_palloc(r->pool, 2048);
+                if (buff != NULL) {
+                    int rd;
+                    while ((rd = ap_get_client_block(r, buff, 2048)) > 0) {
+                        s.content_read += rd;
+                    }
+                }
+            }
+                                                                            
+/* #ifndef REUSE_WORKER */
+            end->done(&end, xconf->log); 
+/* #endif */
+                }
+                else /* this means we couldn't get an endpoint */
+                    rc = 0; /* just to make sure that we know we've failed */
+            }
+
+#ifndef NO_GETTIMEOFDAY
+            if(conf->format != NULL) {
+                char *duration = NULL;
+                char *status = NULL;
+                long micro,seconds;
+                gettimeofday(&tv_end, NULL);
+                if( tv_end.tv_usec < tv_begin.tv_usec ) {
+                    tv_end.tv_usec += 1000000;
+                    tv_end.tv_sec--;
+                }
+                micro = tv_end.tv_usec - tv_begin.tv_usec;
+                seconds = tv_end.tv_sec - tv_begin.tv_sec;
+                duration = apr_psprintf(r->pool,"%.1d.%.6d",seconds,micro);
+                apr_table_setn(r->notes, JK_DURATION, duration);
+                request_log_transaction(r,conf);
+            }
+#endif
+
+            jk_close_pool(&private_data.p);
+
+            if (rc > 0) {
+                /* If tomcat returned no body and the status is not OK,
+                   let apache handle the error code */
+                if( !r->sent_bodyct && r->status >= HTTP_BAD_REQUEST ) {
+                    return r->status;
+                }
+                return OK;    /* NOT r->status, even if it has changed. */
+            } else if (rc == JK_CLIENT_ERROR) {
+                r->connection->aborted = 1;
+                return OK;
+            } else {
+                return HTTP_INTERNAL_SERVER_ERROR;
+            }
+        } else {
+            return HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+    }
+
+    return DECLINED;
+}
+
+/** Standard apache hook, cleanup jk
+ */
+static apr_status_t jk_apr_pool_cleanup(void *data)
+{
+    server_rec *s = data;
+
+    while (NULL != s) {
+        jk_server_conf_t *conf =
+            (jk_server_conf_t *)ap_get_module_config(s->module_config,
+                                                     &jk_module);
+
+        if (conf)
+        {
+            /* On pool cleanup pass NULL for the jk_logger to
+               prevent segmentation faults on Windows because
+               we can't guarantee what order pools get cleaned
+               up between APR implementations. */
+            wc_close(NULL);
+            if (conf->worker_properties)
+                map_free(&conf->worker_properties);
+            if (conf->uri_to_context)
+                map_free(&conf->uri_to_context);
+            if (conf->automount)
+                map_free(&conf->automount);
+            if (conf->uw_map)
+                uri_worker_map_free(&conf->uw_map, NULL);
+        }
+        s = s->next;
+    }
+    return APR_SUCCESS;
+}
+
+/** Create default jk_config. XXX This is mostly server-independent,
+    all servers are using something similar - should go to common.
+ */
+static void *create_jk_config(apr_pool_t *p, server_rec *s)
+{
+    jk_server_conf_t *c =
+        (jk_server_conf_t *) apr_pcalloc(p, sizeof(jk_server_conf_t));
+
+    c->worker_properties = NULL;
+    map_alloc(& c->worker_properties);
+    c->worker_file     = NULL;
+    c->log_file        = NULL;
+    c->log_level       = -1;
+    c->log             = NULL;
+    c->alias_dir       = NULL;
+    c->format_string   = NULL;
+    c->format          = NULL;
+    c->mountcopy       = JK_FALSE;
+    c->was_initialized = JK_FALSE;
+    c->options         = JK_OPT_FWDURIDEFAULT;
+
+    /*
+     * By default we will try to gather SSL info.
+     * Disable this functionality through JkExtractSSL
+     */
+    c->ssl_enable  = JK_TRUE;
+    /*
+     * The defaults ssl indicators match those in mod_ssl (seems
+     * to be in more use).
+     */
+    c->https_indicator  = "HTTPS";
+    c->certs_indicator  = "SSL_CLIENT_CERT";
+
+    /*
+     * The following (comented out) environment variables match apache_ssl!
+     * If you are using apache_sslapache_ssl uncomment them (or use the
+     * configuration directives to set them.
+     *
+    c->cipher_indicator = "HTTPS_CIPHER";
+    c->session_indicator = NULL;
+     */
+
+    /*
+     * The following environment variables match mod_ssl! If you
+     * are using another module (say apache_ssl) comment them out.
+     */
+    c->cipher_indicator = "SSL_CIPHER";
+    c->session_indicator = "SSL_SESSION_ID";
+    c->key_size_indicator = "SSL_CIPHER_USEKEYSIZE";
+
+    if(!map_alloc(&(c->uri_to_context))) {
+        jk_error_exit(APLOG_MARK, APLOG_EMERG, s, p, "Memory error");
+    }
+    if(!map_alloc(&(c->automount))) {
+        jk_error_exit(APLOG_MARK, APLOG_EMERG, s, p, "Memory error");
+    }
+
+    c->uw_map = NULL;
+    c->secret_key = NULL; 
+
+    c->envvars_in_use = JK_FALSE;
+    c->envvars = apr_table_make(p, 0);
+
+    c->s = s;
+
+    apr_pool_cleanup_register(p, s, jk_apr_pool_cleanup, jk_apr_pool_cleanup);
+    return c;
+}
+
+
+/** Utility - copy a map . XXX Should move to jk_map, it's generic code.
+ */
+static void copy_jk_map(apr_pool_t *p, server_rec * s, jk_map_t * src, 
+                        jk_map_t * dst)
+{   
+    int sz = map_size(src);
+        int i;
+        for(i = 0 ; i < sz ; i++) {
+            void *old;
+            char *name = map_name_at(src, i);
+            if(map_get(src, name, NULL) == NULL) {
+                if(!map_put(dst, name, 
+                            apr_pstrdup(p, map_get_string(src, name, NULL)), 
+                            &old)) {
+                    jk_error_exit(APLOG_MARK, APLOG_EMERG, s, p, 
+                                  "Memory error");
+                }
+            } 
+        }
+}
+
+/** Standard apache callback, merge jk options specified in <Directory>
+    context or <Host>.
+ */
+static void *merge_jk_config(apr_pool_t *p, 
+                             void *basev, 
+                             void *overridesv)
+{
+    jk_server_conf_t *base = (jk_server_conf_t *) basev;
+    jk_server_conf_t *overrides = (jk_server_conf_t *)overridesv;
+ 
+    if(base->ssl_enable) {
+        overrides->ssl_enable        = base->ssl_enable;
+        overrides->https_indicator   = base->https_indicator;
+        overrides->certs_indicator   = base->certs_indicator;
+        overrides->cipher_indicator  = base->cipher_indicator;
+        overrides->session_indicator = base->session_indicator;
+    }
+
+    overrides->options = base->options;
+
+    if(overrides->mountcopy) {
+        copy_jk_map(p, overrides->s, base->uri_to_context, 
+                    overrides->uri_to_context);
+        copy_jk_map(p, overrides->s, base->automount, overrides->automount);
+    }
+
+    if(base->envvars_in_use) {
+        overrides->envvars_in_use = JK_TRUE;
+        overrides->envvars = apr_table_overlay(p, overrides->envvars, 
+                                               base->envvars);
+    }
+
+    if(!uri_worker_map_alloc(&(overrides->uw_map), 
+                             overrides->uri_to_context, 
+                             overrides->log)) {
+        jk_error_exit(APLOG_MARK, APLOG_EMERG, overrides->s, 
+                      overrides->s->process->pool, "Memory error");
+    }
+    
+    if (base->secret_key)
+        overrides->secret_key = base->secret_key;
+
+    return overrides;
+}
+
+static int JK_METHOD jk_log_to_file(jk_logger_t *l,
+                          int level,
+                          const char *what)
+{
+    if( l &&
+        (l->level <= level || level == JK_LOG_REQUEST_LEVEL) &&
+        l->logger_private && what) {
+        unsigned sz = strlen(what);
+        apr_size_t wrote = sz;
+        char error[256];
+        if(sz) {
+            apr_status_t status;
+            file_logger_t *p = l->logger_private;
+            if(p->jklogfp) {
+                 apr_status_t rv;
+                 rv = apr_global_mutex_lock(jk_log_lock);
+                 if (rv != APR_SUCCESS) {           
+                     ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL,
+                                   "apr_global_mutex_lock(jk_log_lock) failed");
+                     /* XXX: Maybe this should be fatal? */
+                }
+                status = apr_file_write(p->jklogfp,what,&wrote);
+                if (status != APR_SUCCESS) {
+                    apr_strerror(status, error, 254);
+                    ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0,
+                             NULL, "mod_jk: jk_log_to_file %s failed: %s\n",
+                             what, error);
+                }
+                rv = apr_global_mutex_unlock(jk_log_lock);
+                if (rv != APR_SUCCESS) {           
+                    ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, NULL,
+                                  "apr_global_mutex_unlock(jk_log_lock) failed");
+                    /* XXX: Maybe this should be fatal? */
+                }
+            }
+        }
+
+        return JK_TRUE;
+    }
+
+    return JK_FALSE;
+}
+
+/*
+** +-------------------------------------------------------+
+** |                                                       |
+** |              jk logfile support                       |
+** |                                                       |
+** +-------------------------------------------------------+
+*/
+
+static apr_status_t jklog_cleanup(void *d)
+{
+    /* set the main_log to NULL */
+    main_log = NULL;
+    return APR_SUCCESS;
+}
+
+static int open_jklog(server_rec *s, apr_pool_t *p)
+{
+    jk_server_conf_t *conf;
+    const char *fname;
+    apr_status_t rc;
+    piped_log *pl;
+    jk_logger_t *jkl;
+    file_logger_t *flp;
+    int    jklog_flags = ( APR_WRITE | APR_APPEND | APR_CREATE );
+    apr_fileperms_t jklog_mode  = ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD );
+
+    conf = ap_get_module_config(s->module_config, &jk_module);
+
+    if (main_log != NULL) {
+        conf->log = main_log;
+        return 0;
+    }
+    if (conf->log_file == NULL) {
+        return 0;
+    }
+    if (*(conf->log_file) == '\0') {
+        return 0;
+    }
+
+    if (*conf->log_file == '|') {
+        if ((pl = ap_open_piped_log(p, conf->log_file+1)) == NULL) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+                         "mod_jk: could not open reliable pipe "
+                         "to jk log %s", conf->log_file+1);
+            return -1;
+        }
+        conf->jklogfp = (void *)ap_piped_log_write_fd(pl);
+    }
+    else {
+        fname = ap_server_root_relative(p, conf->log_file);
+        if (!fname) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s,
+                         "mod_jk: Invalid JkLog "
+                         "path %s", conf->log_file);
+            return -1;
+        }
+        if ((rc = apr_file_open(&conf->jklogfp, fname,
+                                jklog_flags, jklog_mode, p))
+                != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, rc, s,
+                         "mod_jk: could not open JkLog "
+                         "file %s", fname);
+            return -1;
+        }
+        apr_file_inherit_set(conf->jklogfp);
+    }
+    jkl = (jk_logger_t *)apr_palloc(p,sizeof(jk_logger_t));
+    flp = (file_logger_t *)apr_palloc(p,sizeof(file_logger_t));
+    if(jkl && flp) {                              
+        jkl->log = jk_log_to_file;              
+        jkl->level = conf->log_level;      
+        jkl->logger_private = flp; 
+        flp->jklogfp = conf->jklogfp;
+        conf->log = jkl;
+        if (main_log == NULL)
+            main_log = conf->log;
+        apr_pool_cleanup_register(p, main_log, jklog_cleanup, jklog_cleanup);
+        return 0;
+    }
+
+    return -1;
+}
+
+
+/** Standard apache callback, initialize jk.
+ */
+static void jk_child_init(apr_pool_t *pconf, 
+              server_rec *s)
+{
+}
+
+/** Initialize jk, using worker.properties. 
+    We also use apache commands ( JkWorker, etc), but this use is 
+    deprecated, as we'll try to concentrate all config in
+    workers.properties, urimap.properties, and ajp14 autoconf.
+    
+    Apache config will only be used for manual override, using 
+    SetHandler and normal apache directives ( but minimal jk-specific
+    stuff )
+*/
+static void init_jk( apr_pool_t *pconf, jk_server_conf_t *conf, server_rec *s ) {
+    /*     jk_map_t *init_map = NULL; */
+    jk_map_t *init_map = conf->worker_properties;
+
+    if(!uri_worker_map_alloc(&(conf->uw_map), 
+                             conf->uri_to_context, conf->log)) {
+        jk_error_exit(APLOG_MARK, APLOG_EMERG, s, pconf, "Memory error");
+    }
+
+    /*     if(map_alloc(&init_map)) { */
+    if( ! map_read_properties(init_map, conf->worker_file)) {
+        if( map_size( init_map ) == 0 ) {
+            ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO,
+                         APLOG_EMERG, NULL,
+                         "No worker file and no worker options in httpd.conf \n"
+                         "use JkWorkerFile to set workers\n");
+            return;
+        }
+    }
+
+    /* we add the URI->WORKER MAP since workers using AJP14
+       will feed it */
+    worker_env.uri_to_worker = conf->uw_map;
+    worker_env.virtual       = "*";     /* for now */
+    worker_env.server_name   = (char *)ap_get_server_version();
+    if(wc_open(init_map, &worker_env, conf->log)) {
+        ap_add_version_component(pconf, JK_EXPOSED_VERSION);
+        return;
+    }
+    return;
+}
+
+static int jk_post_config(apr_pool_t *pconf, 
+                          apr_pool_t *plog, 
+                          apr_pool_t *ptemp, 
+                          server_rec *s)
+{
+    apr_status_t rv;
+
+    if(!s->is_virtual) {
+        jk_server_conf_t *conf =
+            (jk_server_conf_t *)ap_get_module_config(s->module_config,
+                                                     &jk_module);
+        if(!conf->was_initialized) {
+            conf->was_initialized = JK_TRUE;
+            init_jk( pconf, conf, s );
+        }
+    }
+
+    /* create the jk log lockfiles in the parent */
+    if ((rv = apr_global_mutex_create(&jk_log_lock, NULL,
+                                      APR_LOCK_DEFAULT, pconf)) != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
+                     "mod_jk: could not create jk_log_lock");
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+#if APR_USE_SYSVSEM_SERIALIZE
+    rv = unixd_set_global_mutex_perms(jk_log_lock);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
+                     "mod_jk: Could not set permissions on "
+                     "jk_log_lock; check User and Group directives");
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+#endif
+
+    /* step through the servers and
+     * - open each jk logfile
+     */    
+    for (; s; s = s->next) {
+        if (open_jklog(s, pconf))
+            return HTTP_INTERNAL_SERVER_ERROR;
+
+    }
+
+    return OK;
+}
+
+/** Use the internal mod_jk mappings to find if this is a request for
+ *    tomcat and what worker to use. 
+ */
+static int jk_translate(request_rec *r)
+{    
+    if(!r->proxyreq) {        
+        jk_server_conf_t *conf =
+            (jk_server_conf_t *)ap_get_module_config(r->server->module_config,
+                                                     &jk_module);
+
+        if(conf) {
+            char *worker;
+            char *uri;
+            if( (r->handler != NULL ) && 
+                (! strcmp( r->handler, JK_HANDLER ) )) {
+                /* Somebody already set the handler, probably manual config
+                 * or "native" configuration, no need for extra overhead
+                 */
+                jk_log(conf->log, JK_LOG_DEBUG, 
+                       "Manually mapped, no need to call uri_to_worker\n");
+                return DECLINED;
+            }
+
+            if (apr_table_get(r->subprocess_env, "no-jk")) {
+                jk_log(conf->log, JK_LOG_DEBUG, "Into translate no-jk env var detected for uri=%s, declined\n",
+                 r->uri);
+                                                                                                                                          
+                 return DECLINED;
+            }
+
+            /* Special case to make sure that apache can serve a directory
+               listing if there are no matches for the DirectoryIndex and
+               Tomcat webapps are mapped into apache using JkAutoAlias. */
+            if(r->main != NULL && (conf->alias_dir != NULL) &&
+               !strcmp(r->main->handler,DIR_MAGIC_TYPE)) {
+
+                /* Append the request uri to the JkAutoAlias directory and
+                   determine if the file exists. */
+                char *clean_uri;        
+                apr_finfo_t finfo; 
+                finfo.filetype = APR_NOFILE;
+                clean_uri = apr_pstrdup(r->pool, r->uri);
+                ap_no2slash(clean_uri); 
+                /* Map uri to a context static file */
+                if (strlen(clean_uri) > 1) {
+                    char *context_path = NULL;
+
+                    context_path = apr_pstrcat(r->pool,conf->alias_dir,
+                                              ap_os_escape_path(r->pool,clean_uri,1),
+                                              NULL);                                 
+                    if( context_path != NULL ) {
+                        apr_stat(&finfo,context_path,APR_FINFO_TYPE,r->pool);
+                    }                   
+                }    
+                if(finfo.filetype != APR_REG) {
+                    jk_log(conf->log, JK_LOG_DEBUG,
+                        "JkAutoAlias, no DirectoryIndex file for URI %s\n",
+                        r->uri);        
+                     return DECLINED;
+                }
+            }    
+
+            uri = apr_pstrdup(r->pool, r->uri);
+            worker = map_uri_to_worker(conf->uw_map, uri, conf->log);
+
+            if(worker) {
+                r->handler=apr_pstrdup(r->pool,JK_HANDLER);
+                apr_table_setn(r->notes, JK_WORKER_ID, worker);
+        
+                /* This could be a sub-request, possibly from mod_dir */
+                /* Also set the HANDLER and uri for subrequest */ 
+                if(r->main) {
+                    r->main->handler=apr_pstrdup(r->main->pool,JK_HANDLER);
+                    r->main->uri=apr_pstrdup(r->main->pool,r->uri);
+                    apr_table_setn(r->main->notes, JK_WORKER_ID, worker);
+                }
+
+                return OK;
+            } else if(conf->alias_dir != NULL) {
+                char *clean_uri = apr_pstrdup(r->pool, r->uri);
+                ap_no2slash(clean_uri);
+                /* Automatically map uri to a context static file */
+                jk_log(conf->log, JK_LOG_DEBUG,
+                    "mod_jk::jk_translate, check alias_dir: %s\n",
+                    conf->alias_dir);
+                if (strlen(clean_uri) > 1) {
+                    /* Get the context directory name */
+                    char *context_dir = NULL;
+                    char *context_path = NULL;
+                    char *child_dir = NULL;
+                    char *index = clean_uri;
+                    char *suffix = strchr(index+1,'/');
+                    if( suffix != NULL ) {
+                        int size = suffix - index;
+                        context_dir = apr_pstrndup(r->pool,index,size);
+                        /* Get the context child directory name */
+                        index = index + size + 1;
+                        suffix = strchr(index,'/');
+                        if( suffix != NULL ) {
+                            size = suffix - index;
+                            child_dir = apr_pstrndup(r->pool,index,size);
+                        } else {
+                            child_dir = index;
+                        }
+                        /* Deny access to WEB-INF and META-INF directories */
+                        if( child_dir != NULL ) {
+                            jk_log(conf->log, JK_LOG_DEBUG,
+                                "mod_jk::jk_translate, AutoAlias child_dir: %s\n",child_dir);
+                            if( !strcasecmp(child_dir,"WEB-INF") ||
+                                !strcasecmp(child_dir,"META-INF") ) {
+                                jk_log(conf->log, JK_LOG_DEBUG,
+                                    "mod_jk::jk_translate, AutoAlias HTTP_FORBIDDEN for URI: %s\n",
+                                    r->uri);
+                                return HTTP_FORBIDDEN;
+                            }
+                        }
+                    } else {
+                        context_dir = apr_pstrdup(r->pool,index);
+                    }
+
+                    context_path = apr_pstrcat(r->pool,conf->alias_dir,
+                                              ap_os_escape_path(r->pool,context_dir,1),
+                                              NULL);
+                    if( context_path != NULL ) {
+                        apr_finfo_t finfo;
+                        finfo.filetype = APR_NOFILE;
+                        apr_stat(&finfo,context_path,APR_FINFO_TYPE,r->pool);
+                        if( finfo.filetype == APR_DIR ) {
+                            char *escurl = ap_os_escape_path(r->pool, clean_uri, 1);
+                            char *ret = apr_pstrcat(r->pool,conf->alias_dir,escurl,NULL);
+                            /* Add code to verify real path ap_os_canonical_name */
+                            if( ret != NULL ) {
+                                jk_log(conf->log, JK_LOG_DEBUG,
+                                    "mod_jk::jk_translate, AutoAlias OK for file: %s\n",ret);
+                                r->filename = ret;
+                                return OK;
+                            }
+                        } else {
+                            /* Deny access to war files in web app directory */
+                            int size = strlen(context_dir);
+                            if( size > 4 && !strcasecmp(context_dir+(size-4),".war") ) {
+                                jk_log(conf->log, JK_LOG_DEBUG,
+                                    "mod_jk::jk_translate, AutoAlias HTTP_FORBIDDEN for URI: %s\n",
+                                    r->uri);
+                                return HTTP_FORBIDDEN;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    return DECLINED;
+}
+
+#if (MODULE_MAGIC_NUMBER_MAJOR > 20010808)
+/* bypass the directory_walk and file_walk for non-file requests */
+static int jk_map_to_storage(request_rec *r)
+{
+
+    if(!r->proxyreq && !apr_table_get(r->notes, JK_WORKER_ID)) {
+        jk_server_conf_t *conf =
+            (jk_server_conf_t *)ap_get_module_config(r->server->module_config,
+                                                     &jk_module);
+
+        if(conf) {
+            char *worker;
+            char *uri;
+            if( (r->handler != NULL ) &&
+                (! strcmp( r->handler, JK_HANDLER ) )) {
+                /* Somebody already set the handler, probably manual config
+                 * or "native" configuration, no need for extra overhead
+                 */
+                jk_log(conf->log, JK_LOG_DEBUG,
+                       "Manually mapped, no need to call uri_to_worker\n");
+                return DECLINED;
+            }
+
+            if (apr_table_get(r->subprocess_env, "no-jk")) {
+                jk_log(conf->log, JK_LOG_DEBUG, "Into map_to_storage no-jk env var detected for uri=%s, declined\n",
+                r->uri);
+                                                                                                                                          
+                return DECLINED;
+            }
+
+            uri = apr_pstrdup(r->pool, r->uri);
+            worker = map_uri_to_worker(conf->uw_map, uri, conf->log);
+
+            if(worker) {
+                r->handler=apr_pstrdup(r->pool,JK_HANDLER);
+                apr_table_setn(r->notes, JK_WORKER_ID, worker);
+
+                /* This could be a sub-request, possibly from mod_dir */
+                if(r->main)
+                    apr_table_setn(r->main->notes, JK_WORKER_ID, worker);
+
+            }
+        }
+    }
+
+    if (apr_table_get(r->notes, JK_WORKER_ID)) {
+
+        /* First find just the name of the file, no directory */
+        r->filename = (char *)apr_filepath_name_get(r->uri);
+
+        /* Only if sub-request for a directory, most likely from mod_dir */
+        if (r->main && r->main->filename &&
+            !apr_filepath_name_get(r->main->filename)){
+    
+            /* The filename from the main request will be set to what should
+             * be picked up, aliases included. Tomcat will need to know about
+             * those aliases or things won't work for them. Normal files should
+             * be fine. */
+
+            /* Need absolute path to stat */
+            if (apr_filepath_merge(&r->filename,
+                                   r->main->filename, r->filename,
+                                   APR_FILEPATH_SECUREROOT |
+                                   APR_FILEPATH_TRUENAME,
+                                   r->pool)
+                != APR_SUCCESS){
+              return DECLINED; /* We should never get here, very bad */
+            }
+
+            /* Stat the file so that mod_dir knows it's there */
+            apr_stat(&r->finfo, r->filename, APR_FINFO_TYPE, r->pool);
+        }
+
+        return OK;
+    }
+    return DECLINED;
+}
+#endif
+
+static void jk_register_hooks(apr_pool_t *p)
+{
+    ap_hook_handler(jk_handler,NULL,NULL,APR_HOOK_MIDDLE);
+    ap_hook_post_config(jk_post_config,NULL,NULL,APR_HOOK_MIDDLE);
+    ap_hook_child_init(jk_child_init,NULL,NULL,APR_HOOK_MIDDLE);
+    ap_hook_translate_name(jk_translate,NULL,NULL,APR_HOOK_MIDDLE);
+#if (MODULE_MAGIC_NUMBER_MAJOR > 20010808)
+    ap_hook_map_to_storage(jk_map_to_storage,NULL,NULL,APR_HOOK_MIDDLE);
+#endif
+}
+
+module AP_MODULE_DECLARE_DATA jk_module =
+{
+    STANDARD20_MODULE_STUFF,
+    NULL,                /* dir config creater */
+    NULL,                /* dir merger --- default is to override */
+    create_jk_config,    /* server config */
+    merge_jk_config,     /* merge server config */
+    jk_cmds,             /* command ap_table_t */
+    jk_register_hooks    /* register hooks */
+};
+
diff --git a/connectors/jk/native/apache-2.0/mod_jk.dsp b/connectors/jk/native/apache-2.0/mod_jk.dsp
new file mode 100644
index 0000000..0b1bf11
--- /dev/null
+++ b/connectors/jk/native/apache-2.0/mod_jk.dsp
@@ -0,0 +1,271 @@
+# Microsoft Developer Studio Project File - Name="apache" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=apache - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_jk.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_jk.mak" CFG="apache - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "apache - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "apache - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "apache - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /I "..\common" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "$(APACHE2_HOME)\include" /I "$(APACHE2_HOME)\srclib\apr\include" /I "$(APACHE2_HOME)\srclib\apr-util\include" /Fd"Release\mod_jk" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib advapi32.lib /nologo /dll /machine:I386
+# ADD LINK32 libhttpd.lib libapr.lib libaprutil.lib kernel32.lib user32.lib advapi32.lib wsock32.lib /nologo /dll /machine:I386 /libpath:"$(APACHE2_HOME)\Release" /libpath:"$(APACHE2_HOME)\srclib\apr\Release" /libpath:"$(APACHE2_HOME)\srclib\apr-util\Release" /libpath:"$(APACHE2_HOME)\lib"
+
+!ELSEIF  "$(CFG)" == "apache - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /I "..\common" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "$(APACHE2_HOME)\include" /I "$(APACHE2_HOME)\srclib\apr\include" /I "$(APACHE2_HOME)\srclib\apr-util\include" /Fd"Debug\mod_jk" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 libhttpd.lib libapr.lib libaprutil.lib kernel32.lib user32.lib advapi32.lib wsock32.lib /nologo /dll /debug /machine:I386 /libpath:"$(APACHE2_HOME)/Debug" /libpath:"$(APACHE2_HOME)\srclib\apr\Debug" /libpath:"$(APACHE2_HOME)\srclib\apr-util\Debug" /libpath:"$(APACHE2_HOME)\lib"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "apache - Win32 Release"
+# Name "apache - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\common\jk_ajp12_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp_common.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_connect.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_context.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_jni_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_lb_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_msg_buff.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_sockbuf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_uri_worker_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_util.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_jk.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\common\jk_ajp12_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp23_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp_common.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_connect.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_context.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_global.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_jni_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_lb_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_msg_buff.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_mt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_service.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_sockbuf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_uri_worker_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_util.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/connectors/jk/native/build.xml b/connectors/jk/native/build.xml
new file mode 100644
index 0000000..67f7140
--- /dev/null
+++ b/connectors/jk/native/build.xml
@@ -0,0 +1,456 @@
+<?xml version="1.0" ?>
+
+<project name="jk_native" default="main" basedir=".">
+
+  <description>Build the native component of jk.</description>
+  
+  <property file="${user.home}/.ant.properties" />
+  <property file="${user.home}/build.properties" />
+  <property file="../build.properties" />
+  <property file="build.properties" />
+
+  <!-- Overriden if called from the main -->
+  <property name="jk.build" location="../build" /> 
+
+  <path id="cp.jkant" >
+    <pathelement location="${jk.build}/lib/jkant.jar"/>
+  </path>
+
+  <property name="native.dir" location="." />
+  <property name="apxs13" value="/opt/apache13/bin/apxs" />
+  <property name="apxs20" value="/opt/apache2/bin/apxs" />
+  
+  <property name="iplanet.home" location="/opt/iplanet" />
+  <property name="apache2.home" location="/opt/apache2" />
+  <property name="apache13.home" location="/opt/apache13" />
+  
+  <property name="apache2.include" location="${apache2.home}/include" />
+  <property name="apache13.include" location="${apache13.home}/include" />
+
+  <property name="so.debug" value="true" />
+  <property name="so.optimize" value="false" />
+  <property name="so.profile" value="false" />
+
+  <!-- ==================== Targets ==================== -->
+
+  <target name="main" depends="init,apache20,apache13,iis,netscape,jni">
+  </target>
+
+  <target name="init" >
+    <echo message="${user.home}" />
+    <taskdef resource="META-INF/ant.tasks" 
+	     classpathref="cp.jkant" />
+    <available property="apache13.detect" 
+               file="${apache13.home}" />
+    <available property="apache2.detect" 
+               file="${apache2.home}" />
+    <available property="iis.detect" 
+               file="${iis.home}" />
+    <available property="iplanet.detect" 
+               file="${iplanet.home}" />
+    <available property="HAVE_APR" 
+               file="${apr.include}/apr.h" />
+    <mkdir dir="${jk.build}/jk" />
+    <condition property="linux">
+       <equals arg1="${os.name}" arg2="Linux"/>
+    </condition>
+    <condition property="solaris">
+       <equals arg1="${os.name}" arg2="SunOS"/>
+    </condition>
+    <condition property="win32">
+      <os family="windows"/>
+    </condition>
+    <condition property="hpux">
+      <equals arg1="${os.name}" arg2="HP-UX"/>
+    </condition>
+    <!-- I believe they are using cross-compilation, so checking the os.name
+         doesn't help. We'll check if the NDK is installed instead -->
+    <condition property="netware">
+      <available file="novellndk.home" />
+    </condition>
+    <echo message="linux=${linux} solaris=${solaris} win32=${win32} hpux=${hpux} netware=${netware}"/>
+
+  </target>
+
+  <target name="jni" depends="init">
+    <mkdir dir="${jk.build}/jk/jni" />
+    <so sofile="jni_connect" 
+	buildDir="${jk.build}/jk/jni" 
+	optimize="${so.optimize}"
+	debug="${so.debug}"
+	profile="${so.profile}"
+	>
+      <jniConfig />
+      <src dir="${native.dir}">
+	<include name="jni/*.c" />
+	<include name="common/jk_map.c" />
+	<include name="common/jk_util.c" />
+	<include name="common/jk_pool.c" />
+	<include name="common/jk_logger.c" />
+        <include name="common/jk_nwmain.c" if="netware" />
+        <include name="common/apr/*.c" unless="HAVE_APR" />
+      </src>
+      <includes>
+	<include name="${native.dir}/common" />
+	<include name="${native.dir}/jni" />
+	<include name="${java.home}/../include" />
+	<include name="${build.compiler.base}/include" />
+        
+        <!-- Platform specific includes -->
+	<include name="${novellndk.home}/include/nlm" if="netware" />
+	<include name="${novellndk.home}/include" if="netware" />
+        <include name="${java.home}/../include/netware" if="netware" />
+        <include name="${java.home}/../include/solaris" if="solaris" />
+        <include name="${java.home}/../include/hp-ux" if="hpux" />        
+        <include name="${java.home}/../include/linux"  if="linux"/>
+        <include name="${java.home}/../include/win32" if="win32" unless="netware" />
+      </includes>
+      <depends>
+	<fileset dir="${native.dir}/common" includes="*.h" />
+      </depends>
+      
+      <!-- Platform-specific tags -->
+      <altSoFile value="jni_conn" if="netware" />
+      
+      <def name="N_PLAT_NLM" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="NETWARE" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="XP_NETWARE" if="netware"
+	   info="Building for NetWare platform" />
+           
+      <import fileName="${novellndk.home}/imports/clib.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/lib0.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/nlmlib.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/threads.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/socklib.imp" if="netware" />
+      <export fileName="${native.dir}/jni/jk_jnicb.exp" if="netware" />
+      <linkOpt value="-desc &quot;JNI Natives for Tomcat&quot;" if="netware" />
+      <linkOpt value="-screenname &quot;System Console&quot;" if="netware" />
+      <linkOpt value="-nlmversion 1,2,6" if="netware" />
+      <linkOpt value="-threadname &quot;JK_JNI Thread&quot;" if="netware" />
+      <linkOpt value="-stacksize 64000" if="netware" />
+      
+      <def name="HPUX11" if="hpux" />
+
+      <def name="WIN32" if="win32" unless="netware" />
+      <def name="DEBUG" if="win32.debug" unless="netware"  />
+      <def name="NDEBUG" if="win32.release" unless="netware"  />
+      <def name="_WINDOWS" if="win32" unless="netware" />
+      <def name="_MBCS" if="win32" unless="netware" />
+      <def name="_USRDLL" if="win32" unless="netware" />
+      <def name="JNI_CONNECT_EXPORTS" if="win32" unless="netware" />
+      <linkOpt value="/libpath:&quot;${build.compiler.base}/lib&quot;" if="win32" unless="netware" />
+    </so>
+  </target>
+
+  <target name="apache20" depends="init" if="apache2.detect">
+    <mkdir dir="${jk.build}/jk/apache2" />
+    <so sofile="mod_jk" 
+	buildDir="${jk.build}/jk/apache2"
+	optimize="${so.optimize}"
+	debug="${so.debug}"
+	taskDebug="0"
+	profile="${so.profile}"	>
+      <def name="_REENTRANT" />
+      <def name="CHANNEL" if="use.channel"
+           info="Use the new (experimental) channel interface" />
+      <def name="AJP12" if="use.ajp12"
+           info="Build the deprecated ajp12 worker" />
+      <def name="CHUNK_SIZE" value="4096" 
+	   info="Read/Write buffer size" />
+      <def name="REUSE_WORKER" 
+	   unless="option_no_reuse_worker"
+	   info="Reuse the worker endpoint, using per thread data" />
+      <def name="USE_APACHE_MD5" unless="netware"
+	   info="Use the MD5 implementation that is part of apache2" />
+      <def name="HPUX11" if="hpux" />
+      <apacheConfig apxs="${apxs20}" />
+      <jniConfig />
+      <src dir=".">
+	<include name="apache-2.0/mod_jk.c" />
+	<include name="common/**/*.c" />
+        <exclude name="common/ajp12/*.c" unless="use.ajp12" />
+        <exclude name="common/jk_nwmain.c" unless="netware" />
+      </src>
+      <includes>
+	<include name="${native.dir}/common" />
+	<include name="${apache2.include}" />
+	<include name="${java.home}/../include" />
+        
+        <!-- Platform specific includes -->
+        <include name="${java.home}/../include/win32" if="win32" unless="netware" />
+        <include name="${java.home}/../include/hp-ux" if="hpux" />        
+        <include name="${java.home}/../include/netware" if="netware" />
+        <include name="${java.home}/../include/linux"  if="linux"/>
+        <include name="${java.home}/../include/solaris" if="solaris" />
+	<include name="${novelllibc.home}/include" if="netware" />
+	<include name="${novelllibc.home}/include/winsock" if="netware" />
+      </includes>
+      <depends>
+	<fileset dir="${native.dir}/common" includes="*.h" />
+      </depends>
+
+      <!-- Platform-specific tags -->
+      <def name="WIN32" if="win32" unless="netware" />
+      <def name="DEBUG" if="win32.debug" unless="netware"  />
+      <def name="NDEBUG" if="win32.release" unless="netware"  />
+      <def name="_WINDOWS" if="win32" unless="netware" />
+      <def name="_MBCS" if="win32" unless="netware" />
+      <def name="_USRDLL" if="win32" unless="netware" />
+
+      <import fileName="libapr.lib" if="win32" unless="netware" />
+      <import fileName="libaprutil.lib" if="win32" unless="netware" />
+      <import fileName="libhttpd.lib" if="win32" unless="netware" />
+      <import fileName="wsock32.lib" if="win32" unless="netware" />
+      <linkOpt value="/libpath:&quot;${build.compiler.base}/lib&quot;" if="win32" unless="netware" />
+      <linkOpt value="/libpath:&quot;${apache2.home}/lib&quot;" if="win32" unless="netware" />
+      
+      <def name="N_PLAT_NLM" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="NETWARE" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="__NETWARE__" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="__NOVELL_LIBC__" if="netware"
+	   info="Building for NetWare platform with LibC libraries" />
+           
+      <nlmmodule value="Apache2" if="netware" />
+      <import fileName="${novelllibc.home}/imports/libc.imp" if="netware" />
+      <import fileName="${novelllibc.home}/imports/ws2nlm.imp" if="netware" />
+      <import fileName="${apache2.home}/lib/httpd.imp" if="netware" />
+      <import fileName="${apache2.home}/lib/aprlib.imp" if="netware" />
+      <export symbol="jk_module" if="netware" />
+      <linkOpt value="-desc &quot;Apache 2.0 plugin for Tomcat&quot;" if="netware" />
+      <linkOpt value="-nlmversion 1,2,6" if="netware" />
+      <linkOpt value="-threadname &quot;mod_jk Module&quot;" if="netware" />
+      <linkOpt value="-stacksize 8192" if="netware" />
+      <linkOpt value="-l ${novelllibc.home}/imports" if="netware" />
+      <linkOpt value="-flags AUTOUNLOAD, PSEUDOPREEMPTION" if="netware" />
+      <linkOpt value="-entry _LibCPrelude" if="netware" />
+      <linkOpt value="-exit _LibCPostlude" if="netware" />
+    </so>
+
+  </target>
+  
+
+  <target name="apache13" depends="init" if="apache13.detect">
+    <mkdir dir="${jk.build}/jk/apache13" />
+    <so sofile="mod_jk" 
+	buildDir="${jk.build}/jk/apache13"
+	optimize="${so.optimize}"
+	debug="${so.debug}"
+	profile="${so.profile}">
+      <apacheConfig apxs="${apxs13}" />
+
+      <src dir=".">
+	<include name="apache-1.3/mod_jk.c" />
+	<include name="common/*.c" />
+	<exclude name="common/jk_jni_worker.c" />
+        <exclude name="common/jk_nwmain.c" unless="netware" />
+        <include name="common/apr/*.c" unless="HAVE_APR" />
+      </src>
+      <includes>
+	<include name="${native.dir}/common" />
+	<include name="${build.compiler.base}/include" />
+	<include name="${apache13.include}" />
+	<include name="${java.home}/../include" />
+
+        <!-- Platform specific includes -->
+	<include name="${apache13.home}/include" if="netware" />
+	<include name="${apache13.home}/os/netware" if="netware" />
+	<include name="${novellndk.home}/include/nlm" if="netware" />
+	<include name="${novellndk.home}/include" if="netware" />
+	<include name="${novellndk.home}/include/winsock" if="netware" />
+        <include name="${java.home}/../include/netware" if="netware" />
+                 
+	<include name="${apache13.home}/os/win32" if="win32" unless="netware" />
+        <include name="${java.home}/../include/win32" if="win32" unless="netware" />
+        <include name="${java.home}/../include/hp-ux" if="hpux" />
+        <include name="${native.dir}/common" if="win32" unless="netware" />
+      </includes>
+      <depends>
+	<fileset dir="${native.dir}/common" includes="*.h" />
+      </depends>
+
+      <!-- Platform-specific tags -->
+      <def name="N_PLAT_NLM" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="NETWARE" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="XP_NETWARE" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="USE_SPRINTF" if="netware"
+	   info="Use the sprintf function to build strings" />
+           
+      <def name="HPUX11" if="hpux" />
+
+      <def name="WIN32" if="win32" unless="netware" />
+      <def name="DEBUG" if="win32.debug" unless="netware"  />
+      <def name="NDEBUG" if="win32.release" unless="netware"  />
+      <def name="_WINDOWS" if="win32" unless="netware" />
+      <def name="_MBCS" if="win32" unless="netware" />
+      <def name="_USRDLL" if="win32" unless="netware" />
+      <def name="MOD_JK_EXPORTS" if="win32" unless="netware" />
+      
+      <nlmmodule value="apache" if="netware" />
+      <import fileName="${novellndk.home}/imports/clib.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/lib0.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/nlmlib.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/threads.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/socklib.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/ws2nlm.imp" if="netware" />
+      <import fileName="${apache13.home}/os/netware/ApacheCore.imp" if="netware" />
+      <export symbol="jk_module" if="netware" />
+      <linkOpt value="-desc &quot;Apache 1.3 plugin for Tomcat&quot;" if="netware" />
+      <linkOpt value="-screenname &quot;System Console&quot;" if="netware" />
+      <linkOpt value="-nlmversion 1,2,6" if="netware" />
+      <linkOpt value="-threadname &quot;mod_jk Thread&quot;" if="netware" />
+      <linkOpt value="-stacksize 64000" if="netware" />
+      
+      <import fileName="ApacheCore.lib" if="win32" unless="netware" />
+      <import fileName="wsock32.lib" if="win32" unless="netware" />
+      <linkOpt value="/libpath:&quot;${build.compiler.base}/lib&quot;" if="win32" unless="netware" />
+      <linkOpt value="/libpath:&quot;${apache13.home}/CoreR&quot;" if="win32" unless="netware" />
+      <linkOpt value="/libpath:&quot;${apache13.home}/Release&quot;" if="win32" unless="netware" />
+      <linkOpt value="/libpath:&quot;${apache13.home}/libexec&quot;" if="win32" unless="netware" />
+    </so>
+  </target>
+
+  <target name="iis" depends="init" if="iis.detect">
+    <mkdir dir="${jk.build}/jk/iis" />
+    <so sofile="isapi_redirector" 
+        buildDir="${jk.build}/jk/iis"
+        optimize="${so.optimize}"
+        debug="${so.debug}"
+        profile="${so.profile}">
+        
+      <src dir=".">
+        <include name="iis/jk_isapi_plugin.c" />
+        <include name="common/*.c" />
+        <exclude name="common/jk_nwmain.c" unless="netware" />
+        <include name="common/apr/*.c" unless="HAVE_APR" />
+      </src>
+      <includes>
+        <include name="${java.home}/../include" />
+        <include name="${java.home}/../include/win32" />
+        <include name="${native.dir}/common" />
+        <include name="${mssdk.include}" />
+        <include name="${build.compiler.base}/include" />
+      </includes>
+      <depends>
+        <fileset dir="${native.dir}/common" includes="*.h" />
+      </depends>
+        
+      <!-- Platform-specific tags -->
+      <def name="WIN32" />
+      <def name="DEBUG" if="win32.debug"  />
+      <def name="NDEBUG" if="win32.release"  />
+      <def name="_WINDOWS"  />
+      <def name="_MBCS" />
+      <def name="_USRDLL" />
+      <def name="ISAPI_EXPORTS" />
+      <import fileName="advapi32.lib" />
+      <import fileName="wsock32.lib" />
+      <export symbol="HttpFilterProc"/>
+      <export symbol="GetFilterVersion"/>
+      <export symbol="GetExtensionVersion"/>
+      <export symbol="HttpExtensionProc"/>
+      <export symbol="TerminateFilter"/>
+      <export symbol="TerminateExtension"/>
+      <linkOpt value="/libpath:&quot;${mssdk.lib}&quot;" />
+      <linkOpt value="/libpath:&quot;${build.compiler.base}/lib&quot;" />
+    </so>
+  </target>
+
+  <target name="netscape" depends="init" if="iplanet.detect">
+    <mkdir dir="${jk.build}/jk/netscape" />
+    <property name="netscape.home" value="${iplanet.home}/plugins" />
+    <available property="unix" file="/etc/passwd" />
+    <so sofile="nsapi_redirector" 
+	buildDir="${jk.build}/jk/netscape"
+	optimize="${so.optimize}"
+	debug="${so.debug}"
+	profile="${so.profile}">
+
+      <src dir=".">
+	<include name="netscape/jk_nsapi_plugin.c" />
+	<include name="common/*.c" />
+        <exclude name="common/jk_nwmain.c" unless="netware" />
+        <include name="common/apr/*.c" unless="HAVE_APR" />
+      </src>
+      <includes>
+	<include name="${native.dir}/common" />
+	<include name="${build.compiler.base}/include" />
+	<include name="${netscape.home}/include" />
+	<include name="${java.home}/../include" />
+
+        <!-- Platform specific includes -->
+        
+	<include name="${novellndk.home}/include/nlm" if="netware" />
+	<include name="${novellndk.home}/include" if="netware" />
+        <include name="${java.home}/../include/linux" if="linux" />
+        <include name="${java.home}/../include/netware" if="netware" />
+                 
+	<include name="${apache13.home}/os/win32" if="win32" unless="netware" />
+        <include name="${java.home}/../include/win32" if="win32" unless="netware" />
+        <include name="${native.dir}/common" if="win32" unless="netware" />
+      </includes>
+      <depends>
+	<fileset dir="${native.dir}/common" includes="*.h" />
+      </depends>
+
+      <!-- Platform-specific tags -->
+      <altSoFile value="nsapi_rd" if="netware" />
+
+      <def name="XP_UNIX" if="unix"
+	   info="Building for unix platform" />
+      
+      <def name="N_PLAT_NLM" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="NETWARE" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="XP_NETWARE" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="USE_SPRINTF" if="netware"
+	   info="Use the sprintf function to build strings" />
+           
+      <def name="WIN32" if="win32" unless="netware" />
+      <def name="DEBUG" if="win32.debug" unless="netware"  />
+      <def name="NDEBUG" if="win32.release" unless="netware"  />
+      <def name="_WINDOWS" if="win32" unless="netware" />
+      <def name="_MBCS" if="win32" unless="netware" />
+      <def name="_USRDLL" if="win32" unless="netware" />
+      <def name="XP_WIN32" if="win32" unless="netware" />
+      <def name="NSAPI_EXPORTS" if="win32" unless="netware" />
+      
+      <def name="XP_UNIX" if="unix"
+	   info="Unix platform - needed for nsapi.h" />
+      
+      <nlmmodule value="nshttpd" if="netware" />
+      <import fileName="${novellndk.home}/imports/clib.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/lib0.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/nlmlib.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/threads.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/socklib.imp" if="netware" />
+      <import fileName="${netscape.home}/imports/nsapi.imp" if="netware" />
+      <export symbol="jk_init" if="netware" />
+      <export symbol="jk_service" if="netware" />
+      <linkOpt value="-desc &quot;Netscape Plugin for Tomcat&quot;" if="netware" />
+      <linkOpt value="-screenname &quot;System Console&quot;" if="netware" />
+      <linkOpt value="-nlmversion 1,2,6" if="netware" />
+      <linkOpt value="-threadname &quot;NSTomcat Thread&quot;" if="netware" />
+      <linkOpt value="-stacksize 64000" if="netware" />
+      
+      <import fileName="ns-httpd36.lib" if="win32" unless="netware" />
+      <import fileName="wsock32.lib" if="win32" unless="netware" />
+      <linkOpt value="/libpath:&quot;${build.compiler.base}/lib&quot;" if="win32" unless="netware" />
+      <linkOpt value="/libpath:&quot;${netscape.home}/lib&quot;" if="win32" unless="netware" />
+    </so>
+  </target>
+
+  <target name="clean" >
+    <delete dir="${jk.build}/jk"/>
+  </target>
+
+</project>
diff --git a/connectors/jk/native/buildconf.sh b/connectors/jk/native/buildconf.sh
new file mode 100755
index 0000000..03fab91
--- /dev/null
+++ b/connectors/jk/native/buildconf.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+echo "rm autom4te.cache"
+rm -rf autom4te.cache
+
+echo "libtoolize --force --automake --copy"
+libtoolize --force --automake --copy
+echo "aclocal"
+#aclocal --acdir=`aclocal --print-ac-dir`
+#aclocal --acdir=/usr/local/share/aclocal
+aclocal
+echo "autoheader"
+autoheader
+echo "automake -a --foreign --copy"
+automake -a --foreign --copy
+echo "autoconf"
+autoconf
+
+echo "rm autom4te.cache"
+rm -rf autom4te.cache
diff --git a/connectors/jk/native/common/.cvsignore b/connectors/jk/native/common/.cvsignore
new file mode 100644
index 0000000..b1b7696
--- /dev/null
+++ b/connectors/jk/native/common/.cvsignore
@@ -0,0 +1,8 @@
+*.o
+*.lo
+*.slo
+.libs
+Makefile
+stamp-h.in
+list.mk
+stamp-h
diff --git a/connectors/jk/native/common/Makefile.in b/connectors/jk/native/common/Makefile.in
new file mode 100644
index 0000000..7692677
--- /dev/null
+++ b/connectors/jk/native/common/Makefile.in
@@ -0,0 +1,31 @@
+#### XXXX DO we need this Makefile ????
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+APXSLDFLAGS=@APXSLDFLAGS@
+APXSCFLAGS=@APXSCFLAGS@
+APXSCPPFLAGS=@APXSCPPFLAGS@
+
+top_builddir = ..
+
+LIBTOOL = @LIBTOOL@
+CC = @CC@
+
+OEXT=.lo
+include list.mk
+
+JAVA_INCL=-I @JAVA_HOME@/include -I @JAVA_HOME@/include/@OS@
+CFLAGS=@apache_include@ @CFLAGS@ ${APXSCFLAGS} ${APXSCPPFLAGS} ${JAVA_INCL}
+
+include @top_srcdir@/scripts/build/rules.mk
+
+JK=./
+
+all: ${APACHE_OBJECTS}
+
+install:
+
+clean:
+	rm -f *.o *.slo *.lo
diff --git a/connectors/jk/native/common/jk_ajp12_worker.c b/connectors/jk/native/common/jk_ajp12_worker.c
new file mode 100644
index 0000000..7373d49
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp12_worker.c
@@ -0,0 +1,635 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: ajpv1.2 worker, used to call local or remote jserv hosts   *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Based on:    jserv_ajpv12.c from Jserv                                  *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#include "jk_ajp12_worker.h"
+#include "jk_pool.h"
+#include "jk_connect.h"
+#include "jk_util.h"
+#include "jk_sockbuf.h"
+#ifdef AS400
+#include "util_ebcdic.h"
+#include <string.h>
+#endif
+
+#define AJP_DEF_HOST            ("localhost")
+#define AJP_DEF_PORT            (8007)
+#define READ_BUF_SIZE           (8*1024)
+#define DEF_RETRY_ATTEMPTS      (1)
+
+struct ajp12_worker {
+    struct sockaddr_in worker_inet_addr;
+    unsigned connect_retry_attempts;
+    char *name; 
+    jk_worker_t worker;
+};
+
+typedef struct ajp12_worker ajp12_worker_t;
+
+struct ajp12_endpoint { 
+    ajp12_worker_t *worker;
+    
+    int sd;
+    jk_sockbuf_t sb;
+
+    jk_endpoint_t endpoint;
+};
+typedef struct ajp12_endpoint ajp12_endpoint_t;
+
+static int ajpv12_mark(ajp12_endpoint_t *p, 
+                       unsigned char type);
+
+#ifdef AS400
+static int ajpv12_sendasciistring(ajp12_endpoint_t *p, 
+                             char *buffer);
+#endif
+
+#ifdef AS400
+static int ajpv12_sendstring(ajp12_endpoint_t *p, 
+                             char *buffer);
+#else
+static int ajpv12_sendstring(ajp12_endpoint_t *p, 
+                             const char *buffer);
+#endif
+
+static int ajpv12_sendint(ajp12_endpoint_t *p, 
+                          int d);
+
+static int ajpv12_sendnbytes(ajp12_endpoint_t *p, 
+                             const void *buffer, 
+                             int bufferlen);
+
+static int ajpv12_flush(ajp12_endpoint_t *p);
+
+static int ajpv12_handle_response(ajp12_endpoint_t *p, 
+                                  jk_ws_service_t *s,
+                                  jk_logger_t *l);
+
+static int ajpv12_handle_request(ajp12_endpoint_t *p, 
+                                 jk_ws_service_t *s,
+                                 jk_logger_t *l);
+
+static int JK_METHOD service(jk_endpoint_t *e, 
+                             jk_ws_service_t *s,
+                             jk_logger_t *l,
+                             int *is_recoverable_error)
+{
+    jk_log(l, JK_LOG_DEBUG, "Into jk_endpoint_t::service\n");
+
+    if(e && e->endpoint_private && s && is_recoverable_error) {
+        ajp12_endpoint_t *p = e->endpoint_private;
+        unsigned attempt;
+
+        *is_recoverable_error = JK_TRUE;
+
+        for(attempt = 0 ; attempt < p->worker->connect_retry_attempts ; attempt++) {
+            p->sd = jk_open_socket(&p->worker->worker_inet_addr, 
+                                   JK_TRUE, 
+                                   JK_FALSE,
+                                   l);
+
+            jk_log(l, JK_LOG_DEBUG, "In jk_endpoint_t::service, sd = %d\n", p->sd);
+            if(p->sd >= 0) {
+                break;
+            }
+        }
+        if(p->sd >= 0) {
+
+            /*
+             * After we are connected, each error that we are going to
+             * have is probably unrecoverable
+             */
+            *is_recoverable_error = JK_FALSE;
+            jk_sb_open(&p->sb, p->sd);
+            if(ajpv12_handle_request(p, s, l)) {
+                jk_log(l, JK_LOG_DEBUG, "In jk_endpoint_t::service, sent request\n");
+                return ajpv12_handle_response(p, s, l);
+            }                        
+        }
+        jk_log(l, JK_LOG_ERROR, "In jk_endpoint_t::service, Error sd = %d\n", p->sd);
+    } else {
+        jk_log(l, JK_LOG_ERROR, "In jk_endpoint_t::service, NULL parameters\n");
+    }
+
+    return JK_FALSE;
+}
+
+static int JK_METHOD done(jk_endpoint_t **e,
+                          jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, "Into jk_endpoint_t::done\n");
+    if(e && *e && (*e)->endpoint_private) {
+        ajp12_endpoint_t *p = (*e)->endpoint_private;
+        if(p->sd > 0) {
+            jk_close_socket(p->sd);
+        }
+        free(p);
+        *e = NULL;
+        return JK_TRUE;
+    }
+
+    jk_log(l, JK_LOG_ERROR, "In jk_endpoint_t::done, NULL parameters\n");
+    return JK_FALSE;
+}
+
+static int JK_METHOD validate(jk_worker_t *pThis,
+                              jk_map_t *props,                            
+                              jk_worker_env_t *we,
+                              jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, "Into jk_worker_t::validate\n");
+
+    if(pThis && pThis->worker_private) {        
+        ajp12_worker_t *p = pThis->worker_private;
+        int port = jk_get_worker_port(props, 
+                                      p->name,
+                                      AJP_DEF_PORT);
+
+        char *host = jk_get_worker_host(props, 
+                                        p->name,
+                                        AJP_DEF_HOST);
+
+        jk_log(l, JK_LOG_DEBUG, "In jk_worker_t::validate for worker %s contact is %s:%d\n", 
+               p->name, host, port);
+
+        if(port > 1024 && host) {
+            if(jk_resolve(host, port, &p->worker_inet_addr)) {
+                return JK_TRUE;
+            }
+            jk_log(l, JK_LOG_ERROR, "In jk_worker_t::validate, resolve failed\n");
+        }
+        jk_log(l, JK_LOG_ERROR, "In jk_worker_t::validate, Error %s %d\n", host, port);
+    } else {
+        jk_log(l, JK_LOG_ERROR, "In jk_worker_t::validate, NULL parameters\n");
+    }
+
+    return JK_FALSE;
+}
+
+static int JK_METHOD init(jk_worker_t *pThis,
+                          jk_map_t *props, 
+                          jk_worker_env_t *we,
+                          jk_logger_t *log)
+{
+    /* Nothing to do for now */
+    return JK_TRUE;
+}
+
+static int JK_METHOD get_endpoint(jk_worker_t *pThis,
+                                  jk_endpoint_t **pend,
+                                  jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, "Into jk_worker_t::get_endpoint\n");
+
+    if(pThis && pThis->worker_private && pend) {        
+        ajp12_endpoint_t *p = (ajp12_endpoint_t *)malloc(sizeof(ajp12_endpoint_t));
+        if(p) {
+            p->sd = -1;         
+            p->worker = pThis->worker_private;
+            p->endpoint.endpoint_private = p;
+            p->endpoint.service = service;
+            p->endpoint.done = done;
+            *pend = &p->endpoint;
+            return JK_TRUE;
+        }
+        jk_log(l, JK_LOG_ERROR, "In jk_worker_t::get_endpoint, malloc failed\n");
+    } else {
+        jk_log(l, JK_LOG_ERROR, "In jk_worker_t::get_endpoint, NULL parameters\n");
+    }
+
+    return JK_FALSE;
+}
+
+static int JK_METHOD destroy(jk_worker_t **pThis,
+                             jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, "Into jk_worker_t::destroy\n");
+    if(pThis && *pThis && (*pThis)->worker_private) {
+        ajp12_worker_t *private_data = (*pThis)->worker_private;
+        free(private_data->name);
+        free(private_data);
+
+        return JK_TRUE;
+    }
+
+    jk_log(l, JK_LOG_ERROR, "In jk_worker_t::destroy, NULL parameters\n");
+    return JK_FALSE;
+}
+
+int JK_METHOD ajp12_worker_factory(jk_worker_t **w,
+                                   const char *name,
+                                   jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, "Into ajp12_worker_factory\n");
+    if(NULL != name && NULL != w) {
+        ajp12_worker_t *private_data = 
+            (ajp12_worker_t *)malloc(sizeof(ajp12_worker_t));
+
+        if(private_data) {
+            private_data->name = strdup(name);
+
+            if(private_data->name) {
+                private_data->connect_retry_attempts= DEF_RETRY_ATTEMPTS;
+                private_data->worker.worker_private = private_data;
+
+                private_data->worker.validate       = validate;
+                private_data->worker.init           = init;
+                private_data->worker.get_endpoint   = get_endpoint;
+                private_data->worker.destroy        = destroy;
+
+                *w = &private_data->worker;
+                return JK_TRUE;
+            }
+
+            free(private_data);
+        } 
+        jk_log(l, JK_LOG_ERROR, "In ajp12_worker_factory, malloc failed\n");
+    } else {
+        jk_log(l, JK_LOG_ERROR, "In ajp12_worker_factory, NULL parameters\n");
+    }
+
+    return JK_FALSE;
+}
+
+static int ajpv12_sendnbytes(ajp12_endpoint_t *p, 
+                             const void *buffer, 
+                             int bufferlen) 
+{
+    unsigned char bytes[2];
+    static const unsigned char null_b[2] = { (unsigned char)0xff, (unsigned char)0xff };
+
+    if(buffer) {
+        bytes[0] = (unsigned char) ( (bufferlen >> 8) & 0xff );
+        bytes[1] = (unsigned char) ( bufferlen & 0xff );
+
+        if(jk_sb_write(&p->sb, bytes, 2)) {
+            return jk_sb_write(&p->sb, buffer, bufferlen);
+        } else {
+            return JK_FALSE;
+        }
+    } else {
+        return jk_sb_write(&p->sb, null_b, 2);
+    }
+}
+
+#ifdef AS400
+static int ajpv12_sendasciistring(ajp12_endpoint_t *p, 
+                             char *buffer)  
+{
+    int bufferlen;
+
+    if(buffer && (bufferlen = strlen(buffer))) {
+	return ajpv12_sendnbytes(p, buffer, bufferlen);
+    } else {
+        return ajpv12_sendnbytes(p, NULL, 0);
+    }
+}
+#endif
+
+#ifdef AS400
+static int ajpv12_sendstring(ajp12_endpoint_t *p, 
+                             char *buffer) 
+#else
+static int ajpv12_sendstring(ajp12_endpoint_t *p, 
+                             const char *buffer) 
+#endif
+{
+    int bufferlen;
+
+    if(buffer && (bufferlen = strlen(buffer))) {
+#if defined(AS400) || defined(_OSD_POSIX)
+        jk_xlate_to_ascii(buffer, bufferlen);
+#endif  
+	return ajpv12_sendnbytes(p, buffer, bufferlen);
+    } else {
+        return ajpv12_sendnbytes(p, NULL, 0);
+    }
+}
+
+static int ajpv12_mark(ajp12_endpoint_t *p, 
+                       unsigned char type) 
+{
+    if(jk_sb_write(&p->sb, &type, 1)) {
+        return JK_TRUE;
+    } else {
+        return JK_FALSE;
+    }
+}
+
+static int ajpv12_sendint(ajp12_endpoint_t *p, 
+                          int d)
+{
+    char buf[20];
+    sprintf(buf, "%d", d);
+    return ajpv12_sendstring(p, buf);
+}
+
+static int ajpv12_flush(ajp12_endpoint_t *p)
+{
+    return jk_sb_flush(&p->sb);
+}
+
+static int ajpv12_handle_request(ajp12_endpoint_t *p, 
+                                 jk_ws_service_t *s,
+                                 jk_logger_t *l) 
+{
+    int ret;
+
+    jk_log(l, JK_LOG_DEBUG, "Into ajpv12_handle_request\n");
+    /*
+     * Start the ajp 12 service sequence
+     */
+    jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_request, sending the ajp12 start sequence\n");
+    
+    ret = (ajpv12_mark(p, 1) &&
+           ajpv12_sendstring(p, s->method) &&
+           ajpv12_sendstring(p, 0) &&   /* zone */
+           ajpv12_sendstring(p, 0) &&   /* servlet */
+           ajpv12_sendstring(p, s->server_name) &&
+           ajpv12_sendstring(p, 0) &&   /* doc root */
+           ajpv12_sendstring(p, 0) &&   /* path info */
+           ajpv12_sendstring(p, 0) &&   /* path translated */
+#ifdef AS400
+           ajpv12_sendasciistring(p, s->query_string) &&
+#else
+           ajpv12_sendstring(p, s->query_string)&&
+#endif
+           ajpv12_sendstring(p, s->remote_addr) &&
+           ajpv12_sendstring(p, s->remote_host) &&
+           ajpv12_sendstring(p, s->remote_user) &&
+           ajpv12_sendstring(p, s->auth_type)   &&
+           ajpv12_sendint(p, s->server_port)    &&
+#ifdef AS400
+           ajpv12_sendasciistring(p, s->method) &&
+#else
+           ajpv12_sendstring(p, s->method)      &&
+#endif
+           ajpv12_sendstring(p, s->req_uri)     &&
+           ajpv12_sendstring(p, 0)              && /* */
+           ajpv12_sendstring(p, 0)              && /* SCRIPT_NAME */
+#ifdef AS400
+           ajpv12_sendasciistring(p, s->server_name) &&
+#else
+           ajpv12_sendstring(p, s->server_name) &&
+#endif
+           ajpv12_sendint(p, s->server_port)    &&
+           ajpv12_sendstring(p, s->protocol)    &&
+           ajpv12_sendstring(p, 0)              && /* SERVER_SIGNATURE */ 
+           ajpv12_sendstring(p, s->server_software) &&
+           ajpv12_sendstring(p, s->jvm_route)   && /* JSERV_ROUTE */
+           ajpv12_sendstring(p, "")             && /* JSERV ajpv12 compatibility */
+           ajpv12_sendstring(p, ""));              /* JSERV ajpv12 compatibility */
+
+    if(!ret) {
+        jk_log(l, JK_LOG_ERROR, 
+              "In ajpv12_handle_request, failed to send the ajp12 start sequence\n");
+        return JK_FALSE;
+    }
+
+    if(s->num_attributes > 0) {
+        unsigned  i;
+        jk_log(l, JK_LOG_DEBUG, 
+               "ajpv12_handle_request, sending the environment variables\n");
+    
+        for(i = 0 ; i < s->num_attributes ; i++) {
+            ret = (ajpv12_mark(p, 5)                            &&
+                   ajpv12_sendstring(p, s->attributes_names[i]) &&
+                   ajpv12_sendstring(p, s->attributes_values[i]));
+            if(!ret) {
+                jk_log(l, JK_LOG_ERROR, 
+                       "In ajpv12_handle_request, failed to send environment\n");
+                return JK_FALSE;
+            }
+        }
+    }
+
+    jk_log(l, JK_LOG_DEBUG, 
+           "ajpv12_handle_request, sending the headers\n");
+
+    /* Send the request headers */
+    if(s->num_headers) {
+        unsigned  i;
+        for(i = 0 ; i < s->num_headers ; ++i) {
+            ret = (ajpv12_mark(p, 3) &&
+                   ajpv12_sendstring(p, s->headers_names[i]) &&
+                   ajpv12_sendstring(p, s->headers_values[i]) );
+
+            if(!ret) {
+                jk_log(l, JK_LOG_ERROR, 
+                       "In ajpv12_handle_request, failed to send headers\n");
+                return JK_FALSE;
+            }
+        }
+    }
+
+    jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_request, sending the terminating mark\n");
+
+    ret =  (ajpv12_mark(p, 4) && ajpv12_flush(p));
+    if(!ret) {
+        jk_log(l, JK_LOG_ERROR, 
+               "In ajpv12_handle_request, failed to send the terminating mark\n");
+        return JK_FALSE;
+    }
+
+    if(s->content_length) {
+        char buf[READ_BUF_SIZE];
+        unsigned so_far = 0;
+
+        jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_request, sending the request body\n");
+
+        while(so_far < s->content_length) {
+            unsigned this_time = 0;
+            unsigned to_read = s->content_length - so_far;
+            if(to_read > READ_BUF_SIZE) {
+                to_read = READ_BUF_SIZE;
+            }
+
+            if(!s->read(s, buf, to_read, &this_time)) {
+                jk_log(l, JK_LOG_ERROR, 
+                       "In ajpv12_handle_request, failed to read from the web server\n");
+                return JK_FALSE;
+            }
+            jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_request, read %d bytes\n", this_time);
+            if(this_time > 0) {
+                so_far += this_time;
+                if((int)this_time != send(p->sd, buf, this_time, 0)) {
+                    jk_log(l, JK_LOG_ERROR, 
+                           "In ajpv12_handle_request, failed to write to the container\n");
+                    return JK_FALSE;
+                }
+                jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_request, sent %d bytes\n", this_time);
+            } else if (this_time == 0) {
+             jk_log(l, JK_LOG_ERROR,
+                     "In ajpv12_handle_request, Error: short read. content length is %d, read %d\n",
+                     s->content_length, so_far);
+             return JK_FALSE;
+            }
+        }
+    }
+
+    jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_request done\n");
+    return JK_TRUE;
+}
+
+static int ajpv12_handle_response(ajp12_endpoint_t *p, 
+                                  jk_ws_service_t *s,
+                                  jk_logger_t *l) 
+{
+    int status      = 200;
+    char *reason    = NULL;
+    char **names    = NULL;
+    char **values   = NULL;
+    int  headers_capacity = 0;
+    int  headers_len = 0;
+    int  write_to_ws;
+
+    jk_log(l, JK_LOG_DEBUG, "Into ajpv12_handle_response\n");
+    /*
+     * Read headers ...
+     */
+    while(1) {
+        char *line  = NULL;
+        char *name  = NULL;
+        char *value = NULL;
+#if defined(AS400) || defined(_REENTRANT)
+        char *lasts;
+#endif
+
+        if(!jk_sb_gets(&p->sb, &line)) {
+            jk_log(l, JK_LOG_ERROR, "ajpv12_handle_response, error reading header line\n");
+            return JK_FALSE;
+        }
+#if defined(AS400) || defined(_OSD_POSIX)
+        jk_xlate_from_ascii(line, strlen(line));
+#endif
+        
+        jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_response, read %s\n", line);
+        if(0 == strlen(line)) {
+            jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_response, headers are done\n");
+            break;      /* Empty line -> end of headers */
+        }
+
+        name = line;
+        while(isspace(*name) && *name) {
+            name++;     /* Skip leading white chars */
+        }
+        if(!*name) {    /* Empty header name */
+            jk_log(l, JK_LOG_ERROR, "ajpv12_handle_response, empty header name\n");
+            return JK_FALSE;
+        }
+        if(!(value = strchr(name, ':'))) { 
+            jk_log(l, JK_LOG_ERROR, "ajpv12_handle_response, no value supplied\n");
+            return JK_FALSE; /* No value !!! */
+        }
+        *value = '\0';
+        value++;
+        while(isspace(*value) && *value) { 
+            value++;    /* Skip leading white chars */
+        }
+        if(!*value) {   /* Empty header value */
+            jk_log(l, JK_LOG_ERROR, "ajpv12_handle_response, empty header value\n");
+            return JK_FALSE;
+        }
+
+        jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_response, read %s=%s\n", name, value);
+        if(0 == strcmp("Status", name)) {
+#if defined(AS400) || defined(_REENTRANT)
+            char *numeric = strtok_r(value, " \t", &lasts);
+#else
+            char *numeric = strtok(value, " \t");
+#endif
+
+            status = atoi(numeric);
+            if(status < 100 || status > 999) {
+                jk_log(l, JK_LOG_ERROR, "ajpv12_handle_response, invalid status code\n");
+                return JK_FALSE;
+            }
+#if defined(AS400) || defined(_REENTRANT)
+            reason = jk_pool_strdup(s->pool, strtok_r(NULL, " \t", &lasts));
+#else 
+            reason = jk_pool_strdup(s->pool, strtok(NULL, " \t"));
+#endif
+        } else {
+            if(headers_capacity == headers_len) {
+                jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_response, allocating header arrays\n");
+                names = (char **)jk_pool_realloc(s->pool,  
+                                                 sizeof(char *) * (headers_capacity + 5),
+                                                 names,
+                                                 sizeof(char *) * headers_capacity);
+                values = (char **)jk_pool_realloc(s->pool,  
+                                                  sizeof(char *) * (headers_capacity + 5),
+                                                  values,
+                                                  sizeof(char *) * headers_capacity);
+                if(!values || !names) {
+                    jk_log(l, JK_LOG_ERROR, "ajpv12_handle_response, malloc error\n");
+                    return JK_FALSE;
+                }
+                headers_capacity = headers_capacity + 5;
+            }
+            names[headers_len] = jk_pool_strdup(s->pool, name);
+            values[headers_len] = jk_pool_strdup(s->pool, value);
+            headers_len++;
+        }
+    }
+
+    jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_response, starting response\n");
+    if(!s->start_response(s, 
+                          status, 
+                          reason, 
+                          (const char * const *)names, 
+                          (const char * const *)values, 
+                          headers_len)) {
+        jk_log(l, JK_LOG_ERROR, "ajpv12_handle_response, error starting response\n");
+        return JK_FALSE;
+    }
+
+    jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_response, reading response body\n");
+    /*
+     * Read response body
+     */
+    write_to_ws = JK_TRUE;
+    while(1) {
+        unsigned to_read = READ_BUF_SIZE;
+        unsigned acc = 0;
+        char *buf = NULL;
+    
+        if(!jk_sb_read(&p->sb, &buf, to_read, &acc)) {
+            jk_log(l, JK_LOG_ERROR, "ajpv12_handle_response, error reading from \n");
+            return JK_FALSE;
+        }
+
+        if(!acc) {
+            jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_response, response body is done\n");
+            break;
+        }
+
+        if(write_to_ws) {
+            if(!s->write(s, buf, acc)) {
+                jk_log(l, JK_LOG_ERROR, "ajpv12_handle_response, error writing back to server\n");
+                write_to_ws = JK_FALSE;
+            }
+        }
+    }
+
+    jk_log(l, JK_LOG_DEBUG, "ajpv12_handle_response done\n");
+    return JK_TRUE;
+}
diff --git a/connectors/jk/native/common/jk_ajp12_worker.h b/connectors/jk/native/common/jk_ajp12_worker.h
new file mode 100644
index 0000000..06af91e
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp12_worker.h
@@ -0,0 +1,43 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: ajpv1.2 worker header file                                 *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef JK_AJP12_WORKER_H
+#define JK_AJP12_WORKER_H
+
+#include "jk_logger.h"
+#include "jk_service.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define JK_AJP12_WORKER_NAME ("ajp12")
+
+int JK_METHOD ajp12_worker_factory(jk_worker_t **w,
+                                   const char *name,
+                                   jk_logger_t *l);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_AJP12_WORKER_H */
diff --git a/connectors/jk/native/common/jk_ajp13.c b/connectors/jk/native/common/jk_ajp13.c
new file mode 100644
index 0000000..cea1483
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp13.c
@@ -0,0 +1,48 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Experimental bi-directionl protocol handler.               *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+
+#include "jk_global.h"
+#include "jk_util.h"
+#include "jk_ajp_common.h"
+#include "jk_ajp13.h"
+
+int ajp13_marshal_shutdown_into_msgb(jk_msg_buf_t *msg,
+                                     jk_pool_t *p,
+                                     jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, "Into ajp13_marshal_shutdown_into_msgb\n");
+    
+    /* To be on the safe side */
+    jk_b_reset(msg);
+
+    /*
+     * Just a single byte with s/d command.
+     */
+    if (jk_b_append_byte(msg, JK_AJP13_SHUTDOWN)) {
+		jk_log(l, JK_LOG_ERROR, "Error ajp13_marshal_shutdown_into_msgb - Error appending shutdown message\n");
+        return JK_FALSE;
+    }
+
+    return JK_TRUE;
+}
diff --git a/connectors/jk/native/common/jk_ajp13.h b/connectors/jk/native/common/jk_ajp13.h
new file mode 100644
index 0000000..e98c095
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp13.h
@@ -0,0 +1,117 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Experimental bi-directionl protocol handler.               *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+#ifndef JK_AJP13_H
+#define JK_AJP13_H
+
+#include "jk_ajp_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define AJP13_PROTO                 13
+#define AJP13_WS_HEADER             0x1234
+#define AJP13_SW_HEADER             0x4142  /* 'AB' */
+
+#define AJP13_DEF_HOST              ("localhost")
+#define AJP13_DEF_PORT              (8009)
+#define AJP13_READ_BUF_SIZE         (8*1024)
+#define AJP13_DEF_CACHE_SZ          (1)
+#define JK_INTERNAL_ERROR           (-2)
+#define JK_FATAL_ERROR              (-3)
+#define JK_CLIENT_ERROR             (-4)
+#define AJP13_MAX_SEND_BODY_SZ      (DEF_BUFFER_SZ - 6)
+#define AJP13_DEF_TIMEOUT           (0) /* Idle timout for pooled connections */
+
+/*
+ * Message does not have a response (for example, JK_AJP13_END_RESPONSE)
+ */
+#define JK_AJP13_ERROR              -1
+/*
+ * Message does not have a response (for example, JK_AJP13_END_RESPONSE)
+ */
+#define JK_AJP13_NO_RESPONSE        0
+/*
+ * Message have a response.
+ */
+#define JK_AJP13_HAS_RESPONSE       1
+
+/*
+ * Forward a request from the web server to the servlet container.
+ */
+#define JK_AJP13_FORWARD_REQUEST    (unsigned char)2
+
+/*
+ * Write a body chunk from the servlet container to the web server
+ */
+#define JK_AJP13_SEND_BODY_CHUNK    (unsigned char)3
+
+/*
+ * Send response headers from the servlet container to the web server.
+ */
+#define JK_AJP13_SEND_HEADERS       (unsigned char)4
+
+/*
+ * Marks the end of response.
+ */
+#define JK_AJP13_END_RESPONSE       (unsigned char)5
+
+/*
+ * Marks the end of response.
+ */
+#define JK_AJP13_GET_BODY_CHUNK     (unsigned char)6
+
+/*
+ * Asks the container to shutdown
+ */
+#define JK_AJP13_SHUTDOWN           (unsigned char)7
+
+/*
+ * Told container to take control (secure login phase)
+ */
+#define AJP13_PING_REQUEST          (unsigned char)8
+
+/*
+ * Check if the container is alive
+ */
+#define AJP13_CPING_REQUEST          (unsigned char)10
+
+/*
+ * Reply from the container to alive request
+ */
+#define AJP13_CPONG_REPLY            (unsigned char)9
+
+
+
+/*
+ * Functions
+ */
+
+int ajp13_marshal_shutdown_into_msgb(jk_msg_buf_t *msg,
+                                     jk_pool_t *p,
+                                     jk_logger_t *l);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_AJP13_H */
diff --git a/connectors/jk/native/common/jk_ajp13_worker.c b/connectors/jk/native/common/jk_ajp13_worker.c
new file mode 100644
index 0000000..16b7910
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp13_worker.c
@@ -0,0 +1,104 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Bi-directional protocol.                                   *
+ * Author:      Costin <costin@costin.dnt.ro>                              *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+#include "jk_ajp13_worker.h"
+
+
+/* -------------------- Method -------------------- */
+static int JK_METHOD validate(jk_worker_t *pThis,
+                              jk_map_t *props,                            
+                              jk_worker_env_t *we,
+                              jk_logger_t *l)
+{
+	return (ajp_validate(pThis, props, we, l, AJP13_PROTO));
+}
+
+
+static int JK_METHOD init(jk_worker_t *pThis,
+                          jk_map_t *props, 
+                          jk_worker_env_t *we,
+                          jk_logger_t *l)
+{
+	return (ajp_init(pThis, props, we, l, AJP13_PROTO));
+}
+
+
+static int JK_METHOD destroy(jk_worker_t **pThis,
+                             jk_logger_t *l)
+{
+	return (ajp_destroy(pThis, l, AJP13_PROTO));
+}
+
+
+static int JK_METHOD get_endpoint(jk_worker_t *pThis,
+                                  jk_endpoint_t **pend,
+                                  jk_logger_t *l)
+{
+    return (ajp_get_endpoint(pThis, pend, l, AJP13_PROTO));
+}
+
+int JK_METHOD ajp13_worker_factory(jk_worker_t **w,
+                                   const char   *name,
+                                   jk_logger_t  *l)
+{
+    ajp_worker_t *aw = (ajp_worker_t *)malloc(sizeof(ajp_worker_t));
+    
+    jk_log(l, JK_LOG_DEBUG, "Into ajp13_worker_factory\n");
+
+    if (name == NULL || w == NULL) {
+        jk_log(l, JK_LOG_ERROR, "In ajp13_worker_factory, NULL parameters\n");
+	    return JK_FALSE;
+    }
+        
+    if (! aw) {
+        jk_log(l, JK_LOG_ERROR, "In ajp13_worker_factory, malloc of private_data failed\n");
+	    return JK_FALSE;
+    }
+
+    aw->name = strdup(name);          
+    
+    if (! aw->name) {
+	    free(aw);
+	    jk_log(l, JK_LOG_ERROR, "In ajp13_worker_factory, malloc failed\n");
+	    return JK_FALSE;
+    } 
+
+	aw->proto					 = AJP13_PROTO;
+	aw->login					 = NULL;
+
+    aw->ep_cache_sz            = 0;
+    aw->ep_cache               = NULL;
+    aw->connect_retry_attempts = AJP_DEF_RETRY_ATTEMPTS;
+    aw->worker.worker_private  = aw;
+    
+    aw->worker.validate        = validate;
+    aw->worker.init            = init;
+    aw->worker.get_endpoint    = get_endpoint;
+    aw->worker.destroy         = destroy;
+    
+	aw->logon				   = NULL;	/* No Logon on AJP13 */
+
+    *w = &aw->worker;
+    return JK_TRUE;
+}
diff --git a/connectors/jk/native/common/jk_ajp13_worker.h b/connectors/jk/native/common/jk_ajp13_worker.h
new file mode 100644
index 0000000..20cd83b
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp13_worker.h
@@ -0,0 +1,48 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: ajpv1.3 worker header file                                 *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef JK_AJP13_WORKER_H
+#define JK_AJP13_WORKER_H
+
+#include "jk_pool.h"
+#include "jk_connect.h"
+#include "jk_util.h"
+#include "jk_msg_buff.h"
+#include "jk_ajp_common.h"
+#include "jk_ajp13.h"
+#include "jk_logger.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define JK_AJP13_WORKER_NAME ("ajp13")
+
+int JK_METHOD ajp13_worker_factory(jk_worker_t **w,
+                                   const char *name,
+                                   jk_logger_t *l);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_AJP13_WORKER_H */
diff --git a/connectors/jk/native/common/jk_ajp14.c b/connectors/jk/native/common/jk_ajp14.c
new file mode 100644
index 0000000..4564c69
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp14.c
@@ -0,0 +1,604 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Next generation bi-directional protocol handler.           *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+
+#include "jk_global.h"
+#include "jk_util.h"
+#include "jk_map.h"
+#include "jk_ajp_common.h"
+#include "jk_ajp14.h"
+#include "jk_md5.h"
+
+/*
+ * Compute the MD5 with ENTROPY / SECRET KEY
+ */
+
+void ajp14_compute_md5(jk_login_service_t *s, 
+                       jk_logger_t        *l)
+{
+	jk_md5((const unsigned char *)s->entropy, (const unsigned char *)s->secret_key, s->computed_key);
+
+	jk_log(l, JK_LOG_DEBUG, "Into ajp14_compute_md5 (%s/%s) -> (%s)\n", s->entropy, s->secret_key, s->computed_key);
+}
+
+
+/*
+ * Build the Login Init Command
+ *
+ * +-------------------------+---------------------------+---------------------------+
+ * | LOGIN INIT CMD (1 byte) | NEGOCIATION DATA (32bits) | WEB SERVER INFO (CString) |
+ * +-------------------------+---------------------------+---------------------------+
+ *
+ */
+
+int ajp14_marshal_login_init_into_msgb(jk_msg_buf_t       *msg,
+                                       jk_login_service_t *s,
+                                       jk_logger_t        *l)
+{
+    jk_log(l, JK_LOG_DEBUG, "Into ajp14_marshal_login_init_into_msgb\n");
+    
+    /* To be on the safe side */
+    jk_b_reset(msg);
+
+    /*
+     * LOGIN
+     */
+    if (jk_b_append_byte(msg, AJP14_LOGINIT_CMD)) 
+        return JK_FALSE;
+
+	/*
+     * NEGOCIATION FLAGS
+     */
+     if (jk_b_append_long(msg, s->negociation))
+		return JK_FALSE;
+
+	/*
+	 * WEB-SERVER NAME
+  	 */
+     if (jk_b_append_string(msg, s->web_server_name)) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14_marshal_login_init_into_msgb - Error appending the web_server_name string\n");
+        return JK_FALSE;
+    }
+
+    return JK_TRUE;
+}
+
+
+/*
+ * Decode the Login Seed Command
+ *
+ * +-------------------------+---------------------------+
+ * | LOGIN SEED CMD (1 byte) | MD5 of entropy (32 chars) |
+ * +-------------------------+---------------------------+
+ *
+ */
+
+int ajp14_unmarshal_login_seed(jk_msg_buf_t       *msg,
+                               jk_login_service_t *s,
+                               jk_logger_t        *l)
+{
+    if (jk_b_get_bytes(msg, (unsigned char *)s->entropy, AJP14_ENTROPY_SEED_LEN) < 0) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14_unmarshal_login_seed - can't get seed\n");
+        return JK_FALSE;
+    }
+
+	s->entropy[AJP14_ENTROPY_SEED_LEN] = 0;	/* Just to have a CString */
+	return JK_TRUE;
+}
+
+/*
+ * Build the Login Computed Command
+ *
+ * +-------------------------+---------------------------------------+
+ * | LOGIN COMP CMD (1 byte) | MD5 of RANDOM + SECRET KEY (32 chars) |
+ * +-------------------------+---------------------------------------+
+ *
+ */
+
+int ajp14_marshal_login_comp_into_msgb(jk_msg_buf_t       *msg,
+                                       jk_login_service_t *s,
+                                       jk_logger_t        *l)
+{
+    jk_log(l, JK_LOG_DEBUG, "Into ajp14_marshal_login_comp_into_msgb\n");
+    
+    /* To be on the safe side */
+    jk_b_reset(msg);
+
+    /*
+     * LOGIN
+     */
+    if (jk_b_append_byte(msg, AJP14_LOGCOMP_CMD)) 
+        return JK_FALSE;
+
+	/*
+	 * COMPUTED-SEED
+  	 */
+     if (jk_b_append_bytes(msg, (const unsigned char *)s->computed_key, AJP14_COMPUTED_KEY_LEN)) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14_marshal_login_comp_into_msgb - Error appending the COMPUTED MD5 bytes\n");
+        return JK_FALSE;
+    }
+
+    return JK_TRUE;
+}
+
+
+/*
+ * Decode the LogOk Command
+ *
+ * +--------------------+------------------------+-------------------------------+
+ * | LOGOK CMD (1 byte) | NEGOCIED DATA (32bits) | SERVLET ENGINE INFO (CString) |
+ * +--------------------+------------------------+-------------------------------+
+ *
+ */
+
+int ajp14_unmarshal_log_ok(jk_msg_buf_t       *msg,
+                           jk_login_service_t *s,
+                           jk_logger_t        *l)
+{
+	unsigned long 	nego;
+	char *			sname;
+
+	nego = jk_b_get_long(msg);
+
+	if (nego == 0xFFFFFFFF) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14_unmarshal_log_ok - can't get negociated data\n");
+        return JK_FALSE;
+    }
+
+	sname = (char *)jk_b_get_string(msg);
+
+	if (! sname) {
+		jk_log(l, JK_LOG_ERROR, "Error ajp14_unmarshal_log_ok - can't get servlet engine name\n");
+		return JK_FALSE;
+	}
+	
+	if (s->servlet_engine_name)			/* take care of removing previously allocated data */
+		free(s->servlet_engine_name);
+
+	s->servlet_engine_name = strdup(sname);
+
+	if (! s->servlet_engine_name) {
+		jk_log(l, JK_LOG_ERROR, "Error ajp14_unmarshal_log_ok - can't malloc servlet engine name\n");
+		return JK_FALSE;
+	}
+
+	return JK_TRUE;
+}
+
+
+/*
+ * Decode the Log Nok Command 
+ *
+ * +---------------------+-----------------------+
+ * | LOGNOK CMD (1 byte) | FAILURE CODE (32bits) |
+ * +---------------------+-----------------------+
+ *
+ */
+
+int ajp14_unmarshal_log_nok(jk_msg_buf_t *msg,
+                            jk_logger_t  *l)
+{
+	unsigned long   status;
+
+    jk_log(l, JK_LOG_DEBUG, "Into ajp14_unmarshal_log_nok\n");
+
+	status = jk_b_get_long(msg);
+
+    if (status == 0xFFFFFFFF) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14_unmarshal_log_nok - can't get failure code\n");
+        return JK_FALSE;
+    }
+
+	jk_log(l, JK_LOG_INFO, "Can't Log with servlet engine - code %08lx", status);
+	return JK_TRUE;
+}
+
+
+/* 
+ * Build the Shutdown Cmd
+ *
+ * +-----------------------+---------------------------------------+
+ * | SHUTDOWN CMD (1 byte) | MD5 of RANDOM + SECRET KEY (32 chars) |
+ * +-----------------------+---------------------------------------+
+ *
+ */
+
+int ajp14_marshal_shutdown_into_msgb(jk_msg_buf_t       *msg,
+                                     jk_login_service_t *s,
+                                     jk_logger_t        *l)
+{
+    jk_log(l, JK_LOG_DEBUG, "Into ajp14_marshal_shutdown_into_msgb\n");
+
+    /* To be on the safe side */
+    jk_b_reset(msg);
+
+    /*
+     * SHUTDOWN CMD
+     */
+    if (jk_b_append_byte(msg, AJP14_SHUTDOWN_CMD))
+        return JK_FALSE;
+
+    /*
+     * COMPUTED-SEED
+     */
+     if (jk_b_append_bytes(msg, (const unsigned char *)s->computed_key, AJP14_COMPUTED_KEY_LEN)) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14_marshal_shutdown_into_msgb - Error appending the COMPUTED MD5 bytes\n");
+        return JK_FALSE;
+    }
+
+	return JK_TRUE;
+}
+
+/*
+ * Decode the Shutdown Nok Command
+ *
+ * +----------------------+-----------------------+
+ * | SHUTNOK CMD (1 byte) | FAILURE CODE (32bits) |
+ * +----------------------+-----------------------+
+ *
+ */
+int ajp14_unmarshal_shutdown_nok(jk_msg_buf_t *msg,
+                                 jk_logger_t  *l)
+{
+    unsigned long   status;
+
+    jk_log(l, JK_LOG_DEBUG, "Into ajp14_unmarshal_shutdown_nok\n");
+
+    status = jk_b_get_long(msg);
+
+    if (status == 0xFFFFFFFF) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14_unmarshal_shutdown_nok - can't get failure code\n");
+        return JK_FALSE;
+    }
+
+    jk_log(l, JK_LOG_INFO, "Can't shutdown servlet engine - code %08lx", status);
+    return JK_TRUE;
+}
+
+/*
+ * Build the Unknown Packet
+ *
+ * +-----------------------------+---------------------------------+------------------------------+
+ * | UNKNOWN PACKET CMD (1 byte) | UNHANDLED MESSAGE SIZE (16bits) | UNHANDLED MESSAGE (bytes...) |
+ * +-----------------------------+---------------------------------+------------------------------+
+ *
+ */
+
+int ajp14_marshal_unknown_packet_into_msgb(jk_msg_buf_t		*msg,
+                                           jk_msg_buf_t		*unk,
+                                           jk_logger_t  	*l)
+{
+	jk_log(l, JK_LOG_DEBUG, "Into ajp14_marshal_unknown_packet_into_msgb\n");
+
+	/* To be on the safe side */
+	jk_b_reset(msg);
+
+	/*
+ 	 * UNKNOWN PACKET CMD
+	 */
+	if (jk_b_append_byte(msg, AJP14_UNKNOW_PACKET_CMD))
+		return JK_FALSE;
+
+	/*
+	 * UNHANDLED MESSAGE SIZE
+	 */
+	if (jk_b_append_int(msg, (unsigned short) jk_b_get_len(unk)))
+		return JK_FALSE;
+
+	/*
+	 * UNHANDLED MESSAGE (Question : Did we have to send all the message or only part of)
+	 *					 (           ie: only 1k max								    )
+	 */
+	if (jk_b_append_bytes(msg, jk_b_get_buff(unk), jk_b_get_len(unk))) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14_marshal_unknown_packet_into_msgb - Error appending the UNHANDLED MESSAGE\n");
+        return JK_FALSE;
+    }
+
+    return JK_TRUE;
+}
+
+/*
+ * Build the Context Query Cmd (autoconf)
+ *
+ * +--------------------------+---------------------------------+
+ * | CONTEXT QRY CMD (1 byte) | VIRTUAL HOST NAME (CString (*)) |
+ * +--------------------------+---------------------------------+
+ *
+ */
+
+int ajp14_marshal_context_query_into_msgb(jk_msg_buf_t *msg,
+										  char         *virtual,
+										  jk_logger_t  *l)
+{
+	jk_log(l, JK_LOG_DEBUG, "Into ajp14_marshal_context_query_into_msgb\n");
+
+	/* To be on the safe side */
+	jk_b_reset(msg);
+
+    /*
+     * CONTEXT QUERY CMD
+     */
+    if (jk_b_append_byte(msg, AJP14_CONTEXT_QRY_CMD))
+        return JK_FALSE;
+
+    /*
+     * VIRTUAL HOST CSTRING
+     */
+     if (jk_b_append_string(msg, virtual)) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14_marshal_context_query_into_msgb - Error appending the virtual host string\n");
+        return JK_FALSE;
+    }
+
+	return JK_TRUE;
+}
+
+
+/*
+ * Decode the Context Info Cmd (Autoconf)
+ *
+ * The Autoconf feature of AJP14, let us know which URL/URI could
+ * be handled by the servlet-engine
+ *
+ * +---------------------------+---------------------------------+----------------------------+-------------------------------+-----------+
+ * | CONTEXT INFO CMD (1 byte) | VIRTUAL HOST NAME (CString (*)) | CONTEXT NAME (CString (*)) | URL1 [\n] URL2 [\n] URL3 [\n] | NEXT CTX. |
+ * +---------------------------+---------------------------------+----------------------------+-------------------------------+-----------+
+ */
+
+int ajp14_unmarshal_context_info(jk_msg_buf_t *msg,
+								 jk_context_t *c,
+                                 jk_logger_t  *l)
+{
+    char *vname;
+    char *cname;
+    char *uri;
+
+    vname  = (char *)jk_b_get_string(msg);
+
+    jk_log(l, JK_LOG_DEBUG, "ajp14_unmarshal_context_info - get virtual %s for virtual %s\n", vname, c->virtual);
+
+    if (! vname) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14_unmarshal_context_info - can't get virtual hostname\n");
+        return JK_FALSE;
+    }
+
+    /* Check if we get the correct virtual host */
+    if (c->virtual != NULL && 
+	vname != NULL &&
+	strcmp(c->virtual, vname)) {
+        /* set the virtual name, better to add to a virtual list ? */
+        
+        if (context_set_virtual(c, vname) == JK_FALSE) {
+            jk_log(l, JK_LOG_ERROR, "Error ajp14_unmarshal_context_info - can't malloc virtual hostname\n");
+            return JK_FALSE;
+        }
+    }
+
+    for (;;) {
+    
+        cname  = (char *)jk_b_get_string(msg); 
+
+        if (! cname) {
+            jk_log(l, JK_LOG_ERROR, "Error ajp14_unmarshal_context_info - can't get context\n");
+            return JK_FALSE;
+        }   
+
+        jk_log(l, JK_LOG_DEBUG, "ajp14_unmarshal_context_info - get context %s for virtual %s\n", cname, vname);
+
+        /* grab all contexts up to empty one which indicate end of contexts */
+        if (! strlen(cname)) 
+            break;
+
+        /* create new context base (if needed) */
+
+        if (context_add_base(c, cname) == JK_FALSE) {
+            jk_log(l, JK_LOG_ERROR, "Error ajp14_unmarshal_context_info - can't add/set context %s\n", cname);
+            return JK_FALSE;
+        }
+
+	    for (;;) {
+
+            uri  = (char *)jk_b_get_string(msg);
+
+		    if (!uri) {
+                jk_log(l, JK_LOG_ERROR, "Error ajp14_unmarshal_context_info - can't get URI\n");
+                return JK_FALSE;
+		    }
+
+		    if (! strlen(uri)) {
+			    jk_log(l, JK_LOG_DEBUG, "No more URI for context %s", cname);
+			    break;
+		    }
+
+		    jk_log(l, JK_LOG_INFO, "Got URI (%s) for virtualhost %s and context %s\n", uri, vname, cname);
+
+		    if (context_add_uri(c, cname, uri) == JK_FALSE) {
+                jk_log(l, JK_LOG_ERROR, "Error ajp14_unmarshal_context_info - can't add/set uri (%s) for context %s\n", uri, cname);
+                return JK_FALSE;
+            } 
+	    }
+	}
+
+    return JK_TRUE;
+}
+
+
+/*
+ * Build the Context State Query Cmd
+ *
+ * We send the list of contexts where we want to know state, empty string end context list*
+ * If cname is set, only ask about THIS context
+ *
+ * +----------------------------+----------------------------------+----------------------------+----+
+ * | CONTEXT STATE CMD (1 byte) |  VIRTUAL HOST NAME (CString (*)) | CONTEXT NAME (CString (*)) | .. |
+ * +----------------------------+----------------------------------+----------------------------+----+
+ *
+ */
+
+int ajp14_marshal_context_state_into_msgb(jk_msg_buf_t *msg,
+                                          jk_context_t *c,
+                                          char         *cname,
+                                          jk_logger_t  *l)
+{
+    jk_context_item_t *ci;
+    int                i;
+
+    jk_log(l, JK_LOG_DEBUG, "Into ajp14_marshal_context_state_into_msgb\n");
+
+    /* To be on the safe side */
+    jk_b_reset(msg);
+
+    /*
+     * CONTEXT STATE CMD
+     */
+    if (jk_b_append_byte(msg, AJP14_CONTEXT_STATE_CMD))
+        return JK_FALSE;
+
+    /*
+     * VIRTUAL HOST CSTRING
+     */
+     if (jk_b_append_string(msg, c->virtual)) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14_marshal_context_state_into_msgb - Error appending the virtual host string\n");
+        return JK_FALSE;
+    }
+    
+    if (cname) {
+
+        ci = context_find_base(c, cname);
+
+        if (! ci) {
+            jk_log(l, JK_LOG_ERROR, "Warning ajp14_marshal_context_state_into_msgb - unknown context %s\n", cname);
+            return JK_FALSE;
+        }
+
+        /*
+         * CONTEXT CSTRING
+         */
+
+        if (jk_b_append_string(msg, cname )) {
+            jk_log(l, JK_LOG_ERROR, "Error ajp14_marshal_context_state_into_msgb - Error appending the context string %s\n", cname);
+            return JK_FALSE;
+        }
+    }
+    else { /* Grab all contexts name */
+
+        for (i = 0; i < c->size; i++) {
+        
+            /*
+             * CONTEXT CSTRING
+             */
+            if (jk_b_append_string(msg, c->contexts[i]->cbase )) {
+                jk_log(l, JK_LOG_ERROR, "Error ajp14_marshal_context_state_into_msgb - Error appending the context string\n");
+                return JK_FALSE;
+            }
+        }
+    }
+
+    /* End of context list, an empty string */ 
+
+    if (jk_b_append_string(msg, "")) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14_marshal_context_state_into_msgb - Error appending end of contexts\n");
+        return JK_FALSE;
+    }
+
+    return JK_TRUE;
+}
+
+
+/*
+ * Decode the Context State Reply Cmd
+ *
+ * We get update of contexts list, empty string end context list*
+ *
+ * +----------------------------------+---------------------------------+----------------------------+------------------+----+
+ * | CONTEXT STATE REPLY CMD (1 byte) | VIRTUAL HOST NAME (CString (*)) | CONTEXT NAME (CString (*)) | UP/DOWN (1 byte) | .. |
+ * +----------------------------------+---------------------------------+----------------------------+------------------+----+
+ *
+ */
+
+int ajp14_unmarshal_context_state_reply(jk_msg_buf_t *msg,
+										jk_context_t *c,
+                           				jk_logger_t  *l)
+{
+    char                *vname;
+    char                *cname;
+    jk_context_item_t   *ci;
+
+    /* get virtual name */
+	vname  = (char *)jk_b_get_string(msg);
+
+    if (! vname) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14_unmarshal_context_state_reply - can't get virtual hostname\n");
+        return JK_FALSE;
+    }
+
+    /* Check if we speak about the correct virtual */
+    if (strcmp(c->virtual, vname)) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14_unmarshal_context_state_reply - incorrect virtual %s instead of %s\n",
+               vname, c->virtual);
+        return JK_FALSE;
+    }
+ 
+    for (;;) {
+
+        /* get context name */
+	    cname  = (char *)jk_b_get_string(msg);
+
+	    if (! cname) {
+		    jk_log(l, JK_LOG_ERROR, "Error ajp14_unmarshal_context_state_reply - can't get context\n");
+		    return JK_FALSE;
+	    }	
+
+        if (! strlen(cname))
+            break;
+
+        ci = context_find_base(c, cname);
+
+        if (! ci) {
+            jk_log(l, JK_LOG_ERROR, "Error ajp14_unmarshal_context_state_reply - unknow context %s for virtual %s\n", 
+                   cname, vname);
+            return JK_FALSE;
+        }
+
+	    ci->status = jk_b_get_int(msg);
+
+        jk_log(l, JK_LOG_DEBUG, "ajp14_unmarshal_context_state_reply - updated context %s to state %d\n", cname, ci->status);
+    }
+
+    return JK_TRUE;
+}
+
+/*
+ * Decode the Context Update Cmd
+ * 
+ * +-----------------------------+---------------------------------+----------------------------+------------------+
+ * | CONTEXT UPDATE CMD (1 byte) | VIRTUAL HOST NAME (CString (*)) | CONTEXT NAME (CString (*)) | UP/DOWN (1 byte) |
+ * +-----------------------------+---------------------------------+----------------------------+------------------+
+ * 
+ */
+
+int ajp14_unmarshal_context_update_cmd(jk_msg_buf_t *msg,
+                                       jk_context_t *c,
+                                       jk_logger_t  *l)
+{
+	return (ajp14_unmarshal_context_state_reply(msg, c, l));
+}
+
diff --git a/connectors/jk/native/common/jk_ajp14.h b/connectors/jk/native/common/jk_ajp14.h
new file mode 100644
index 0000000..4826120
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp14.h
@@ -0,0 +1,312 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Next generation bi-directional protocol handler.           *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+#ifndef JK_AJP14_H
+#define JK_AJP14_H
+
+#include "jk_ajp_common.h"
+#include "jk_context.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define AJP14_PROTO					14
+
+#define AJP14_WS_HEADER             0x1235
+#define AJP14_SW_HEADER             0x1235	/* AJP14 use now the same header in both directions */
+
+#define AJP14_DEF_HOST            	("localhost")
+#define AJP14_DEF_PORT            	(8011)
+#define AJP14_READ_BUF_SIZE         (8*1024)
+#define AJP14_DEF_RETRY_ATTEMPTS    (1)
+#define AJP14_DEF_CACHE_SZ      	(1)
+#define AJP14_MAX_SEND_BODY_SZ  	(DEF_BUFFER_SZ - 6)
+#define AJP14_HEADER_LEN    		(4)
+#define AJP14_HEADER_SZ_LEN 		(2)
+
+/*
+ * Initial Login Phase (web server -> servlet engine)
+ */
+#define AJP14_LOGINIT_CMD			(unsigned char)0x10
+
+/*
+ * Second Login Phase (servlet engine -> web server), md5 seed is received
+ */
+#define AJP14_LOGSEED_CMD			(unsigned char)0x11
+
+/*
+ * Third Login Phase (web server -> servlet engine), md5 of seed + secret is sent
+ */
+#define AJP14_LOGCOMP_CMD			(unsigned char)0x12
+
+/*
+ * Login Accepted (servlet engine -> web server)
+ */
+#define AJP14_LOGOK_CMD				(unsigned char)0x13
+
+/*
+ * Login Rejected (servlet engine -> web server), will be logged
+ */
+#define AJP14_LOGNOK_CMD			(unsigned char)0x14
+
+/*
+ * Context Query (web server -> servlet engine), which URI are handled by servlet engine ?
+ */
+#define AJP14_CONTEXT_QRY_CMD		(unsigned char)0x15
+
+/*
+ * Context Info (servlet engine -> web server), URI handled response
+ */
+#define AJP14_CONTEXT_INFO_CMD		(unsigned char)0x16
+
+/* 
+ * Context Update (servlet engine -> web server), status of context changed
+ */
+#define AJP14_CONTEXT_UPDATE_CMD	(unsigned char)0x17
+
+/*
+ * Servlet Engine Status (web server -> servlet engine), what's the status of the servlet engine ?
+ */
+#define AJP14_STATUS_CMD			(unsigned char)0x18
+
+/*
+ * Secure Shutdown command (web server -> servlet engine), please servlet stop yourself.
+ */
+#define AJP14_SHUTDOWN_CMD			(unsigned char)0x19
+
+/*
+ * Secure Shutdown command Accepted (servlet engine -> web server)
+ */
+#define AJP14_SHUTOK_CMD			(unsigned char)0x1A
+
+/*
+ * Secure Shutdown Rejected (servlet engine -> web server)
+ */
+#define AJP14_SHUTNOK_CMD			(unsigned char)0x1B
+
+/*
+ * Context Status (web server -> servlet engine), what's the status of the context ?
+ */
+#define AJP14_CONTEXT_STATE_CMD		(unsigned char)0x1C
+
+/*
+ * Context Status Reply (servlet engine -> web server), status of context
+ */
+#define AJP14_CONTEXT_STATE_REP_CMD	(unsigned char)0x1D
+
+/*
+ * Unknown Packet Reply (web server <-> servlet engine), when a packet couldn't be decoded
+ */
+#define AJP14_UNKNOW_PACKET_CMD		(unsigned char)0x1E
+
+
+/*
+ * Negociation flags 
+ */
+
+/*
+ * web-server want context info after login
+ */
+#define AJP14_CONTEXT_INFO_NEG      0x80000000 
+
+/*
+ * web-server want context updates
+ */
+#define AJP14_CONTEXT_UPDATE_NEG    0x40000000 
+
+/*
+ * web-server want compressed stream 
+ */
+#define AJP14_GZIP_STREAM_NEG       0x20000000 
+
+/*
+ * web-server want crypted DES56 stream with secret key
+ */
+#define AJP14_DES56_STREAM_NEG      0x10000000 
+
+/*
+ * Extended info on server SSL vars
+ */
+#define AJP14_SSL_VSERVER_NEG       0x08000000 
+
+/*
+ *Extended info on client SSL vars
+ */
+#define AJP14_SSL_VCLIENT_NEG       0x04000000 
+
+/*
+ * Extended info on crypto SSL vars
+ */
+#define AJP14_SSL_VCRYPTO_NEG       0x02000000 
+
+/*
+ * Extended info on misc SSL vars
+ */
+#define AJP14_SSL_VMISC_NEG         0x01000000 
+
+/*
+ * mask of protocol supported 
+ */
+#define AJP14_PROTO_SUPPORT_AJPXX_NEG   0x00FF0000 
+
+/* 
+ * communication could use AJP14 
+ */
+#define AJP14_PROTO_SUPPORT_AJP14_NEG   0x00010000 
+
+/*
+ * communication could use AJP15 
+ */
+#define AJP14_PROTO_SUPPORT_AJP15_NEG   0x00020000 
+
+/*
+ * communication could use AJP16
+ */
+#define AJP14_PROTO_SUPPORT_AJP16_NEG   0x00040000 
+
+/*
+ * Some failure codes
+ */
+#define AJP14_BAD_KEY_ERR				0xFFFFFFFF
+#define AJP14_ENGINE_DOWN_ERR			0xFFFFFFFE
+#define AJP14_RETRY_LATER_ERR			0xFFFFFFFD
+#define AJP14_SHUT_AUTHOR_FAILED_ERR    0xFFFFFFFC
+
+/*
+ * Some status codes
+ */
+#define AJP14_CONTEXT_DOWN       0x01
+#define AJP14_CONTEXT_UP         0x02
+#define AJP14_CONTEXT_OK         0x03
+
+/* 
+ * Misc defines
+ */
+#define AJP14_ENTROPY_SEED_LEN		32	/* we're using MD5 => 32 chars */
+#define AJP14_COMPUTED_KEY_LEN		32  /* we're using MD5 also */
+
+/*
+ * The login structure
+ */
+typedef struct jk_login_service jk_login_service_t;
+
+struct jk_login_service {
+
+    /*
+	 *  Pointer to web-server name
+     */
+    char * web_server_name;
+
+	/*
+	 * Pointer to servlet-engine name
+	 */
+	char * servlet_engine_name;
+
+	/*
+	 * Pointer to secret key
+	 */
+	char * secret_key;
+
+	/*
+	 * Received entropy seed
+	 */
+	char entropy[AJP14_ENTROPY_SEED_LEN + 1];
+
+	/*
+	 * Computed key
+	 */
+	char computed_key[AJP14_COMPUTED_KEY_LEN + 1];
+
+    /*
+     *  What we want to negociate
+     */
+    unsigned long negociation;
+
+	/*
+	 * What we received from servlet engine 
+     */
+	unsigned long negociated;
+};                                
+
+/*
+ * functions defined here 
+ */
+
+void 	ajp14_compute_md5(jk_login_service_t *s, 
+						  jk_logger_t *l);
+
+int 	ajp14_marshal_login_init_into_msgb(jk_msg_buf_t *msg, 
+										   jk_login_service_t *s, 
+										   jk_logger_t *l);
+
+int 	ajp14_unmarshal_login_seed(jk_msg_buf_t *msg, 
+								   jk_login_service_t *s, 
+								   jk_logger_t *l);
+
+int 	ajp14_marshal_login_comp_into_msgb(jk_msg_buf_t *msg, 
+										   jk_login_service_t *s, 
+										   jk_logger_t *l);
+
+int 	ajp14_unmarshal_log_ok(jk_msg_buf_t *msg, 
+							   jk_login_service_t *s, 
+							   jk_logger_t *l);
+
+int 	ajp14_unmarshal_log_nok(jk_msg_buf_t *msg, 
+								jk_logger_t *l);
+
+int 	ajp14_marshal_shutdown_into_msgb(jk_msg_buf_t *msg, 
+										 jk_login_service_t *s, 
+										 jk_logger_t *l);
+
+int 	ajp14_unmarshal_shutdown_nok(jk_msg_buf_t *msg, 
+									 jk_logger_t *l);
+
+int 	ajp14_marshal_unknown_packet_into_msgb(jk_msg_buf_t *msg, 
+											   jk_msg_buf_t *unk, 
+											   jk_logger_t *l);
+
+int 	ajp14_marshal_context_query_into_msgb(jk_msg_buf_t *msg, 
+											  char *virtual, 
+											  jk_logger_t *l);
+
+int 	ajp14_unmarshal_context_info(jk_msg_buf_t *msg, 
+									 jk_context_t *context, 
+									 jk_logger_t *l);
+
+int 	ajp14_marshal_context_state_into_msgb(jk_msg_buf_t *msg, 
+											  jk_context_t *context, 
+                                              char         *cname,
+											  jk_logger_t *l);
+
+int 	ajp14_unmarshal_context_state_reply(jk_msg_buf_t *msg, 
+											jk_context_t *context, 
+											jk_logger_t *l);
+
+int 	ajp14_unmarshal_context_update_cmd(jk_msg_buf_t *msg, 
+										   jk_context_t *context, 
+										   jk_logger_t *l);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_AJP14_H */
diff --git a/connectors/jk/native/common/jk_ajp14_worker.c b/connectors/jk/native/common/jk_ajp14_worker.c
new file mode 100644
index 0000000..3cdfabc
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp14_worker.c
@@ -0,0 +1,382 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: AJP14 next generation Bi-directional protocol.             *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+#include "jk_context.h"
+#include "jk_ajp14_worker.h"
+
+
+/*
+ * AJP14 Autoconf Phase
+ *
+ * CONTEXT QUERY / REPLY
+ */
+
+#define MAX_URI_SIZE    512
+
+static int handle_discovery(ajp_endpoint_t  *ae,
+                            jk_worker_env_t *we,
+                            jk_msg_buf_t    *msg,
+                            jk_logger_t     *l)
+{
+    int                 cmd;
+    int                 i,j;
+    jk_login_service_t  *jl = ae->worker->login;
+    jk_context_item_t   *ci;
+    jk_context_t        *c;  
+    char                *buf;
+
+#ifndef TESTME
+
+    ajp14_marshal_context_query_into_msgb(msg, we->virtual, l);
+
+    jk_log(l, JK_LOG_DEBUG, "Into ajp14:discovery - send query\n");
+
+    if (ajp_connection_tcp_send_message(ae, msg, l) != JK_TRUE)
+        return JK_FALSE;
+
+    jk_log(l, JK_LOG_DEBUG, "Into ajp14:discovery - wait context reply\n");
+
+    jk_b_reset(msg);
+
+    if (ajp_connection_tcp_get_message(ae, msg, l) != JK_TRUE)
+        return JK_FALSE;
+
+    if ((cmd = jk_b_get_byte(msg)) != AJP14_CONTEXT_INFO_CMD) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14:discovery - awaited command %d, received %d\n", AJP14_CONTEXT_INFO_CMD, cmd);
+        return JK_FALSE;
+    }
+
+    if (context_alloc(&c, we->virtual) != JK_TRUE) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14:discovery - can't allocate context room\n");
+        return JK_FALSE;
+    }
+ 
+    if (ajp14_unmarshal_context_info(msg, c, l) != JK_TRUE) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14:discovery - can't get context reply\n");
+        return JK_FALSE;
+    }
+
+    jk_log(l, JK_LOG_DEBUG, "Into ajp14:discovery - received context\n");
+
+    buf = malloc(MAX_URI_SIZE);      /* Really a very long URI */
+
+    if (! buf) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp14:discovery - can't alloc buf\n");
+        return JK_FALSE;
+    }
+
+    for (i = 0; i < c->size; i++) {
+        ci = c->contexts[i];
+        for (j = 0; j < ci->size; j++) {
+
+#ifndef USE_SPRINTF
+            snprintf(buf, MAX_URI_SIZE - 1, "/%s/%s", ci->cbase, ci->uris[j]);
+#else
+            sprintf(buf, "/%s/%s", ci->cbase, ci->uris[j]);
+#endif
+
+            jk_log(l, JK_LOG_INFO, "Into ajp14:discovery - worker %s will handle uri %s in context %s [%s]\n",
+                    ae->worker->name, ci->uris[j], ci->cbase, buf);
+
+            uri_worker_map_add(we->uri_to_worker, buf, ae->worker->name, l);
+        }
+    }
+
+    free(buf);
+    context_free(&c);
+
+#else 
+
+    uri_worker_map_add(we->uri_to_worker, "/examples/servlet/*", ae->worker->name, l);
+    uri_worker_map_add(we->uri_to_worker, "/examples/*.jsp", ae->worker->name, l);
+    uri_worker_map_add(we->uri_to_worker, "/examples/*.gif", ae->worker->name, l);
+
+#endif 
+
+    return JK_TRUE;
+}
+
+/* 
+ * AJP14 Logon Phase 
+ *
+ * INIT + REPLY / NEGO + REPLY 
+ */
+
+static int handle_logon(ajp_endpoint_t *ae,
+					   jk_msg_buf_t	   *msg,
+					   jk_logger_t     *l)
+{
+	int	cmd;
+
+	jk_login_service_t *jl = ae->worker->login;
+
+	ajp14_marshal_login_init_into_msgb(msg, jl, l);
+
+	jk_log(l, JK_LOG_DEBUG, "Into ajp14:logon - send init\n");
+
+	if (ajp_connection_tcp_send_message(ae, msg, l) != JK_TRUE)	
+		return JK_FALSE;
+ 
+	jk_log(l, JK_LOG_DEBUG, "Into ajp14:logon - wait init reply\n");
+
+	jk_b_reset(msg);
+
+	if (ajp_connection_tcp_get_message(ae, msg, l) != JK_TRUE)
+		return JK_FALSE;
+
+	if ((cmd = jk_b_get_byte(msg)) != AJP14_LOGSEED_CMD) {
+		jk_log(l, JK_LOG_ERROR, "Error ajp14:logon: awaited command %d, received %d\n", AJP14_LOGSEED_CMD, cmd);
+		return JK_FALSE;
+	}
+
+	if (ajp14_unmarshal_login_seed(msg, jl, l) != JK_TRUE)
+		return JK_FALSE;
+				
+	jk_log(l, JK_LOG_DEBUG, "Into ajp14:logon - received entropy %s\n", jl->entropy);
+
+	ajp14_compute_md5(jl, l);
+
+	if (ajp14_marshal_login_comp_into_msgb(msg, jl, l) != JK_TRUE)
+		return JK_FALSE;
+	
+	if (ajp_connection_tcp_send_message(ae, msg, l) != JK_TRUE) 
+		return JK_FALSE;
+
+	jk_b_reset(msg);
+
+	if (ajp_connection_tcp_get_message(ae, msg, l) != JK_TRUE)
+		return JK_FALSE;
+
+	switch (jk_b_get_byte(msg)) {
+
+	case AJP14_LOGOK_CMD  :	
+		if (ajp14_unmarshal_log_ok(msg, jl, l) == JK_TRUE) {
+			jk_log(l, JK_LOG_DEBUG, "Successfully connected to servlet-engine %s\n", jl->servlet_engine_name);
+			return JK_TRUE;
+		}
+		break;
+		
+	case AJP14_LOGNOK_CMD :
+		ajp14_unmarshal_log_nok(msg, l);
+		break;
+	}
+
+	return JK_FALSE;
+}
+
+static int logon(ajp_endpoint_t *ae,
+                jk_logger_t    *l)
+{
+	jk_pool_t          *p = &ae->pool;
+	jk_msg_buf_t	   *msg;
+	int					rc;
+
+	jk_log(l, JK_LOG_DEBUG, "Into ajp14:logon\n");
+
+	msg = jk_b_new(p);
+	jk_b_set_buffer_size(msg, DEF_BUFFER_SZ);
+	
+	if ((rc = handle_logon(ae, msg, l)) == JK_FALSE) 
+		ajp_close_endpoint(ae, l);
+
+	return rc;
+}
+
+static int discovery(ajp_endpoint_t *ae,
+                     jk_worker_env_t *we,
+                     jk_logger_t    *l)
+{
+    jk_pool_t          *p = &ae->pool;
+    jk_msg_buf_t       *msg;
+    int                 rc;
+
+    jk_log(l, JK_LOG_DEBUG, "Into ajp14:discovery\n");
+
+    msg = jk_b_new(p);
+    jk_b_set_buffer_size(msg, DEF_BUFFER_SZ);
+
+    if ((rc = handle_discovery(ae, we, msg, l)) == JK_FALSE)
+        ajp_close_endpoint(ae, l);
+
+    return rc;
+}
+
+/* -------------------- Method -------------------- */
+static int JK_METHOD validate(jk_worker_t *pThis,
+                              jk_map_t    *props,
+                              jk_worker_env_t *we,
+                              jk_logger_t *l)
+{   
+	ajp_worker_t *aw;
+	char * secret_key;
+
+    if (ajp_validate(pThis, props, we, l, AJP14_PROTO) == JK_FALSE)
+		return JK_FALSE;
+
+   aw = pThis->worker_private;
+
+   secret_key = jk_get_worker_secret_key(props, aw->name);
+
+   if ((!secret_key) || (!strlen(secret_key))) {
+		jk_log(l, JK_LOG_ERROR, "validate error, empty or missing secretkey\n");
+		return JK_FALSE;
+	}
+
+	/* jk_log(l, JK_LOG_DEBUG, "Into ajp14:validate - secret_key=%s\n", secret_key); */
+	return JK_TRUE;
+}
+
+static int JK_METHOD get_endpoint(jk_worker_t    *pThis,
+                                  jk_endpoint_t **pend,
+                                  jk_logger_t    *l)
+{
+    return (ajp_get_endpoint(pThis, pend, l, AJP14_PROTO));
+}
+
+static int JK_METHOD init(jk_worker_t *pThis,
+                          jk_map_t    *props, 
+                          jk_worker_env_t *we,
+                          jk_logger_t *l)
+{
+	ajp_worker_t   *aw;
+	ajp_endpoint_t *ae;
+	jk_endpoint_t  *je;
+    int             rc;
+
+   	if (ajp_init(pThis, props, we, l, AJP14_PROTO) == JK_FALSE)
+		return JK_FALSE;
+
+   	aw = pThis->worker_private;
+
+	/* Set Secret Key (used at logon time) */	
+	aw->login->secret_key = strdup(jk_get_worker_secret_key(props, aw->name));
+
+	if (aw->login->secret_key == NULL) {
+		jk_log(l, JK_LOG_ERROR, "can't malloc secret_key\n");
+		return JK_FALSE;
+	}
+
+	/* Set WebServerName (used at logon time) */
+	aw->login->web_server_name = strdup(we->server_name);
+
+	if (aw->login->web_server_name == NULL) {
+		jk_log(l, JK_LOG_ERROR, "can't malloc web_server_name\n");
+		return JK_FALSE;
+	}
+
+	if (get_endpoint(pThis, &je, l) == JK_FALSE)
+		return JK_FALSE;
+	
+	ae = je->endpoint_private;
+
+	if (ajp_connect_to_endpoint(ae, l) == JK_TRUE) {
+
+	/* connection stage passed - try to get context info
+	 * this is the long awaited autoconf feature :)
+	 */
+        rc = discovery(ae, we, l);
+		ajp_close_endpoint(ae, l);
+        return rc;
+	}
+
+	return JK_TRUE;
+}
+    
+
+static int JK_METHOD destroy(jk_worker_t **pThis,
+                             jk_logger_t *l)
+{
+	ajp_worker_t *aw = (*pThis)->worker_private;
+
+	if (aw->login) {
+
+		if (aw->login->web_server_name) {
+			free(aw->login->web_server_name);
+			aw->login->web_server_name = NULL;
+		}
+
+		if (aw->login->secret_key) {
+			free(aw->login->secret_key);
+			aw->login->secret_key = NULL;
+		}
+
+		free(aw->login);
+		aw->login = NULL;
+	}
+
+    return (ajp_destroy(pThis, l, AJP14_PROTO));
+}
+
+int JK_METHOD ajp14_worker_factory(jk_worker_t **w,
+                                   const char   *name,
+                                   jk_logger_t  *l)
+{
+    ajp_worker_t *aw = (ajp_worker_t *)malloc(sizeof(ajp_worker_t));
+   
+    jk_log(l, JK_LOG_DEBUG, "Into ajp14_worker_factory\n");
+
+    if (name == NULL || w == NULL) {
+        jk_log(l, JK_LOG_ERROR, "In ajp14_worker_factory, NULL parameters\n");
+        return JK_FALSE;
+    }
+
+    if (! aw) {
+        jk_log(l, JK_LOG_ERROR, "In ajp14_worker_factory, malloc of private data failed\n");
+        return JK_FALSE;
+    }
+
+    aw->name = strdup(name);
+   
+    if (! aw->name) {
+        free(aw);
+        jk_log(l, JK_LOG_ERROR, "In ajp14_worker_factory, malloc failed for name\n");
+        return JK_FALSE;
+    }
+
+    aw->proto                  = AJP14_PROTO;
+
+    aw->login                  = (jk_login_service_t *)malloc(sizeof(jk_login_service_t));
+
+	if (aw->login == NULL) {
+		jk_log(l, JK_LOG_ERROR, "In ajp14_worker_factory, malloc failed for login area\n");
+		return JK_FALSE;
+	}
+	
+	memset(aw->login, 0, sizeof(jk_login_service_t));
+
+	aw->login->negociation	    = (AJP14_CONTEXT_INFO_NEG | AJP14_PROTO_SUPPORT_AJP14_NEG);
+	aw->login->web_server_name  = NULL; /* must be set in init */
+
+    aw->ep_cache_sz            = 0;
+    aw->ep_cache               = NULL;
+    aw->connect_retry_attempts = AJP_DEF_RETRY_ATTEMPTS;
+    aw->worker.worker_private  = aw;
+   
+    aw->worker.validate        = validate;
+    aw->worker.init            = init;
+    aw->worker.get_endpoint    = get_endpoint;
+    aw->worker.destroy         = destroy;
+
+	aw->logon				   = logon; /* LogOn Handler for AJP14 */
+    *w = &aw->worker;
+    return JK_TRUE;
+}
diff --git a/connectors/jk/native/common/jk_ajp14_worker.h b/connectors/jk/native/common/jk_ajp14_worker.h
new file mode 100644
index 0000000..8d02e45
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp14_worker.h
@@ -0,0 +1,50 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: ajpv14 worker header file                                  *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef JK_AJP14_WORKER_H
+#define JK_AJP14_WORKER_H
+
+#include "jk_pool.h"
+#include "jk_connect.h"
+#include "jk_util.h"
+#include "jk_msg_buff.h"
+#include "jk_ajp13.h"
+#include "jk_ajp14.h"
+#include "jk_logger.h"
+#include "jk_service.h"
+#include "jk_ajp13_worker.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define JK_AJP14_WORKER_NAME ("ajp14")
+
+int JK_METHOD ajp14_worker_factory(jk_worker_t **w,
+                                   const char *name,
+                                   jk_logger_t *l);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_AJP14_WORKER_H */
diff --git a/connectors/jk/native/common/jk_ajp_common.c b/connectors/jk/native/common/jk_ajp_common.c
new file mode 100644
index 0000000..e27bd84
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp_common.c
@@ -0,0 +1,1825 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: common stuff for bi-directional protocols ajp13/ajp14.     *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+
+#include "jk_global.h"
+#include "jk_util.h"
+#include "jk_ajp13.h"
+#include "jk_ajp14.h"
+#include "jk_ajp_common.h"
+#include "jk_connect.h"
+#ifdef AS400
+#include "util_ebcdic.h"
+#endif
+#if defined(NETWARE) && defined(__NOVELL_LIBC__)
+#include "novsock2.h"
+#endif
+
+
+const char *response_trans_headers[] = {
+    "Content-Type", 
+    "Content-Language", 
+    "Content-Length", 
+    "Date", 
+    "Last-Modified", 
+    "Location", 
+    "Set-Cookie", 
+    "Set-Cookie2", 
+    "Servlet-Engine", 
+    "Status", 
+    "WWW-Authenticate"
+};
+
+static const char *long_res_header_for_sc(int sc) 
+{
+    const char *rc = NULL;
+    if(sc <= SC_RES_HEADERS_NUM && sc > 0) {
+        rc = response_trans_headers[sc - 1];
+    }
+
+    return rc;
+}
+
+
+static int sc_for_req_method(const char    *method,
+                             unsigned char *sc) 
+{
+    int rc = JK_TRUE;
+    if(0 == strcmp(method, "GET")) {
+        *sc = SC_M_GET;
+    } else if(0 == strcmp(method, "POST")) {
+        *sc = SC_M_POST;
+    } else if(0 == strcmp(method, "HEAD")) {
+        *sc = SC_M_HEAD;
+    } else if(0 == strcmp(method, "PUT")) {
+        *sc = SC_M_PUT;
+    } else if(0 == strcmp(method, "DELETE")) {
+        *sc = SC_M_DELETE;
+    } else if(0 == strcmp(method, "OPTIONS")) {
+        *sc = SC_M_OPTIONS;
+    } else if(0 == strcmp(method, "TRACE")) {
+        *sc = SC_M_TRACE;
+    } else if(0 == strcmp(method, "PROPFIND")) {
+        *sc = SC_M_PROPFIND;
+    } else if(0 == strcmp(method, "PROPPATCH")) {
+        *sc = SC_M_PROPPATCH;
+    } else if(0 == strcmp(method, "MKCOL")) {
+        *sc = SC_M_MKCOL;
+    } else if(0 == strcmp(method, "COPY")) {
+        *sc = SC_M_COPY;
+    } else if(0 == strcmp(method, "MOVE")) {
+        *sc = SC_M_MOVE;
+    } else if(0 == strcmp(method, "LOCK")) {
+        *sc = SC_M_LOCK;
+    } else if(0 == strcmp(method, "UNLOCK")) {
+        *sc = SC_M_UNLOCK;
+    } else if(0 == strcmp(method, "ACL")) {
+        *sc = SC_M_ACL;
+    } else if(0 == strcmp(method, "REPORT")) {
+        *sc = SC_M_REPORT;
+    } else if(0 == strcmp(method, "VERSION-CONTROL")) {
+        *sc = SC_M_VERSION_CONTROL;
+    } else if(0 == strcmp(method, "CHECKIN")) {
+        *sc = SC_M_CHECKIN;
+    } else if(0 == strcmp(method, "CHECKOUT")) {
+        *sc = SC_M_CHECKOUT;
+    } else if(0 == strcmp(method, "UNCHECKOUT")) {
+        *sc = SC_M_UNCHECKOUT;
+    } else if(0 == strcmp(method, "SEARCH")) {
+        *sc = SC_M_SEARCH;
+    } else if(0 == strcmp(method, "MKWORKSPACE")) {
+        *sc = SC_M_MKWORKSPACE;
+    } else if(0 == strcmp(method, "UPDATE")) {
+        *sc = SC_M_UPDATE;
+    } else if(0 == strcmp(method, "LABEL")) {
+        *sc = SC_M_LABEL;
+    } else if(0 == strcmp(method, "MERGE")) {
+        *sc = SC_M_MERGE;
+    } else if(0 == strcmp(method, "BASELINE-CONTROL")) {
+        *sc = SC_M_BASELINE_CONTROL;
+    } else if(0 == strcmp(method, "MKACTIVITY")) {
+        *sc = SC_M_MKACTIVITY;
+    } else {
+        rc = JK_FALSE;
+    }
+
+    return rc;
+}
+
+static int sc_for_req_header(const char     *header_name,
+                             unsigned short *sc) 
+{
+    switch(header_name[0]) {
+        case 'a':
+            if('c' ==header_name[1] &&
+               'c' ==header_name[2] &&
+               'e' ==header_name[3] &&
+               'p' ==header_name[4] &&
+               't' ==header_name[5]) {
+                if ('-' == header_name[6]) {
+                    if(!strcmp(header_name + 7, "charset")) {
+                        *sc = SC_ACCEPT_CHARSET;
+                    } else if(!strcmp(header_name + 7, "encoding")) {
+                        *sc = SC_ACCEPT_ENCODING;
+                    } else if(!strcmp(header_name + 7, "language")) {
+                        *sc = SC_ACCEPT_LANGUAGE;
+                    } else {
+                        return JK_FALSE;
+                    }
+                } else if ('\0' == header_name[6]) {
+                    *sc = SC_ACCEPT;
+                } else {
+                    return JK_FALSE;
+                }
+            } else if (!strcmp(header_name, "authorization")) {
+                *sc = SC_AUTHORIZATION;
+            } else {
+                return JK_FALSE;
+            }
+        break;
+
+        case 'c':
+            if(!strcmp(header_name, "cookie")) {
+                *sc = SC_COOKIE;
+            } else if(!strcmp(header_name, "connection")) {
+                *sc = SC_CONNECTION;
+            } else if(!strcmp(header_name, "content-type")) {
+                *sc = SC_CONTENT_TYPE;
+            } else if(!strcmp(header_name, "content-length")) {
+                *sc = SC_CONTENT_LENGTH;
+            } else if(!strcmp(header_name, "cookie2")) {
+                *sc = SC_COOKIE2;
+            } else {
+                return JK_FALSE;
+            }
+        break;
+
+        case 'h':
+            if(!strcmp(header_name, "host")) {
+                *sc = SC_HOST;
+            } else {
+                return JK_FALSE;
+            }
+        break;
+
+        case 'p':
+            if(!strcmp(header_name, "pragma")) {
+                *sc = SC_PRAGMA;
+            } else {
+                return JK_FALSE;
+            }
+        break;
+
+        case 'r':
+            if(!strcmp(header_name, "referer")) {
+                *sc = SC_REFERER;
+            } else {
+                return JK_FALSE;
+            }
+        break;
+
+        case 'u':
+            if(!strcmp(header_name, "user-agent")) {
+                *sc = SC_USER_AGENT;
+            } else {
+                return JK_FALSE;
+            }
+        break;
+
+        default:
+            return JK_FALSE;
+    }
+
+    return JK_TRUE;
+}
+
+
+/*
+ * Message structure
+ *
+ *
+AJPV13_REQUEST/AJPV14_REQUEST=
+    request_prefix (1) (byte)
+    method         (byte)
+    protocol       (string)
+    req_uri        (string)
+    remote_addr    (string)
+    remote_host    (string)
+    server_name    (string)
+    server_port    (short)
+    is_ssl         (boolean)
+    num_headers    (short)
+    num_headers*(req_header_name header_value)
+
+    ?context       (byte)(string)
+    ?servlet_path  (byte)(string)
+    ?remote_user   (byte)(string)
+    ?auth_type     (byte)(string)
+    ?query_string  (byte)(string)
+    ?jvm_route     (byte)(string)
+    ?ssl_cert      (byte)(string)
+    ?ssl_cipher    (byte)(string)
+    ?ssl_session   (byte)(string)
+    ?ssl_key_size  (byte)(int)      via JkOptions +ForwardKeySize
+    request_terminator (byte)
+    ?body          content_length*(var binary)
+
+ */
+
+static int ajp_marshal_into_msgb(jk_msg_buf_t    *msg,
+                                 jk_ws_service_t *s,
+                                 jk_logger_t     *l,
+                                 ajp_endpoint_t  *ae)
+{
+    unsigned char method;
+    unsigned i;
+
+    jk_log(l, JK_LOG_DEBUG, "Into ajp_marshal_into_msgb\n");
+
+    if (!sc_for_req_method(s->method, &method)) { 
+        jk_log(l, JK_LOG_ERROR,
+               "Error ajp_marshal_into_msgb - No such method %s\n",
+               s->method);
+        return JK_FALSE;
+    }
+
+    if (jk_b_append_byte(msg, JK_AJP13_FORWARD_REQUEST)  ||
+        jk_b_append_byte(msg, method)               ||
+        jk_b_append_string(msg, s->protocol)        ||
+        jk_b_append_string(msg, s->req_uri)         ||
+        jk_b_append_string(msg, s->remote_addr)     ||
+        jk_b_append_string(msg, s->remote_host)     ||
+        jk_b_append_string(msg, s->server_name)     ||
+        jk_b_append_int(msg, (unsigned short)s->server_port) ||
+        jk_b_append_byte(msg, (unsigned char)(s->is_ssl)) ||
+        jk_b_append_int(msg, (unsigned short)(s->num_headers))) {
+
+        jk_log(l, JK_LOG_ERROR,
+               "Error ajp_marshal_into_msgb - "
+               "Error appending the message begining\n");
+        return JK_FALSE;
+    }
+
+    for (i = 0 ; i < s->num_headers ; i++) {
+        unsigned short sc;
+
+        if (sc_for_req_header(s->headers_names[i], &sc)) {
+            if (jk_b_append_int(msg, sc)) {
+                jk_log(l, JK_LOG_ERROR,
+                       "Error ajp_marshal_into_msgb - "
+                       "Error appending the header name\n");
+                return JK_FALSE;
+            }
+        } else {
+            if (jk_b_append_string(msg, s->headers_names[i])) {
+                jk_log(l, JK_LOG_ERROR,
+                       "Error ajp_marshal_into_msgb - "
+                       "Error appending the header name\n");
+                return JK_FALSE;
+            }
+        }
+        
+        if (jk_b_append_string(msg, s->headers_values[i])) {
+            jk_log(l, JK_LOG_ERROR,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the header value\n");
+            return JK_FALSE;
+        }
+    }
+
+    if (s->secret) {
+        if (jk_b_append_byte(msg, SC_A_SECRET) ||
+            jk_b_append_string(msg, s->secret)) {
+            jk_log(l, JK_LOG_ERROR,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending secret\n");
+            return JK_FALSE;
+        }
+    }
+        
+    if (s->remote_user) {
+        if (jk_b_append_byte(msg, SC_A_REMOTE_USER) ||
+            jk_b_append_string(msg, s->remote_user)) {
+            jk_log(l, JK_LOG_ERROR,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the remote user\n");
+            return JK_FALSE;
+        }
+    }
+    if (s->auth_type) {
+        if (jk_b_append_byte(msg, SC_A_AUTH_TYPE) ||
+            jk_b_append_string(msg, s->auth_type)) {
+            jk_log(l, JK_LOG_ERROR,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the auth type\n");
+            return JK_FALSE;
+        }
+    }
+    if (s->query_string) {
+        if (jk_b_append_byte(msg, SC_A_QUERY_STRING) ||
+#ifdef AS400
+            jk_b_append_asciistring(msg, s->query_string)) {
+#else
+            jk_b_append_string(msg, s->query_string)) {
+#endif
+            jk_log(l, JK_LOG_ERROR,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the query string\n");
+            return JK_FALSE;
+        }
+    }
+    if (s->jvm_route) {
+        if (jk_b_append_byte(msg, SC_A_JVM_ROUTE) ||
+            jk_b_append_string(msg, s->jvm_route)) {
+            jk_log(l, JK_LOG_ERROR,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the jvm route\n");
+            return JK_FALSE;
+        }
+    }
+    if (s->ssl_cert_len) {
+        if (jk_b_append_byte(msg, SC_A_SSL_CERT) ||
+            jk_b_append_string(msg, s->ssl_cert)) {
+            jk_log(l, JK_LOG_ERROR,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the SSL certificates\n");
+            return JK_FALSE;
+        }
+    }
+
+    if (s->ssl_cipher) {
+        if (jk_b_append_byte(msg, SC_A_SSL_CIPHER) ||
+            jk_b_append_string(msg, s->ssl_cipher)) {
+            jk_log(l, JK_LOG_ERROR,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the SSL ciphers\n");
+            return JK_FALSE;
+        }
+    }
+    if (s->ssl_session) {
+        if (jk_b_append_byte(msg, SC_A_SSL_SESSION) ||
+            jk_b_append_string(msg, s->ssl_session)) {
+            jk_log(l, JK_LOG_ERROR,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the SSL session\n");
+            return JK_FALSE;
+        }
+    }
+
+    /*
+     * ssl_key_size is required by Servlet 2.3 API
+     * added support only in ajp14 mode
+     * JFC removed: ae->proto == AJP14_PROTO
+     */
+    if (s->ssl_key_size != -1) {
+        if (jk_b_append_byte(msg, SC_A_SSL_KEY_SIZE) ||
+            jk_b_append_int(msg, (unsigned short) s->ssl_key_size)) {
+            jk_log(l, JK_LOG_ERROR,
+                   "Error ajp_marshal_into_msgb - "
+                   "Error appending the SSL key size\n");
+            return JK_FALSE;
+        }
+    }
+
+    if (s->num_attributes > 0) {
+        for (i = 0 ; i < s->num_attributes ; i++) {
+            if (jk_b_append_byte(msg, SC_A_REQ_ATTRIBUTE)       ||
+                jk_b_append_string(msg, s->attributes_names[i]) ||
+                jk_b_append_string(msg, s->attributes_values[i])) {
+                jk_log(l, JK_LOG_ERROR,
+                      "Error ajp_marshal_into_msgb - "
+                      "Error appending attribute %s=%s\n",
+                      s->attributes_names[i], s->attributes_values[i]);
+                return JK_FALSE;
+            }
+        }
+    }
+
+    if (jk_b_append_byte(msg, SC_A_ARE_DONE)) {
+        jk_log(l, JK_LOG_ERROR,
+               "Error ajp_marshal_into_msgb - "
+               "Error appending the message end\n");
+        return JK_FALSE;
+    }
+    
+    jk_log(l, JK_LOG_DEBUG, "ajp_marshal_into_msgb - Done\n");
+    return JK_TRUE;
+}
+
+/*
+AJPV13_RESPONSE/AJPV14_RESPONSE:=
+    response_prefix (2)
+    status          (short)
+    status_msg      (short)
+    num_headers     (short)
+    num_headers*(res_header_name header_value)
+    *body_chunk
+    terminator      boolean <! -- recycle connection or not  -->
+
+req_header_name := 
+    sc_req_header_name | (string)
+
+res_header_name := 
+    sc_res_header_name | (string)
+
+header_value :=
+    (string)
+
+body_chunk :=
+    length  (short)
+    body    length*(var binary)
+
+ */
+
+
+static int ajp_unmarshal_response(jk_msg_buf_t   *msg,
+                                  jk_res_data_t  *d,
+                                  ajp_endpoint_t *ae,
+                                  jk_logger_t    *l)
+{
+    jk_pool_t * p = &ae->pool;
+
+    d->status = jk_b_get_int(msg);
+
+    if (!d->status) {
+        jk_log(l, JK_LOG_ERROR,
+               "Error ajp_unmarshal_response - Null status\n");
+        return JK_FALSE;
+    }
+
+    d->msg = (char *)jk_b_get_string(msg);
+    if (d->msg) {
+#if defined(AS400) || defined(_OSD_POSIX)
+        jk_xlate_from_ascii(d->msg, strlen(d->msg));
+#endif
+    }
+
+    jk_log(l, JK_LOG_DEBUG,
+           "ajp_unmarshal_response: status = %d\n", d->status);
+
+    d->num_headers = jk_b_get_int(msg);
+    d->header_names = d->header_values = NULL;
+
+    jk_log(l, JK_LOG_DEBUG,
+           "ajp_unmarshal_response: Number of headers is = %d\n",
+           d->num_headers);
+
+    if (d->num_headers) {
+        d->header_names = jk_pool_alloc(p, sizeof(char *) * d->num_headers);
+        d->header_values = jk_pool_alloc(p, sizeof(char *) * d->num_headers);
+
+        if (d->header_names && d->header_values) {
+            unsigned i;
+            for(i = 0 ; i < d->num_headers ; i++) {
+                unsigned short name = jk_b_pget_int(msg, jk_b_get_pos(msg)) ;
+                
+                if ((name & 0XFF00) == 0XA000) {
+                    jk_b_get_int(msg);
+                    name = name & 0X00FF;
+                    if (name <= SC_RES_HEADERS_NUM) {
+                        d->header_names[i] =
+                            (char *)long_res_header_for_sc(name);
+                    } else {
+                        jk_log(l, JK_LOG_ERROR,
+                               "Error ajp_unmarshal_response - "
+                               "No such sc (%d)\n",
+                               name);
+                        return JK_FALSE;
+                    }
+                } else {
+                    d->header_names[i] = (char *)jk_b_get_string(msg);
+                    if (!d->header_names[i]) {
+                        jk_log(l, JK_LOG_ERROR,
+                               "Error ajp_unmarshal_response - "
+                               "Null header name\n");
+                        return JK_FALSE;
+                    }
+#if defined(AS400) || defined(_OSD_POSIX)
+                    jk_xlate_from_ascii(d->header_names[i],
+                                 strlen(d->header_names[i]));
+#endif
+
+                }
+
+                d->header_values[i] = (char *)jk_b_get_string(msg);
+                if (!d->header_values[i]) {
+                    jk_log(l, JK_LOG_ERROR,
+                           "Error ajp_unmarshal_response - "
+                           "Null header value\n");
+                    return JK_FALSE;
+                }
+
+#if defined(AS400) || defined(_OSD_POSIX)
+                jk_xlate_from_ascii(d->header_values[i],
+                             strlen(d->header_values[i]));
+#endif
+
+                jk_log(l, JK_LOG_DEBUG,
+                       "ajp_unmarshal_response: Header[%d] [%s] = [%s]\n", 
+                       i,
+                       d->header_names[i],
+                       d->header_values[i]);
+            }
+        }
+    }
+
+    return JK_TRUE;
+}
+
+
+/*
+ * Reset the endpoint (clean buf)
+ */
+
+static void ajp_reset_endpoint(ajp_endpoint_t *ae)
+{
+    ae->reuse = JK_FALSE;
+    jk_reset_pool(&(ae->pool));
+}
+
+/*
+ * Close the endpoint (clean buf and close socket)
+ */
+
+void ajp_close_endpoint(ajp_endpoint_t *ae,
+                        jk_logger_t    *l)
+{
+    jk_log(l, JK_LOG_DEBUG, "In jk_endpoint_t::ajp_close_endpoint\n");
+
+    ajp_reset_endpoint(ae);
+    jk_close_pool(&(ae->pool));
+
+    if (ae->sd > 0) { 
+        jk_close_socket(ae->sd);
+        jk_log(l, JK_LOG_DEBUG,
+               "In jk_endpoint_t::ajp_close_endpoint, closed sd = %d\n",
+               ae->sd);
+        ae->sd = -1; /* just to avoid twice close */
+    }
+
+    free(ae);
+}
+
+
+/*
+ * Try to reuse a previous connection
+ */
+
+static void ajp_reuse_connection(ajp_endpoint_t *ae,
+                                 jk_logger_t    *l)
+{
+    ajp_worker_t *aw = ae->worker;
+
+    if (aw->ep_cache_sz) {
+        int rc;
+        JK_ENTER_CS(&aw->cs, rc);
+        if (rc) {
+            unsigned i;
+
+            for (i = 0 ; i < aw->ep_cache_sz ; i++) {
+                if (aw->ep_cache[i]) {
+                    ae->sd = aw->ep_cache[i]->sd;
+                    aw->ep_cache[i]->sd = -1;
+                    ajp_close_endpoint(aw->ep_cache[i], l);
+                    aw->ep_cache[i] = NULL;
+                    break;
+                 }
+            }
+            JK_LEAVE_CS(&aw->cs, rc);
+        }
+    }
+}
+
+/*
+ * Wait input event on ajp_endpoint for timeout ms
+ */
+int ajp_is_input_event(ajp_endpoint_t *ae,
+                       int            timeout,
+                       jk_logger_t   *l)
+{
+    fd_set  rset; 
+    fd_set  eset; 
+    struct  timeval tv;
+    int     rc;
+    
+    FD_ZERO(&rset);
+    FD_ZERO(&eset);
+    FD_SET(ae->sd, &rset);
+    FD_SET(ae->sd, &eset);
+
+    tv.tv_sec  = timeout / 1000;
+    tv.tv_usec = (timeout % 1000) * 1000;
+
+    rc = select(ae->sd + 1, &rset, NULL, &eset, &tv);
+      
+    if ((rc < 1) || (FD_ISSET(ae->sd, &eset)))
+    {
+        jk_log(l, JK_LOG_ERROR, "Error ajp13:is_input_event: error during select [%d]\n", rc);
+        return JK_FALSE;
+    }
+    
+    return ((FD_ISSET(ae->sd, &rset)) ? JK_TRUE : JK_FALSE) ;
+}
+
+                         
+/*
+ * Handle the CPING/CPONG initial query
+ */
+int ajp_handle_cping_cpong(ajp_endpoint_t *ae,
+                           int            timeout,
+                           jk_logger_t    *l)
+{
+    int    cmd;
+    jk_msg_buf_t * msg;
+
+    msg = jk_b_new(&ae->pool);
+    jk_b_set_buffer_size(msg, 16);    /* 16 is way too large but I'm lazy :-) */
+    jk_b_reset(msg);
+    jk_b_append_byte(msg, AJP13_CPING_REQUEST); 
+
+    /* Send CPing query */        
+    if (ajp_connection_tcp_send_message(ae, msg, l) != JK_TRUE)
+    {
+        jk_log(l, JK_LOG_ERROR, "Error ajp13:cping: can't send cping query\n");
+        return JK_FALSE;
+    }
+        
+    /* wait for Pong reply for timeout milliseconds
+     */
+    if (ajp_is_input_event(ae, timeout, l) == JK_FALSE)
+    {
+        jk_log(l, JK_LOG_ERROR, "Error ajp13:cping: timeout in reply pong\n");
+        return JK_FALSE;
+    }
+        
+    /* Read and check for Pong reply 
+     */
+    if (ajp_connection_tcp_get_message(ae, msg, l) != JK_TRUE)
+    {
+        jk_log(l, JK_LOG_ERROR, "Error ajp13:cping: awaited reply cpong, not received\n");
+        return JK_FALSE;
+    }
+    
+    if ((cmd = jk_b_get_byte(msg)) != AJP13_CPONG_REPLY) {
+        jk_log(l, JK_LOG_ERROR, "Error ajp13:cping: awaited reply cpong, received %d instead\n", cmd);
+        return JK_FALSE;
+    }
+
+    return JK_TRUE;
+}
+
+int ajp_connect_to_endpoint(ajp_endpoint_t *ae,
+                            jk_logger_t    *l)
+{
+	char buf[32];
+    unsigned attempt;
+
+    for(attempt = 0; attempt < ae->worker->connect_retry_attempts; attempt++) {
+        ae->sd = jk_open_socket(&ae->worker->worker_inet_addr, JK_TRUE,
+                                ae->worker->keepalive, l);
+        if(ae->sd >= 0) {
+            jk_log(l, JK_LOG_DEBUG,
+                   "In jk_endpoint_t::ajp_connect_to_endpoint, "
+                   "connected sd = %d to %s\n",
+                   ae->sd, jk_dump_hinfo(&ae->worker->worker_inet_addr, buf));
+
+             /* set last_access */
+             ae->last_access = time(NULL);
+            /* Check if we must execute a logon after the physical connect */
+            if (ae->worker->logon != NULL)
+                return (ae->worker->logon(ae, l));
+
+            /* should we send a CPING to validate connection ? */
+            if (ae->worker->connect_timeout != 0)
+                return (ajp_handle_cping_cpong(ae, ae->worker->connect_timeout, l));
+                
+            return JK_TRUE;
+        }
+    }
+
+    jk_log(l, JK_LOG_INFO,
+           "Error connecting to tomcat. Tomcat is probably not started or is "
+           "listening on the wrong host/port (%s). Failed errno = %d\n",
+           jk_dump_hinfo(&ae->worker->worker_inet_addr, buf), errno);
+    return JK_FALSE;
+}
+
+/*
+ * Send a message to endpoint, using corresponding PROTO HEADER
+ */
+
+int ajp_connection_tcp_send_message(ajp_endpoint_t *ae,
+                                    jk_msg_buf_t   *msg,
+                                    jk_logger_t    *l)
+{
+    if (ae->proto == AJP13_PROTO) {
+        jk_b_end(msg, AJP13_WS_HEADER);
+        jk_dump_buff(l, JK_LOG_DEBUG, "sending to ajp13", msg);
+    }
+    else if (ae->proto == AJP14_PROTO) {
+        jk_b_end(msg, AJP14_WS_HEADER);
+        jk_dump_buff(l, JK_LOG_DEBUG, "sending to ajp14", msg);
+    }
+    else {
+        jk_log(l, JK_LOG_ERROR,
+               "In jk_endpoint_t::ajp_connection_tcp_send_message, "
+               "unknown protocol %d, supported are AJP13/AJP14\n",
+               ae->proto);
+        return JK_FALSE;
+    }
+
+    if(0 > jk_tcp_socket_sendfull(ae->sd, jk_b_get_buff(msg), jk_b_get_len(msg))) {
+        return JK_FALSE;
+    }
+
+    return JK_TRUE;
+}
+
+/*
+ * Receive a message from endpoint, checking PROTO HEADER
+ */
+
+int ajp_connection_tcp_get_message(ajp_endpoint_t *ae,
+                                   jk_msg_buf_t   *msg,
+                                   jk_logger_t    *l)
+{
+    unsigned char head[AJP_HEADER_LEN];
+    int           rc;
+    int           msglen;
+    unsigned int  header;
+    char buf[32];
+
+    if ((ae->proto != AJP13_PROTO) && (ae->proto != AJP14_PROTO)) {
+        jk_log(l, JK_LOG_ERROR,
+               "ajp_connection_tcp_get_message: "
+               "Can't handle unknown protocol %d\n",
+               ae->proto);
+        return JK_FALSE;
+    }
+
+    rc = jk_tcp_socket_recvfull(ae->sd, head, AJP_HEADER_LEN);
+
+    if(rc < 0) {
+        jk_log(l, JK_LOG_ERROR,
+               "ERROR: can't receive the response message from tomcat, "
+               "network problems or tomcat is down (%s), err=%d\n",
+               jk_dump_hinfo(&ae->worker->worker_inet_addr, buf), rc);
+        return JK_FALSE;
+    }
+
+    header = ((unsigned int)head[0] << 8) | head[1];
+  
+    if (ae->proto == AJP13_PROTO) {
+        if (header != AJP13_SW_HEADER) {
+
+            if (header == AJP14_SW_HEADER) {
+                jk_log(l, JK_LOG_ERROR,
+                       "ajp_connection_tcp_get_message: "
+                       "Error - received AJP14 reply on an AJP13 connection from %s\n",
+                       jk_dump_hinfo(&ae->worker->worker_inet_addr, buf));
+            } else {
+                jk_log(l, JK_LOG_ERROR,
+                       "ajp_connection_tcp_get_message: "
+                       "Error - Wrong message format 0x%04x from %s\n",
+                       header, jk_dump_hinfo(&ae->worker->worker_inet_addr, buf));
+            }
+            return JK_FALSE;
+        }
+    }
+    else if (ae->proto == AJP14_PROTO) {
+        if (header != AJP14_SW_HEADER) {
+
+            if (header == AJP13_SW_HEADER) {
+                jk_log(l, JK_LOG_ERROR,
+                       "ajp_connection_tcp_get_message: "
+                       "Error - received AJP13 reply on an AJP14 connection from %s\n",
+                       jk_dump_hinfo(&ae->worker->worker_inet_addr, buf));
+            } else {
+                jk_log(l, JK_LOG_ERROR,
+                       "ajp_connection_tcp_get_message: "
+                       "Error - Wrong message format 0x%04x from %s\n",
+                       header, jk_dump_hinfo(&ae->worker->worker_inet_addr, buf));
+            }
+            return JK_FALSE;
+        }
+    }   
+
+    msglen  = ((head[2]&0xff)<<8);
+    msglen += (head[3] & 0xFF);
+
+    if(msglen > jk_b_get_size(msg)) {
+        jk_log(l, JK_LOG_ERROR,
+               "ajp_connection_tcp_get_message: "
+               "Error - Wrong message size %d %d from %s\n",
+               msglen, jk_b_get_size(msg), jk_dump_hinfo(&ae->worker->worker_inet_addr, buf));
+        return JK_FALSE;
+    }
+
+    jk_b_set_len(msg, msglen);
+    jk_b_set_pos(msg, 0);
+
+    rc = jk_tcp_socket_recvfull(ae->sd, jk_b_get_buff(msg), msglen);
+    if(rc < 0) {
+        jk_log(l, JK_LOG_ERROR,
+               "ERROR: can't receive the response message from tomcat, "
+               "network problems or tomcat (%s) is down %d\n",
+               jk_dump_hinfo(&ae->worker->worker_inet_addr, buf), rc);
+        return JK_FALSE;
+    }
+
+    if (ae->proto == AJP13_PROTO) {
+        jk_dump_buff(l, JK_LOG_DEBUG, "received from ajp13", msg);
+    } else if (ae->proto == AJP14_PROTO) {
+        jk_dump_buff(l, JK_LOG_DEBUG, "received from ajp14", msg);
+    }
+    return JK_TRUE;
+}
+
+/*
+ * Read all the data from the socket.
+ *
+ * Socket API doesn't guaranty that all the data will be kept in a
+ * single read, so we must loop until all awaited data is received 
+ */
+
+static int ajp_read_fully_from_server(jk_ws_service_t *s,
+                                      unsigned char   *buf,
+                                      unsigned         len)
+{
+    unsigned rdlen = 0;
+    unsigned padded_len = len;
+
+    if (s->is_chunked && s->no_more_chunks) {
+        return 0;
+    }
+    if (s->is_chunked) {
+        /* Corner case: buf must be large enough to hold next
+         * chunk size (if we're on or near a chunk border).
+         * Pad the length to a reasonable value, otherwise the
+         * read fails and the remaining chunks are tossed.
+         */
+        padded_len = (len < CHUNK_BUFFER_PAD) ?
+                      len : len - CHUNK_BUFFER_PAD;
+    }
+
+    while(rdlen < padded_len) {
+        unsigned this_time = 0;
+        if(!s->read(s, buf + rdlen, len - rdlen, &this_time)) {
+            /* Remote Client read failed. */
+            return JK_CLIENT_ERROR;
+        }
+
+        if(0 == this_time) {
+            if (s->is_chunked) {
+                s->no_more_chunks = 1; /* read no more */
+            }
+            break;
+        }
+        rdlen += this_time;
+    }
+
+    return (int)rdlen;
+}
+
+
+/*
+ * Read data from AJP13/AJP14 protocol
+ * Returns -1 on error, else number of bytes read
+ */
+
+static int ajp_read_into_msg_buff(ajp_endpoint_t  *ae,
+                                  jk_ws_service_t *r,
+                                  jk_msg_buf_t    *msg,
+                                  int         len,
+                                  jk_logger_t     *l)
+{
+    unsigned char *read_buf = jk_b_get_buff(msg);
+
+    jk_b_reset(msg);
+
+    read_buf += AJP_HEADER_LEN;    /* leave some space for the buffer headers */
+    read_buf += AJP_HEADER_SZ_LEN; /* leave some space for the read length */
+
+    /* Pick the max size since we don't know the content_length */
+    if (r->is_chunked && len == 0) {
+        len = AJP13_MAX_SEND_BODY_SZ;
+    }
+
+    if ((len = ajp_read_fully_from_server(r, read_buf, len)) < 0) {
+        jk_log(l, JK_LOG_INFO,
+               "ERROR: receiving data from client failed. "
+               "Connection aborted or network problems\n");
+        return JK_CLIENT_ERROR;
+    }
+
+    if (!r->is_chunked) {
+        ae->left_bytes_to_send -= len;
+    }
+
+    if (len > 0) {
+        /* Recipient recognizes empty packet as end of stream, not
+           an empty body packet */
+        if(0 != jk_b_append_int(msg, (unsigned short)len)) {
+            jk_log(l, JK_LOG_INFO, 
+                   "read_into_msg_buff: Error - jk_b_append_int failed\n");
+            return JK_CLIENT_ERROR;
+        }
+    }
+
+    jk_b_set_len(msg, jk_b_get_len(msg) + len);
+
+    return len;
+}
+
+
+/*
+ * send request to Tomcat via Ajp13
+ * - first try to find reuseable socket
+ * - if no one available, try to connect
+ * - send request, but send must be see as asynchronous,
+ *   since send() call will return noerror about 95% of time
+ *   Hopefully we'll get more information on next read.
+ * 
+ * nb: reqmsg is the original request msg buffer
+ *     repmsg is the reply msg buffer which could be scratched
+ */
+static int ajp_send_request(jk_endpoint_t *e,
+                            jk_ws_service_t *s,
+                            jk_logger_t *l,
+                            ajp_endpoint_t *ae,
+                            ajp_operation_t *op)
+{
+    int err = 0;
+    int postlen;
+    
+    /* Up to now, we can recover */
+    op->recoverable = JK_TRUE;
+
+    /*
+     * First try to reuse open connections...
+     */
+    while ((ae->sd > 0))
+    {
+        err = 0;
+        
+        /* handle cping/cpong before request if timeout is set */
+        if (ae->worker->prepost_timeout != 0)
+        {
+            if (ajp_handle_cping_cpong(ae, ae->worker->prepost_timeout, l) == JK_FALSE)
+                err++;
+        }    
+
+        /* If we got an error or can't send data, then try to get a pooled
+         * connection and try again.  If we are succesful, break out of this
+         * loop. */
+        if (err || ajp_connection_tcp_send_message(ae, op->request, l) == JK_FALSE) {
+            jk_log(l, JK_LOG_INFO,
+                   "Error sending request try another pooled connection\n");
+            jk_close_socket(ae->sd);
+            ae->sd = -1;
+            ajp_reuse_connection(ae, l);
+        }
+        else
+            break;
+    }
+    
+    /*
+     * If we failed to reuse a connection, try to reconnect.
+     */
+    if (ae->sd < 0) {
+
+        /* no need to handle cping/cpong here since it should be at connection time */
+
+        if (ajp_connect_to_endpoint(ae, l) == JK_TRUE) {
+            /*
+             * After we are connected, each error that we are going to
+             * have is probably unrecoverable
+             */
+            if (!ajp_connection_tcp_send_message(ae, op->request, l)) {
+                jk_log(l, JK_LOG_INFO,
+                       "Error sending request on a fresh connection\n");
+                return JK_FALSE;
+            }
+        } else {
+            jk_log(l, JK_LOG_INFO, 
+                   "Error connecting to the Tomcat process.\n");
+            return JK_FALSE;
+        }
+    }
+
+    /*
+     * From now on an error means that we have an internal server error
+     * or Tomcat crashed. In any case we cannot recover this.
+     */
+
+    jk_log(l, JK_LOG_DEBUG,
+           "ajp_send_request 2: "
+           "request body to send %d - request body to resend %d\n", 
+           ae->left_bytes_to_send, jk_b_get_len(op->reply) - AJP_HEADER_LEN);
+
+    /*
+     * POST recovery job is done here and will work when data to 
+     * POST are less than 8k, since it's the maximum size of op-post buffer.
+     * We send here the first part of data which was sent previously to the
+     * remote Tomcat
+     */
+     
+    /* Did we have something to resend (ie the op-post has been feeded previously */
+    
+    postlen = jk_b_get_len(op->post);    
+    if (postlen > AJP_HEADER_LEN) {
+        if(!ajp_connection_tcp_send_message(ae, op->post, l)) {
+            jk_log(l, JK_LOG_ERROR, "Error resending request body (%d)\n", postlen);
+            return JK_FALSE;
+        }
+        else
+            jk_log(l, JK_LOG_DEBUG, "Resent the request body (%d)\n", postlen);
+    }
+    else if (s->reco_status == RECO_FILLED)
+    {
+    /* Recovery in LB MODE */
+        postlen = jk_b_get_len(s->reco_buf);
+
+    if (postlen > AJP_HEADER_LEN) {
+        if(!ajp_connection_tcp_send_message(ae, s->reco_buf, l)) {
+            jk_log(l, JK_LOG_ERROR, "Error resending request body (lb mode) (%d)\n", postlen);
+            return JK_FALSE;
+        }
+    }
+    else
+        jk_log(l, JK_LOG_DEBUG, "Resent the request body (lb mode) (%d)\n", postlen);
+    }
+    else {
+        /* We never sent any POST data and we check if we have to send at
+         * least one block of data (max 8k). These data will be kept in reply
+         * for resend if the remote Tomcat is down, a fact we will learn only
+         * doing a read (not yet) 
+         */
+        /* || s->is_chunked - this can't be done here. The original protocol
+           sends the first chunk of post data ( based on Content-Length ),
+           and that's what the java side expects.
+           Sending this data for chunked would break other ajp13 servers.
+
+           Note that chunking will continue to work - using the normal read.
+         */
+
+        if (ae->left_bytes_to_send > 0) {
+            int len = ae->left_bytes_to_send;
+            if (len > AJP13_MAX_SEND_BODY_SZ) {
+                len = AJP13_MAX_SEND_BODY_SZ;
+            }
+            if ((len = ajp_read_into_msg_buff(ae, s, op->post, len, l)) < 0) {
+                /* the browser stop sending data, no need to recover */
+                op->recoverable = JK_FALSE;
+                return JK_CLIENT_ERROR;
+            }
+
+       /* If a RECOVERY buffer is available in LB mode, fill it */
+       if (s->reco_status == RECO_INITED) {
+        jk_b_copy(op->post, s->reco_buf);
+        s->reco_status = RECO_FILLED;
+       }
+
+            s->content_read = len;
+            if (!ajp_connection_tcp_send_message(ae, op->post, l)) {
+                jk_log(l, JK_LOG_ERROR, "Error sending request body\n");
+                return JK_FALSE;
+            }  
+        }
+    }
+    return (JK_TRUE);
+}
+
+/*
+ * What to do with incoming data (dispatcher)
+ */
+
+static int ajp_process_callback(jk_msg_buf_t *msg, 
+                                jk_msg_buf_t *pmsg,
+                                ajp_endpoint_t *ae,
+                                jk_ws_service_t *r, 
+                                jk_logger_t *l) 
+{
+    int code = (int)jk_b_get_byte(msg);
+
+    switch(code) {
+        case JK_AJP13_SEND_HEADERS:
+            {
+                jk_res_data_t res;
+                if (!ajp_unmarshal_response(msg, &res, ae, l)) {
+                    jk_log(l, JK_LOG_ERROR,
+                           "Error ajp_process_callback - "
+                           "ajp_unmarshal_response failed\n");
+                    return JK_AJP13_ERROR;
+                }
+                r->start_response(r, res.status, res.msg, 
+                                  (const char * const *)res.header_names,
+                                  (const char * const *)res.header_values,
+                                  res.num_headers);
+            }
+        return JK_AJP13_SEND_HEADERS;
+
+        case JK_AJP13_SEND_BODY_CHUNK:
+            {
+                unsigned len = (unsigned)jk_b_get_int(msg);
+                if(!r->write(r, jk_b_get_buff(msg) + jk_b_get_pos(msg), len)) {
+                    jk_log(l, JK_LOG_INFO,
+                           "ERROR sending data to client. "
+                           "Connection aborted or network problems\n");
+                    return JK_CLIENT_ERROR;
+                }
+            }
+        break;
+
+        case JK_AJP13_GET_BODY_CHUNK:
+            {
+                int len = (int)jk_b_get_int(msg);
+
+                if(len < 0) {
+                    len = 0;
+                }
+                if(len > AJP13_MAX_SEND_BODY_SZ) {
+                    len = AJP13_MAX_SEND_BODY_SZ;
+                }
+                if((unsigned int)len > ae->left_bytes_to_send) {
+                    len = ae->left_bytes_to_send;
+                }
+
+                /* the right place to add file storage for upload */
+                if ((len = ajp_read_into_msg_buff(ae, r, pmsg, len, l)) >= 0) {
+                    r->content_read += len;
+                    return JK_AJP13_HAS_RESPONSE;
+                }                  
+
+                jk_log(l, JK_LOG_INFO,
+                       "ERROR reading POST data from client. "
+                       "Connection aborted or network problems\n");
+                       
+                return JK_CLIENT_ERROR;       
+            }
+        break;
+
+        case JK_AJP13_END_RESPONSE:
+            {
+                ae->reuse = (int)jk_b_get_byte(msg);
+
+                if( ! ae->reuse ) {
+                    /*
+                     * Strange protocol error.
+                     */
+                    jk_log(l, JK_LOG_DEBUG, "Reuse: %d\n", ae->reuse );
+                    ae->reuse = JK_FALSE;
+                }
+                /* Reuse in all cases */
+                ae->reuse = JK_TRUE;
+            }
+            return JK_AJP13_END_RESPONSE;
+        break;
+
+        default:
+            jk_log(l, JK_LOG_ERROR,
+                   "Error ajp_process_callback - Invalid code: %d\n", code);
+            return JK_AJP13_ERROR;
+    }
+    
+    return JK_AJP13_NO_RESPONSE;
+}
+
+/*
+ * get replies from Tomcat via Ajp13/Ajp14
+ * We will know only at read time if the remote host closed
+ * the connection (half-closed state - FIN-WAIT2). In that case
+ * we must close our side of the socket and abort emission.
+ * We will need another connection to send the request
+ * There is need of refactoring here since we mix 
+ * reply reception (tomcat -> apache) and request send (apache -> tomcat)
+ * and everything using the same buffer (repmsg)
+ * ajp13/ajp14 is async but handling read/send this way prevent nice recovery
+ * In fact if tomcat link is broken during upload (browser -> apache -> tomcat)
+ * we'll loose data and we'll have to abort the whole request.
+ */
+static int ajp_get_reply(jk_endpoint_t *e,
+                         jk_ws_service_t *s,
+                         jk_logger_t *l,
+                         ajp_endpoint_t *p,
+                         ajp_operation_t *op)
+{
+    /* Don't get header from tomcat yet */
+    int headeratclient = JK_FALSE;
+
+    /* Start read all reply message */
+    while(1) {
+        int rc = 0;
+
+        /* If we set a reply timeout, check it something is available */
+        if (p->worker->reply_timeout != 0)
+        {
+            if (ajp_is_input_event(p, p->worker->reply_timeout, l) == JK_FALSE)
+            {
+                jk_log(l, JK_LOG_ERROR,
+                       "Timeout will waiting reply from tomcat. "
+                       "Tomcat is down, stopped or network problems.\n");
+
+                return JK_FALSE;
+            }
+        }
+        
+        if(!ajp_connection_tcp_get_message(p, op->reply, l)) {
+            /* we just can't recover, unset recover flag */
+            if(headeratclient == JK_FALSE) {
+                jk_log(l, JK_LOG_ERROR,
+                       "Tomcat is down or network problems. "
+                       "No response has been sent to the client (yet)\n");
+             /*
+              * communication with tomcat has been interrupted BEFORE 
+              * headers have been sent to the client.
+              * DISCUSSION: As we suppose that tomcat has already started
+              * to process the query we think it's unrecoverable (and we
+              * should not retry or switch to another tomcat in the 
+              * cluster). 
+              */
+              
+              /*
+               * We mark it unrecoverable if recovery_opts set to RECOVER_ABORT_IF_TCGETREQUEST 
+               */
+                if (p->worker->recovery_opts & RECOVER_ABORT_IF_TCGETREQUEST)
+                    op->recoverable = JK_FALSE;
+              /* 
+               * we want to display the webservers error page, therefore
+               * we return JK_FALSE 
+               */
+               return JK_FALSE;
+            } else {
+                    jk_log(l, JK_LOG_ERROR,
+                       "Error reading reply from tomcat. "
+                       "Tomcat is down or network problems. "
+                       "Part of the response has already been sent to the client\n");
+                      
+                    /* communication with tomcat has been interrupted AFTER 
+                     * headers have been sent to the client.
+                     * headers (and maybe parts of the body) have already been
+                     * sent, therefore the response is "complete" in a sense
+                     * that nobody should append any data, especially no 500 error 
+                     * page of the webserver! 
+                     *
+                     * BUT if you retrun JK_TRUE you have a 200 (OK) code in your
+                     * in your apache access.log instead of a 500 (Error). 
+                     * Therefore return FALSE/FALSE
+                     * return JK_TRUE; 
+                     */
+                
+                    /*
+                     * We mark it unrecoverable if recovery_opts set to RECOVER_ABORT_IF_TCSENDHEADER 
+                     */
+                    if (p->worker->recovery_opts & RECOVER_ABORT_IF_TCSENDHEADER)
+                        op->recoverable = JK_FALSE;
+                    
+                return JK_FALSE;
+            }
+        }
+        
+        rc = ajp_process_callback(op->reply, op->post, p, s, l);
+
+        /* no more data to be sent, fine we have finish here */
+        if(JK_AJP13_END_RESPONSE == rc) {
+            return JK_TRUE;
+        } else if(JK_AJP13_SEND_HEADERS == rc) {
+             headeratclient = JK_TRUE;            
+        } else if(JK_AJP13_HAS_RESPONSE == rc) {
+            /* 
+             * in upload-mode there is no second chance since
+             * we may have allready sent part of the uploaded data 
+             * to Tomcat.
+             * In this case if Tomcat connection is broken we must 
+             * abort request and indicate error.
+             * A possible work-around could be to store the uploaded
+             * data to file and replay for it
+             */
+            op->recoverable = JK_FALSE; 
+            rc = ajp_connection_tcp_send_message(p, op->post, l);
+            if (rc < 0) {
+                jk_log(l, JK_LOG_ERROR,
+                       "Error sending request data %d. "
+                       "Tomcat is down or network problems.\n",
+                       rc);
+                return JK_FALSE;
+            }
+        } else if(JK_FATAL_ERROR == rc) {
+            /*
+             * we won't be able to gracefully recover from this so
+             * set recoverable to false and get out.
+             */
+            op->recoverable = JK_FALSE;
+            return JK_FALSE;
+        } else if(JK_CLIENT_ERROR == rc) {
+            /*
+             * Client has stop talking to us, so get out.
+             * We assume this isn't our fault, so just a normal exit.
+             * In most (all?)  cases, the ajp13_endpoint::reuse will still be
+             * false here, so this will be functionally the same as an
+             * un-recoverable error.  We just won't log it as such.
+             */
+            return JK_CLIENT_ERROR;
+        } else if(rc < 0) {
+            return (JK_FALSE); /* XXX error */
+        }
+    }
+}
+
+#define JK_RETRIES 3
+
+/*
+ * service is now splitted in ajp_send_request and ajp_get_reply
+ * much more easier to do errors recovery
+ *
+ * We serve here the request, using AJP13/AJP14 (e->proto)
+ *
+ */
+int JK_METHOD ajp_service(jk_endpoint_t   *e, 
+                jk_ws_service_t *s,
+                jk_logger_t     *l,
+                int             *is_recoverable_error)
+{
+    int i, err;
+    ajp_operation_t oper;
+    ajp_operation_t *op = &oper;
+
+    jk_log(l, JK_LOG_DEBUG, "Into jk_endpoint_t::service\n");
+
+    if(e && e->endpoint_private && s && is_recoverable_error) {
+        ajp_endpoint_t *p = e->endpoint_private;
+            op->request = jk_b_new(&(p->pool));
+        jk_b_set_buffer_size(op->request, DEF_BUFFER_SZ); 
+        jk_b_reset(op->request);
+       
+        op->reply = jk_b_new(&(p->pool));
+        jk_b_set_buffer_size(op->reply, DEF_BUFFER_SZ);
+        jk_b_reset(op->reply); 
+        
+        op->post = jk_b_new(&(p->pool));
+        jk_b_set_buffer_size(op->post, DEF_BUFFER_SZ);
+        jk_b_reset(op->post); 
+        
+        op->recoverable = JK_TRUE;
+        op->uploadfd     = -1;      /* not yet used, later ;) */
+
+        p->left_bytes_to_send = s->content_length;
+        p->reuse = JK_FALSE;
+        *is_recoverable_error = JK_TRUE;
+
+        s->secret = p->worker->secret;
+
+        /* 
+         * We get here initial request (in reqmsg)
+         */
+        if (!ajp_marshal_into_msgb(op->request, s, l, p)) {
+            *is_recoverable_error = JK_FALSE;                
+            return JK_FALSE;
+        }
+
+        /* 
+         * JK_RETRIES could be replaced by the number of workers in
+         * a load-balancing configuration 
+         */
+        for (i = 0; i < JK_RETRIES; i++) {
+            /*
+             * We're using reqmsg which hold initial request
+             * if Tomcat is stopped or restarted, we will pass reqmsg
+             * to next valid tomcat. 
+             */
+            err = ajp_send_request(e, s, l, p, op);
+            if (err == JK_TRUE) {
+
+                /* If we have the no recoverable error, it's probably because
+                 * the sender (browser) stopped sending data before the end
+                 * (certainly in a big post)
+                 */
+                if (! op->recoverable) {
+                    *is_recoverable_error = JK_FALSE;
+                    jk_log(l, JK_LOG_ERROR,
+                           "ERROR: sending request to tomcat failed "
+                           "without recovery in send loop %d\n",
+                           i);
+                    return JK_FALSE;
+                }
+
+                /* Up to there we can recover */
+                *is_recoverable_error = JK_TRUE;
+                op->recoverable = JK_TRUE;
+
+                err = ajp_get_reply(e, s, l, p, op);
+                if (err > 0) {
+                    return (JK_TRUE);
+                }
+
+                if (err != JK_CLIENT_ERROR) {
+                    /* if we can't get reply, check if no recover flag was set 
+                     * if is_recoverable_error is cleared, we have started
+                     * receiving upload data and we must consider that
+                     * operation is no more recoverable
+                     */
+                    if (! op->recoverable) {
+                        *is_recoverable_error = JK_FALSE;
+                        jk_log(l, JK_LOG_ERROR,
+                               "ERROR: receiving reply from tomcat failed "
+                               "without recovery in send loop %d\n",
+                               i);
+                        return JK_FALSE;
+                    }
+                    jk_log(l, JK_LOG_ERROR,
+                           "ERROR: Receiving from tomcat failed, "
+                           "recoverable operation. err=%d\n",
+                           i);
+                }
+            }
+
+            jk_close_socket(p->sd);
+            p->sd = -1;         
+            ajp_reuse_connection(p, l);
+
+            if (err == JK_CLIENT_ERROR) {
+                *is_recoverable_error = JK_FALSE;
+                jk_log(l, JK_LOG_ERROR,
+                       "ERROR: "
+                       "Client connection aborted or network problems\n");
+                return JK_CLIENT_ERROR;
+            }
+            else {
+                jk_log(l, JK_LOG_INFO,
+                       "sending request to tomcat failed in send loop. "
+                       "err=%d\n",
+                       i);
+            }
+
+        }
+        
+        /* Log the error only once per failed request. */
+        jk_log(l, JK_LOG_ERROR,
+               "Error connecting to tomcat. Tomcat is probably not started "
+               "or is listening on the wrong port. worker=%s failed errno = %d\n",
+               p->worker->name, errno);
+
+    } else {
+        jk_log(l, JK_LOG_ERROR, "ajp: end of service with error\n");
+    }
+
+    return JK_FALSE;
+}
+
+/*
+ * Validate the worker (ajp13/ajp14)
+ */
+
+int ajp_validate(jk_worker_t *pThis,
+                 jk_map_t    *props,
+                 jk_worker_env_t *we,
+                 jk_logger_t *l,
+                 int          proto)
+{
+    int    port;
+    char * host;
+
+    jk_log(l, JK_LOG_DEBUG, "Into jk_worker_t::validate\n");
+
+    if (proto == AJP13_PROTO) {
+        port = AJP13_DEF_PORT;
+        host = AJP13_DEF_HOST;
+    }
+    else if (proto == AJP14_PROTO) {
+        port = AJP14_DEF_PORT;
+        host = AJP14_DEF_HOST;
+    }
+    else {
+        jk_log(l, JK_LOG_DEBUG,
+               "In jk_worker_t::validate unknown protocol %d\n", proto);
+        return JK_FALSE;
+    } 
+
+    if (pThis && pThis->worker_private) {
+        ajp_worker_t *p = pThis->worker_private;
+        port = jk_get_worker_port(props, p->name, port);
+        host = jk_get_worker_host(props, p->name, host);
+
+        jk_log(l, JK_LOG_DEBUG,
+              "In jk_worker_t::validate for worker %s contact is %s:%d\n",
+               p->name, host, port);
+
+        if(port > 1024 && host) {
+            if(jk_resolve(host, port, &p->worker_inet_addr)) {
+                return JK_TRUE;
+            }
+            jk_log(l, JK_LOG_ERROR,
+                   "ERROR: can't resolve tomcat address %s\n", host);
+        }
+        jk_log(l, JK_LOG_ERROR,
+               "ERROR: invalid host and port %s %d\n",
+               (( host==NULL ) ? "NULL" : host ), port);
+    } else {
+        jk_log(l, JK_LOG_ERROR, "In jk_worker_t::validate, NULL parameters\n");
+    }
+
+    return JK_FALSE;
+}
+
+
+int ajp_init(jk_worker_t *pThis,
+             jk_map_t    *props,
+             jk_worker_env_t *we,
+             jk_logger_t *l,
+             int          proto)
+{
+    int cache;
+    
+    /*
+     * start the connection cache
+     */
+    jk_log(l, JK_LOG_DEBUG, "Into jk_worker_t::init\n");
+
+    if (proto == AJP13_PROTO) {
+        cache = AJP13_DEF_CACHE_SZ;
+    }
+    else if (proto == AJP14_PROTO) {
+        cache = AJP13_DEF_CACHE_SZ;
+    }
+    else {
+        jk_log(l, JK_LOG_DEBUG,
+               "In jk_worker_t::init, unknown protocol %d\n", proto);
+        return JK_FALSE;
+    }
+
+    if (pThis && pThis->worker_private) {
+        ajp_worker_t *p = pThis->worker_private;
+        int cache_sz = jk_get_worker_cache_size(props, p->name, cache);
+        p->socket_timeout =
+            jk_get_worker_socket_timeout(props, p->name, AJP13_DEF_TIMEOUT);
+
+        jk_log(l, JK_LOG_DEBUG,
+               "In jk_worker_t::init, setting socket timeout to %d\n",
+               p->socket_timeout);
+
+        p->keepalive =
+            jk_get_worker_socket_keepalive(props, p->name, JK_FALSE);
+
+        jk_log(l, JK_LOG_DEBUG,
+               "In jk_worker_t::init, setting socket keepalive to %d\n",
+               p->keepalive);
+
+        p->cache_timeout =
+            jk_get_worker_cache_timeout(props, p->name, AJP_DEF_CACHE_TIMEOUT);
+
+        jk_log(l, JK_LOG_DEBUG,
+               "In jk_worker_t::init, setting cache timeout to %d\n",
+               p->cache_timeout);
+
+        p->connect_timeout =
+            jk_get_worker_connect_timeout(props, p->name, AJP_DEF_CONNECT_TIMEOUT);
+
+        jk_log(l, JK_LOG_DEBUG,
+               "In jk_worker_t::init, setting connect timeout to %d\n",
+               p->connect_timeout);
+
+        p->reply_timeout =
+            jk_get_worker_reply_timeout(props, p->name, AJP_DEF_REPLY_TIMEOUT);
+
+        jk_log(l, JK_LOG_DEBUG,
+               "In jk_worker_t::init, setting reply timeout to %d\n",
+               p->reply_timeout);
+
+        p->prepost_timeout =
+            jk_get_worker_prepost_timeout(props, p->name, AJP_DEF_PREPOST_TIMEOUT);
+
+        jk_log(l, JK_LOG_DEBUG,
+               "In jk_worker_t::init, setting prepost timeout to %d\n",
+               p->prepost_timeout);
+
+        p->recovery_opts =
+            jk_get_worker_recovery_opts(props, p->name, AJP_DEF_RECOVERY_OPTS);
+
+        jk_log(l, JK_LOG_DEBUG,
+               "In jk_worker_t::init, setting recovery opts to %d\n",
+               p->recovery_opts);
+
+        /* 
+         *  Need to initialize secret here since we could return from inside
+         *  of the following loop
+         */
+           
+        p->secret = jk_get_worker_secret(props, p->name );
+        p->ep_cache_sz = 0;
+        p->ep_mincache_sz = 0;
+        if (cache_sz > 0) {
+            p->ep_cache =
+                (ajp_endpoint_t **)malloc(sizeof(ajp_endpoint_t *) * cache_sz);
+            if(p->ep_cache) {
+                int i;
+                p->ep_cache_sz = cache_sz;
+                for(i = 0 ; i < cache_sz ; i++) {
+                    p->ep_cache[i] = NULL;
+                }
+                JK_INIT_CS(&(p->cs), i);
+                if (i) {
+                    return JK_TRUE;
+                }
+            }
+        }
+    } else {
+        jk_log(l, JK_LOG_ERROR, "In jk_worker_t::init, NULL parameters\n");
+    }
+
+    return JK_FALSE;
+}
+
+int ajp_destroy(jk_worker_t **pThis,
+                jk_logger_t *l,
+                int          proto)
+{
+    jk_log(l, JK_LOG_DEBUG, "Into jk_worker_t::destroy\n");
+
+    if (pThis && *pThis && (*pThis)->worker_private) {
+        ajp_worker_t *aw = (*pThis)->worker_private;
+
+        free(aw->name);
+
+        jk_log(l, JK_LOG_DEBUG,
+               "Into jk_worker_t::destroy up to %d endpoint to close\n",
+               aw->ep_cache_sz);
+
+        if(aw->ep_cache_sz) {
+            unsigned i;
+            for(i = 0 ; i < aw->ep_cache_sz ; i++) {
+                if(aw->ep_cache[i]) {
+                    ajp_close_endpoint(aw->ep_cache[i], l);
+                }
+            }
+            free(aw->ep_cache);
+            JK_DELETE_CS(&(aw->cs), i);
+        }
+
+        if (aw->login) {
+            free(aw->login);
+            aw->login = NULL;
+        }
+
+        free(aw);
+        return JK_TRUE;
+    }
+
+    jk_log(l, JK_LOG_ERROR, "In jk_worker_t::destroy, NULL parameters\n");
+    return JK_FALSE;
+}
+
+int JK_METHOD ajp_done(jk_endpoint_t **e,
+                       jk_logger_t    *l)
+{
+    if (e && *e && (*e)->endpoint_private) {
+        ajp_endpoint_t *p = (*e)->endpoint_private;
+        int reuse_ep = p->reuse;
+
+        ajp_reset_endpoint(p);
+
+        if(reuse_ep) {
+            ajp_worker_t *w = p->worker;
+            if(w->ep_cache_sz) {
+                int rc;
+                JK_ENTER_CS(&w->cs, rc);
+                if(rc) {
+                    unsigned i;
+
+                    for(i = 0 ; i < w->ep_cache_sz ; i++) {
+                        if(!w->ep_cache[i]) {
+                            w->ep_cache[i] = p;
+                            break;
+                        }
+                    }
+                    JK_LEAVE_CS(&w->cs, rc);
+                    if(i < w->ep_cache_sz) {
+                        jk_log(l, JK_LOG_DEBUG,
+                               "Into jk_endpoint_t::done, "
+                               "recycling connection\n");
+                        return JK_TRUE;
+                    }
+                }
+            }
+        }
+        jk_log(l, JK_LOG_DEBUG,
+               "Into jk_endpoint_t::done, closing connection %d\n", reuse_ep);
+        ajp_close_endpoint(p, l);
+        *e = NULL;
+
+        return JK_TRUE;
+    }
+
+    jk_log(l, JK_LOG_ERROR, "In jk_endpoint_t::done, NULL parameters\n");
+    return JK_FALSE;
+}
+
+int ajp_get_endpoint(jk_worker_t    *pThis,
+                     jk_endpoint_t **je,
+                     jk_logger_t    *l,
+                     int             proto)
+{
+    jk_log(l, JK_LOG_DEBUG, "Into jk_worker_t::get_endpoint\n");
+
+    if (pThis && pThis->worker_private && je) {
+        ajp_worker_t   *aw = pThis->worker_private;
+        ajp_endpoint_t *ae = NULL;
+
+        if (aw->ep_cache_sz) {
+            int rc;
+            JK_ENTER_CS(&aw->cs, rc);
+            if (rc) {
+                unsigned i;
+                time_t now;
+                now = time(NULL);
+                for (i = 0 ; i < aw->ep_cache_sz ; i++) {
+                    if (aw->ep_cache[i]) {
+                        ae = aw->ep_cache[i];
+                        aw->ep_cache[i] = NULL;
+                        break;
+                    }
+                }
+                /* Handle enpoint cache timeouts */
+                if (aw->cache_timeout) {
+                    for ( ; i < aw->ep_cache_sz ; i++) {
+                        if (aw->ep_cache[i]) {
+                            unsigned elapsed = (unsigned)(now-ae->last_access);
+                            if (elapsed > aw->cache_timeout) {
+                                jk_log(l, JK_LOG_DEBUG, 
+                                    "In jk_endpoint_t::ajp_get_endpoint," 
+                                    " cleaning cache slot = %d elapsed %u\n",
+                                     i, elapsed);
+                                ajp_close_endpoint(aw->ep_cache[i], l);
+                                aw->ep_cache[i] = NULL;
+                            }
+                        }
+                    }
+                }
+                JK_LEAVE_CS(&aw->cs, rc);
+                if (ae) {
+                    if (ae->sd > 0) {
+                        /* Handle timeouts for open sockets */
+                        unsigned elapsed = (unsigned)(now - ae->last_access);
+                        ae->last_access = now;
+                        jk_log(l, JK_LOG_DEBUG,
+                              "In jk_endpoint_t::ajp_get_endpoint, "
+                              "time elapsed since last request = %u seconds\n",
+                               elapsed);
+                        if (aw->socket_timeout > 0 &&
+                            elapsed > aw->socket_timeout) {
+                            jk_close_socket(ae->sd);
+                            jk_log(l, JK_LOG_DEBUG, 
+                                   "In jk_endpoint_t::ajp_get_endpoint, "
+                                   "reached socket timeout, closed sd = %d\n",
+                                    ae->sd);
+                            ae->sd = -1; /* just to avoid twice close */
+                        }
+                    }
+                    *je = &ae->endpoint;
+                    return JK_TRUE;
+                }
+            }
+        }
+
+        ae = (ajp_endpoint_t *)malloc(sizeof(ajp_endpoint_t));
+        if (ae) {
+            ae->sd = -1;
+            ae->reuse = JK_FALSE;
+            ae->last_access = time(NULL);
+            jk_open_pool(&ae->pool, ae->buf, sizeof(ae->buf));
+            ae->worker = pThis->worker_private;
+            ae->endpoint.endpoint_private = ae;
+            ae->proto = proto;
+            ae->endpoint.service = ajp_service;
+            ae->endpoint.done = ajp_done;
+            *je = &ae->endpoint;
+            return JK_TRUE;
+        }
+        jk_log(l, JK_LOG_ERROR,
+              "In jk_worker_t::get_endpoint, malloc failed\n");
+    } else {
+        jk_log(l, JK_LOG_ERROR,
+               "In jk_worker_t::get_endpoint, NULL parameters\n");
+    }
+
+    return JK_FALSE;
+}
diff --git a/connectors/jk/native/common/jk_ajp_common.h b/connectors/jk/native/common/jk_ajp_common.h
new file mode 100644
index 0000000..72c96c6
--- /dev/null
+++ b/connectors/jk/native/common/jk_ajp_common.h
@@ -0,0 +1,366 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: common stuff for bi-directional protocol ajp13/ajp14.      *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+#ifndef JK_AJP_COMMON_H
+#define JK_AJP_COMMON_H
+
+#include "jk_service.h"
+#include "jk_msg_buff.h"
+#include "jk_mt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Conditional request attributes
+ * 
+ */
+#define SC_A_CONTEXT            (unsigned char)1
+#define SC_A_SERVLET_PATH       (unsigned char)2
+#define SC_A_REMOTE_USER        (unsigned char)3
+#define SC_A_AUTH_TYPE          (unsigned char)4
+#define SC_A_QUERY_STRING       (unsigned char)5
+#define SC_A_JVM_ROUTE          (unsigned char)6
+#define SC_A_SSL_CERT           (unsigned char)7
+#define SC_A_SSL_CIPHER         (unsigned char)8
+#define SC_A_SSL_SESSION        (unsigned char)9
+#define SC_A_REQ_ATTRIBUTE      (unsigned char)10
+#define SC_A_SSL_KEY_SIZE       (unsigned char)11       /* only in if JkOptions +ForwardKeySize */
+#define SC_A_SECRET             (unsigned char)12
+#define SC_A_ARE_DONE           (unsigned char)0xFF
+
+/*
+ * Request methods, coded as numbers instead of strings.
+ * The list of methods was taken from Section 5.1.1 of RFC 2616,
+ * RFC 2518, the ACL IETF draft, and the DeltaV IESG Proposed Standard.
+ *          Method        = "OPTIONS"
+ *                        | "GET"    
+ *                        | "HEAD"   
+ *                        | "POST"   
+ *                        | "PUT"    
+ *                        | "DELETE" 
+ *                        | "TRACE"  
+ *                        | "PROPFIND"
+ *                        | "PROPPATCH"
+ *                        | "MKCOL"
+ *                        | "COPY"
+ *                        | "MOVE"
+ *                        | "LOCK"
+ *                        | "UNLOCK"
+ *                        | "ACL"
+ *                        | "REPORT"
+ *                        | "VERSION-CONTROL"
+ *                        | "CHECKIN"
+ *                        | "CHECKOUT"
+ *                        | "UNCHECKOUT"
+ *                        | "SEARCH"
+ *                        | "MKWORKSPACE"
+ *                        | "UPDATE"
+ *                        | "LABEL"
+ *                        | "MERGE"
+ *                        | "BASELINE-CONTROL"
+ *                        | "MKACTIVITY"
+ * 
+ */
+#define SC_M_OPTIONS            (unsigned char)1
+#define SC_M_GET                (unsigned char)2
+#define SC_M_HEAD               (unsigned char)3
+#define SC_M_POST               (unsigned char)4
+#define SC_M_PUT                (unsigned char)5
+#define SC_M_DELETE             (unsigned char)6
+#define SC_M_TRACE              (unsigned char)7
+#define SC_M_PROPFIND           (unsigned char)8
+#define SC_M_PROPPATCH          (unsigned char)9
+#define SC_M_MKCOL              (unsigned char)10
+#define SC_M_COPY               (unsigned char)11
+#define SC_M_MOVE               (unsigned char)12
+#define SC_M_LOCK               (unsigned char)13
+#define SC_M_UNLOCK             (unsigned char)14
+#define SC_M_ACL                (unsigned char)15
+#define SC_M_REPORT             (unsigned char)16
+#define SC_M_VERSION_CONTROL    (unsigned char)17
+#define SC_M_CHECKIN            (unsigned char)18
+#define SC_M_CHECKOUT           (unsigned char)19
+#define SC_M_UNCHECKOUT         (unsigned char)20
+#define SC_M_SEARCH             (unsigned char)21
+#define SC_M_MKWORKSPACE        (unsigned char)22
+#define SC_M_UPDATE             (unsigned char)23
+#define SC_M_LABEL              (unsigned char)24
+#define SC_M_MERGE              (unsigned char)25
+#define SC_M_BASELINE_CONTROL   (unsigned char)26
+#define SC_M_MKACTIVITY         (unsigned char)27
+
+
+/*
+ * Frequent request headers, these headers are coded as numbers
+ * instead of strings.
+ * 
+ * Accept
+ * Accept-Charset
+ * Accept-Encoding
+ * Accept-Language
+ * Authorization
+ * Connection
+ * Content-Type
+ * Content-Length
+ * Cookie
+ * Cookie2
+ * Host
+ * Pragma
+ * Referer
+ * User-Agent
+ * 
+ */
+
+#define SC_ACCEPT               (unsigned short)0xA001
+#define SC_ACCEPT_CHARSET       (unsigned short)0xA002
+#define SC_ACCEPT_ENCODING      (unsigned short)0xA003
+#define SC_ACCEPT_LANGUAGE      (unsigned short)0xA004
+#define SC_AUTHORIZATION        (unsigned short)0xA005
+#define SC_CONNECTION           (unsigned short)0xA006
+#define SC_CONTENT_TYPE         (unsigned short)0xA007
+#define SC_CONTENT_LENGTH       (unsigned short)0xA008
+#define SC_COOKIE               (unsigned short)0xA009    
+#define SC_COOKIE2              (unsigned short)0xA00A
+#define SC_HOST                 (unsigned short)0xA00B
+#define SC_PRAGMA               (unsigned short)0xA00C
+#define SC_REFERER              (unsigned short)0xA00D
+#define SC_USER_AGENT           (unsigned short)0xA00E
+
+/*
+ * Frequent response headers, these headers are coded as numbers
+ * instead of strings.
+ * 
+ * Content-Type
+ * Content-Language
+ * Content-Length
+ * Date
+ * Last-Modified
+ * Location
+ * Set-Cookie
+ * Servlet-Engine
+ * Status
+ * WWW-Authenticate
+ * 
+ */
+
+#define SC_RESP_CONTENT_TYPE        (unsigned short)0xA001
+#define SC_RESP_CONTENT_LANGUAGE    (unsigned short)0xA002
+#define SC_RESP_CONTENT_LENGTH      (unsigned short)0xA003
+#define SC_RESP_DATE                (unsigned short)0xA004
+#define SC_RESP_LAST_MODIFIED       (unsigned short)0xA005
+#define SC_RESP_LOCATION            (unsigned short)0xA006
+#define SC_RESP_SET_COOKIE          (unsigned short)0xA007
+#define SC_RESP_SET_COOKIE2         (unsigned short)0xA008
+#define SC_RESP_SERVLET_ENGINE      (unsigned short)0xA009
+#define SC_RESP_STATUS              (unsigned short)0xA00A
+#define SC_RESP_WWW_AUTHENTICATE    (unsigned short)0xA00B
+#define SC_RES_HEADERS_NUM          11
+
+/*
+ * AJP13/AJP14 use same message structure
+ */
+
+#define AJP_DEF_RETRY_ATTEMPTS    (1)
+
+#define AJP_HEADER_LEN            (4)
+#define AJP_HEADER_SZ_LEN         (2)
+#define CHUNK_BUFFER_PAD          (12)
+#define AJP_DEF_CACHE_TIMEOUT     (15)
+#define AJP_DEF_CONNECT_TIMEOUT   (0)		/* NO CONNECTION TIMEOUT => NO CPING/CPONG */
+#define AJP_DEF_REPLY_TIMEOUT     (0)		/* NO REPLY TIMEOUT                        */
+#define AJP_DEF_PREPOST_TIMEOUT   (0)		/* NO PREPOST TIMEOUT => NO CPING/CPONG    */
+#define AJP_DEF_RECOVERY_OPTS	  (0)		/* NO RECOVERY / NO    */
+
+#define RECOVER_ABORT_IF_TCGETREQUEST  	 0x0001	/* DONT RECOVER IF TOMCAT FAIL AFTER RECEIVING REQUEST */
+#define RECOVER_ABORT_IF_TCSENDHEADER    0x0002	/* DONT RECOVER IF TOMCAT FAIL AFTER SENDING HEADERS */
+
+
+
+struct jk_res_data {
+    int         status;
+#ifdef AS400
+    char *msg;
+#else
+    const char *msg;
+#endif
+    unsigned    num_headers;
+    char      **header_names;
+    char      **header_values;
+};
+typedef struct jk_res_data jk_res_data_t;
+
+#include "jk_ajp14.h"
+
+struct ajp_operation;
+typedef struct ajp_operation ajp_operation_t;
+
+struct ajp_endpoint;
+typedef struct ajp_endpoint ajp_endpoint_t;
+
+struct ajp_worker;
+typedef struct ajp_worker ajp_worker_t;
+
+struct ajp_worker {
+    struct sockaddr_in worker_inet_addr; /* Contains host and port */
+    unsigned connect_retry_attempts;
+    char *name;
+ 
+    /* 
+     * Open connections cache...
+     *
+     * 1. Critical section object to protect the cache.
+     * 2. Cache size. 
+     * 3. An array of "open" endpoints.
+     */
+    JK_CRIT_SEC cs;
+    unsigned ep_cache_sz;
+    unsigned ep_mincache_sz;
+    unsigned ep_maxcache_sz;
+    ajp_endpoint_t **ep_cache;
+
+    int proto; /* PROTOCOL USED AJP13/AJP14 */
+    
+    jk_login_service_t *login;
+    
+    /* Weak secret similar with ajp12, used in ajp13 */ 
+    char *secret;
+
+    jk_worker_t worker; 
+
+    /*
+     * Post physical connect handler.
+     * AJP14 will set here its login handler
+     */ 
+    int (* logon)(ajp_endpoint_t *ae,
+                  jk_logger_t    *l);
+
+    /*
+    * Handle Socket Timeouts
+    */
+    unsigned socket_timeout;
+    unsigned keepalive;
+    /*
+    * Handle Cache Timeouts
+    */
+    unsigned cache_timeout;
+
+	/*
+	* Handle Connection/Reply Timeouts
+	*/
+	unsigned connect_timeout;	/* connect cping/cpong delay in ms (0 means disabled) 							*/
+	unsigned reply_timeout;	    /* reply timeout delay in ms (0 means disabled)     							*/
+	unsigned prepost_timeout;	/* before sending a request cping/cpong timeout delay in ms (0 means disabled)    */
+
+	/*
+	 * Recovery option
+	 */
+	unsigned recovery_opts;		/* Set the recovery option */
+}; 
+ 
+
+/*
+ * endpoint, the remote connector which does the work
+ */
+struct ajp_endpoint {
+    ajp_worker_t *worker;
+
+    jk_pool_t pool;
+    jk_pool_atom_t buf[BIG_POOL_SIZE];
+
+    int proto;  /* PROTOCOL USED AJP13/AJP14 */
+
+    int sd;
+    int reuse;
+    jk_endpoint_t endpoint;
+	
+    unsigned left_bytes_to_send;
+
+    /* time of the last request
+       handled by this endpoint */
+    time_t last_access;
+
+};
+
+/*
+ * little struct to avoid multiples ptr passing
+ * this struct is ready to hold upload file fd
+ * to add upload persistant storage
+ */
+struct ajp_operation {
+    jk_msg_buf_t    *request;   /* original request storage */
+    jk_msg_buf_t    *reply;     /* reply storage (chuncked by ajp13 */
+    jk_msg_buf_t    *post;      /* small post data storage area */
+    int     uploadfd;           /* future persistant storage id */
+    int     recoverable;        /* if exchange could be conducted on another TC */
+};
+
+/*
+ * Functions
+ */
+
+
+int ajp_validate(jk_worker_t *pThis,
+                 jk_map_t    *props,
+                 jk_worker_env_t *we,
+                 jk_logger_t *l,
+                 int          proto);
+
+int ajp_init(jk_worker_t *pThis,
+             jk_map_t    *props,
+             jk_worker_env_t *we,
+             jk_logger_t *l,
+             int          proto);
+
+int ajp_destroy(jk_worker_t **pThis,
+                jk_logger_t *l,
+                int          proto);
+
+int JK_METHOD ajp_done(jk_endpoint_t **e,
+                       jk_logger_t    *l);
+
+int ajp_get_endpoint(jk_worker_t    *pThis,
+                     jk_endpoint_t **pend,
+                     jk_logger_t    *l,
+                     int             proto);
+
+int ajp_connect_to_endpoint(ajp_endpoint_t *ae,
+                            jk_logger_t    *l);
+
+void ajp_close_endpoint(ajp_endpoint_t *ae,
+                        jk_logger_t    *l);
+
+int ajp_connection_tcp_send_message(ajp_endpoint_t *ae,
+                                    jk_msg_buf_t   *msg,
+                                    jk_logger_t    *l);
+
+int ajp_connection_tcp_get_message(ajp_endpoint_t *ae,
+                                   jk_msg_buf_t   *msg,
+                                   jk_logger_t    *l);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_AJP_COMMON_H */
diff --git a/connectors/jk/native/common/jk_connect.c b/connectors/jk/native/common/jk_connect.c
new file mode 100644
index 0000000..cfa7c92
--- /dev/null
+++ b/connectors/jk/native/common/jk_connect.c
@@ -0,0 +1,305 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/*
+ * Description: Socket/Naming manipulation functions
+ * Based on:    Various Jserv files
+ */
+/**
+ * @package jk_connect
+ * @author      Gal Shachor <shachor@il.ibm.com>
+ * @version     $Revision$
+ */
+
+
+#include "jk_connect.h"
+#include "jk_util.h"
+
+#ifdef HAVE_APR
+#include "apr_network_io.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#endif
+
+#if defined(WIN32)
+typedef u_long in_addr_t;
+#endif
+
+
+/** resolve the host IP */
+ 
+int jk_resolve(char *host,
+               int port,
+               struct sockaddr_in *rc) 
+{
+    int x;
+
+    /* TODO: Should be updated for IPV6 support. */
+    /* for now use the correct type, in_addr_t */    
+    /* except on NetWare since the MetroWerks compiler is so strict */
+#if defined(NETWARE)
+    u_long laddr;
+#else
+	in_addr_t laddr;
+#endif
+
+    memset(rc, 0, sizeof(struct sockaddr_in));
+
+    rc->sin_port   = htons((short)port);
+    rc->sin_family = AF_INET;
+
+    /* Check if we only have digits in the string */
+    for(x = 0 ; '\0' != host[x] ; x++) {
+        if(!isdigit(host[x]) && host[x] != '.') {
+            break;
+        }
+    }
+
+    /* If we found also characters we shoud make name to IP resolution */
+    if(host[x] != '\0') {
+
+#ifdef HAVE_APR
+        apr_pool_t *context;
+        apr_sockaddr_t *remote_sa, *temp_sa;
+        char *remote_ipaddr;
+
+        /* May be we could avoid to recreate it each time ? */
+        if (apr_pool_create(&context, NULL) != APR_SUCCESS)
+            return JK_FALSE;
+
+        if (apr_sockaddr_info_get(&remote_sa, host, APR_UNSPEC, (apr_port_t)port, 0, context)
+            != APR_SUCCESS) 
+            return JK_FALSE;
+
+        /* Since we are only handling AF_INET (IPV4) address (in_addr_t) */
+        /* make sure we find one of those.                               */
+        temp_sa = remote_sa;
+        while ((NULL != temp_sa) && (AF_INET != temp_sa->family))
+            temp_sa = temp_sa->next;
+
+        /* if temp_sa is set, we have a valid address otherwise, just return */
+        if (NULL != temp_sa)
+            remote_sa = temp_sa;
+        else
+            return JK_FALSE;
+
+        apr_sockaddr_ip_get(&remote_ipaddr, remote_sa);
+        laddr = inet_addr(remote_ipaddr);
+
+        /* May be we could avoid to delete it each time ? */
+        apr_pool_destroy(context);
+
+#else /* HAVE_APR */
+
+      /* XXX : WARNING : We should really use gethostbyname_r in multi-threaded env */
+      /* Fortunatly when APR is available, ie under Apache 2.0, we use it */
+        struct hostent *hoste = gethostbyname(host);
+        if(!hoste) {
+            return JK_FALSE;
+        }
+
+        laddr = ((struct in_addr *)hoste->h_addr_list[0])->s_addr;
+
+#endif /* HAVE_APR */
+    } else {
+        /* If we found only digits we use inet_addr() */
+        laddr = inet_addr(host);        
+    }
+    memcpy(&(rc->sin_addr), &laddr , sizeof(laddr));
+
+    return JK_TRUE;
+}
+
+/** connect to Tomcat */
+
+int jk_open_socket(struct sockaddr_in *addr, 
+                   int ndelay,
+                   int keepalive,
+                   jk_logger_t *l)
+{
+    char buf[32];   
+    int sock;
+
+    jk_log(l, JK_LOG_DEBUG, "Into jk_open_socket\n");
+
+    sock = socket(AF_INET, SOCK_STREAM, 0);
+    if(sock > -1) {
+        int ret;
+        /* Tries to connect to Tomcat (continues trying while error is EINTR) */
+        do {
+            jk_log(l, JK_LOG_DEBUG, "jk_open_socket, try to connect socket = %d to %s\n", 
+                   sock, jk_dump_hinfo(addr, buf));
+                   
+/* Need more infos for BSD 4.4 and Unix 98 defines, for now only 
+   iSeries when Unix98 is required at compil time */
+#if (_XOPEN_SOURCE >= 520) && defined(AS400)
+	       ((struct sockaddr *)addr)->sa_len = sizeof(struct sockaddr_in);
+#endif 
+            ret = connect(sock,
+                          (struct sockaddr *)addr,
+                          sizeof(struct sockaddr_in));
+#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+            if(SOCKET_ERROR == ret) { 
+                errno = WSAGetLastError() - WSABASEERR;
+            }
+#endif /* WIN32 */
+            jk_log(l, JK_LOG_DEBUG, "jk_open_socket, after connect ret = %d\n", ret);
+        } while (-1 == ret && EINTR == errno);
+
+        /* Check if we connected */
+        if(0 == ret) {
+                                                int keep = 1;
+            if(ndelay) {
+                int set = 1;
+
+                jk_log(l, JK_LOG_DEBUG, "jk_open_socket, set TCP_NODELAY to on\n");
+                setsockopt(sock, 
+                           IPPROTO_TCP, 
+                           TCP_NODELAY, 
+                           (char *)&set, 
+                           sizeof(set));
+            }   
+
+            if (keepalive) {
+                jk_log(l, JK_LOG_DEBUG, "jk_open_socket, set SO_KEEPALIVE to on\n");
+                setsockopt(sock,
+                           SOL_SOCKET,
+                           SO_KEEPALIVE,
+                           (char *)&keep,
+                           sizeof(keep));
+            }
+
+            jk_log(l, JK_LOG_DEBUG, "jk_open_socket, return, sd = %d\n", sock);
+            return sock;
+        }   
+        jk_log(l, JK_LOG_INFO, "jk_open_socket, connect() failed errno = %d\n", errno);
+        jk_close_socket(sock);
+    } else {
+#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+        errno = WSAGetLastError() - WSABASEERR;
+#endif /* WIN32 */
+        jk_log(l, JK_LOG_ERROR, "jk_open_socket, socket() failed errno = %d\n", errno);
+    }    
+
+    return -1;
+}
+
+/** close the socket */
+
+int jk_close_socket(int s)
+{
+#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+    if(INVALID_SOCKET  != s) {
+        return closesocket(s) ? -1 : 0; 
+    }
+#else 
+    if(-1 != s) {
+        return close(s); 
+    }
+#endif
+
+    return -1;
+}
+
+/** send a long message
+ * @param sd  opened socket.
+ * @param b   buffer containing the data.
+ * @param len length to send.
+ * @return    -2: send returned 0 ? what this that ?
+ *            -3: send failed.
+ *            >0: total size send.
+ * @bug       this fails on Unixes if len is too big for the underlying
+ *             protocol.
+ */
+int jk_tcp_socket_sendfull(int sd, 
+                           const unsigned char *b,
+                           int len)
+{
+    int sent = 0;
+
+    while(sent < len) {
+        int this_time = send(sd, 
+                             (char *)b + sent , 
+                             len - sent, 
+                             0);
+        
+        if(0 == this_time) {
+            return -2;
+        }
+        if(this_time < 0) {
+            return -3;
+        }
+        sent += this_time;
+    }
+
+    return sent;
+}
+
+/** receive len bytes. Used in ajp_common.
+ * @param sd  opened socket.
+ * @param b   buffer to store the data.
+ * @param len length to receive.
+ * @return    <0: receive failed or connection closed.
+ *            >0: length of the received data.
+ */
+int jk_tcp_socket_recvfull(int sd, 
+                           unsigned char *b, 
+                           int len) 
+{
+    int rdlen = 0;
+
+    while(rdlen < len) {
+        int this_time = recv(sd, 
+                             (char *)b + rdlen, 
+                             len - rdlen, 
+                             0);    
+        if(-1 == this_time) {
+#if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__))
+            /* I assume SOCKET_ERROR == -1 */
+            if(SOCKET_ERROR == this_time) { 
+                errno = WSAGetLastError() - WSABASEERR;
+            }
+#endif /* WIN32 */
+
+            if(EAGAIN == errno) {
+                continue;
+            }
+            /** Pass the errno to the caller */
+            return (errno>0) ? -errno : errno;
+        }
+        if(0 == this_time) {
+            return -1; 
+        }
+        rdlen += this_time;
+    }
+
+    return rdlen;
+}
+
+/**
+ * dump a sockaddr_in in A.B.C.D:P in ASCII buffer
+ *
+ */
+char * jk_dump_hinfo(struct sockaddr_in *saddr, char * buf)
+{
+	unsigned long laddr = (unsigned long)htonl(saddr->sin_addr.s_addr);
+	unsigned short lport = (unsigned short)htons(saddr->sin_port);
+
+	sprintf(buf, "%d.%d.%d.%d:%d", 
+	        (int)(laddr >> 24), (int)((laddr >> 16) & 0xff), (int)((laddr >> 8) & 0xff), (int)(laddr & 0xff), (int)lport);
+	        
+	return buf;
+}
diff --git a/connectors/jk/native/common/jk_connect.h b/connectors/jk/native/common/jk_connect.h
new file mode 100644
index 0000000..6d0c26c
--- /dev/null
+++ b/connectors/jk/native/common/jk_connect.h
@@ -0,0 +1,63 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Socket connections header file                             *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                               *
+ ***************************************************************************/
+
+#ifndef JK_CONNECT_H
+#define JK_CONNECT_H
+
+#include "jk_logger.h"
+#include "jk_global.h"
+
+#if !defined(WIN32) && !(defined(NETWARE) && defined(__NOVELL_LIBC__))
+    #define closesocket         close
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+int jk_resolve(char *host,
+               int port,
+               struct sockaddr_in *rc);
+
+int jk_open_socket(struct sockaddr_in *addr, 
+                   int ndelay,
+                   int keepalive,
+                   jk_logger_t *l);
+
+int jk_close_socket(int s);
+
+int jk_tcp_socket_sendfull(int sd, 
+                           const unsigned char *b,
+                           int len);
+
+int jk_tcp_socket_recvfull(int sd, 
+                           unsigned char *b, 
+                           int len);
+
+char * jk_dump_hinfo(struct sockaddr_in *saddr, 
+                     char * buf);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_CONNECT_H */
diff --git a/connectors/jk/native/common/jk_context.c b/connectors/jk/native/common/jk_context.c
new file mode 100644
index 0000000..984beb9
--- /dev/null
+++ b/connectors/jk/native/common/jk_context.c
@@ -0,0 +1,290 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Context handling (Autoconf)                                *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#include "jk_global.h"
+#include "jk_context.h"
+#include "jk_ajp_common.h"
+
+
+/*
+ * Set the virtual name of the context 
+ */
+
+int context_set_virtual(jk_context_t *c, char *virtual)
+{
+    if (c) {
+
+        if (virtual) {
+            c->virtual = jk_pool_strdup(&c->p, virtual);
+
+            if (! c->virtual)
+                return JK_FALSE;
+        }
+
+        return JK_TRUE;
+    }
+
+    return JK_FALSE;
+}
+
+/*
+ * Init the context info struct
+ */
+
+int context_open(jk_context_t *c, char *virtual)
+{
+    if (c) {
+        jk_open_pool(&c->p, c->buf, sizeof(jk_pool_atom_t) * SMALL_POOL_SIZE);
+        c->size  	= 0;
+        c->capacity = 0;
+        c->contexts = NULL;
+
+        return context_set_virtual(c, virtual);
+    }
+
+    return JK_FALSE;
+}
+
+/*
+ * Close the context info struct
+ */
+
+int context_close(jk_context_t *c)
+{
+    if (c) {
+
+        jk_close_pool(&c->p);
+        return JK_TRUE;
+    }
+
+    return JK_FALSE;
+}
+
+/*
+ * Allocate and open context
+ */
+
+int context_alloc(jk_context_t **c, char * virtual)
+{
+    if (c) 
+        return context_open(*c = (jk_context_t *)malloc(sizeof(jk_context_t)), virtual);
+  
+    return JK_FALSE;
+}
+ 
+/*
+ * Close and destroy context
+ */
+
+int context_free(jk_context_t **c)
+{
+    if (c && *c) {
+        context_close(*c);  
+        free(*c);
+        *c = NULL;
+        return JK_TRUE;
+    }
+    
+    return JK_FALSE;
+}
+
+
+/*
+ * Ensure there will be memory in context info to store Context Bases
+ */
+
+static int context_realloc(jk_context_t *c)
+{
+    if (c->size == c->capacity) {
+        jk_context_item_t **contexts;
+        int  capacity = c->capacity + CBASE_INC_SIZE;
+        
+        contexts = (jk_context_item_t **)jk_pool_alloc(&c->p, sizeof(jk_context_item_t *) * capacity);
+
+        if (! contexts)
+            return JK_FALSE;
+
+        if (c->capacity && c->contexts)
+            memcpy(contexts, c->contexts, sizeof(jk_context_item_t *) * c->capacity);
+
+        c->contexts = contexts;
+        c->capacity = capacity;
+    }
+
+    return JK_TRUE;
+}
+
+/*
+ * Ensure there will be memory in context info to URIS
+ */
+ 
+static int context_item_realloc(jk_context_t *c, jk_context_item_t *ci)
+{
+    if (ci->size == ci->capacity) {
+            char **uris;
+            int capacity = ci->capacity + URI_INC_SIZE;
+
+            uris = (char **)jk_pool_alloc(&c->p, sizeof(char *) * capacity);
+
+        if (! uris)
+            return JK_FALSE;
+
+        memcpy(uris, ci->uris, sizeof(char *) * ci->capacity);
+
+        ci->uris     = uris;
+        ci->capacity = capacity;
+    }
+    
+    return JK_TRUE;
+}
+
+        
+/*
+ * Locate a context base in context list
+ */
+
+jk_context_item_t * context_find_base(jk_context_t *c, char *cbase)
+{
+    int                 i;
+    jk_context_item_t * ci;
+
+    if (! c || ! cbase)
+        return NULL;
+
+    for (i = 0 ; i < c->size ; i++) {
+        
+        ci = c->contexts[i];
+
+        if (! ci)
+            continue;
+
+        if (! strcmp(ci->cbase, cbase))
+            return ci;
+    }
+
+    return NULL;
+}
+
+/*
+ * Locate an URI in a context item
+ */
+
+char * context_item_find_uri(jk_context_item_t *ci, char *uri)
+{
+    int i;
+
+    if (! ci || ! uri)
+        return NULL;
+
+    for (i = 0 ; i < ci->size ; i++) {
+        if (! strcmp(ci->uris[i], uri))
+            return ci->uris[i];
+    }
+
+    return NULL;
+}
+
+void context_dump_uris(jk_context_t *c, char *cbase, FILE * f)
+{
+    jk_context_item_t * ci;
+    int                 i;
+
+    ci = context_find_base(c, cbase);
+
+    if (! ci)
+        return;
+
+    for (i = 0; i < ci->size; i++)
+        fprintf(f, "/%s/%s\n", ci->cbase, ci->uris[i]);
+
+    fflush(f); 
+}
+
+
+/*
+ * Add a new context item to context
+ */
+
+jk_context_item_t * context_add_base(jk_context_t *c, char *cbase)
+{
+    jk_context_item_t *  ci;
+
+    if (! c || !cbase)
+        return NULL;
+
+    /* Check if the context base was not allready created */
+    ci = context_find_base(c, cbase);
+
+    if (ci)
+        return ci;
+
+    if (context_realloc(c) != JK_TRUE)
+        return NULL;
+
+    ci = (jk_context_item_t *)jk_pool_alloc(&c->p, sizeof(jk_context_item_t));
+
+    if (! ci)
+        return NULL;
+
+    c->contexts[c->size] = ci;
+    c->size++;
+    ci->cbase       = jk_pool_strdup(&c->p, cbase);
+    ci->status      = 0;
+    ci->size        = 0;
+    ci->capacity    = 0;
+    ci->uris        = NULL;
+
+    return ci;
+}
+
+/*
+ * Add a new URI to a context item
+ */
+
+int context_add_uri(jk_context_t *c, char *cbase, char * uri)
+{
+    jk_context_item_t *  ci;
+
+    if (! uri)
+        return JK_FALSE;
+
+    /* Get/Create the context base */
+    ci = context_add_base(c, cbase);
+        
+    if (! ci)
+        return JK_FALSE;
+
+    if (context_item_find_uri(ci, uri) != NULL)
+        return JK_TRUE;
+
+    if (context_item_realloc(c, ci) == JK_FALSE)
+        return JK_FALSE;
+
+    ci->uris[ci->size] = jk_pool_strdup(&c->p, uri);
+
+    if (ci->uris[ci->size] == NULL)
+        return JK_FALSE;
+
+    ci->size++;
+    return JK_TRUE;
+}
+
diff --git a/connectors/jk/native/common/jk_context.h b/connectors/jk/native/common/jk_context.h
new file mode 100644
index 0000000..80c0788
--- /dev/null
+++ b/connectors/jk/native/common/jk_context.h
@@ -0,0 +1,134 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Context Stuff (Autoconf)                                   *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+#ifndef JK_CONTEXT_H
+#define JK_CONTEXT_H
+
+#include "jk_pool.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define CBASE_INC_SIZE   (8)    /* Allocate memory by step of 8 URIs : ie 8 URI by context */
+#define URI_INC_SIZE (8)        /* Allocate memory by step of 8 CONTEXTs : ie 8 contexts by worker */
+
+typedef struct {
+
+    /*
+     * Context base (ie examples) 
+     */
+ 
+    char *      cbase;
+ 
+    /*
+     * Status (Up/Down)
+     */
+ 
+    int         status;
+
+    /*
+     * Num of URI handled 
+     */
+    
+    int         size;
+
+    /*
+     * Capacity
+     */
+   
+    int         capacity;
+
+    /*
+     * URL/URIs (autoconf)
+     */
+
+    char **     uris;
+}
+jk_context_item_t;
+
+
+typedef struct {
+
+    /*
+     * Memory Pool
+     */
+
+    jk_pool_t       p;
+    jk_pool_atom_t  buf[SMALL_POOL_SIZE];
+
+	/*
+	 * Virtual Server (if use)
+	 */
+
+	char *		            virtual;
+
+    /*
+     * Num of context handled (ie: examples, admin...)
+     */
+
+    int                     size;
+
+    /*
+     * Capacity
+     */
+
+    int                     capacity; 
+
+    /*
+     * Context list, context / URIs
+     */
+
+    jk_context_item_t **    contexts;
+} 
+jk_context_t;
+
+
+/*
+ * functions defined here 
+ */
+
+int context_set_virtual(jk_context_t *c, char *virtual);
+
+int context_open(jk_context_t *c, char *virtual);
+
+int context_close(jk_context_t *c);
+
+int context_alloc(jk_context_t **c, char *virtual);
+
+int context_free(jk_context_t **c);
+
+jk_context_item_t *context_find_base(jk_context_t *c, char *cbase);
+
+char *context_item_find_uri(jk_context_item_t *ci, char *uri);
+
+void context_dump_uris(jk_context_t *c, char *cbase, FILE *f);
+
+jk_context_item_t *context_add_base(jk_context_t *c, char *cbase);
+
+int context_add_uri(jk_context_t *c, char *cbase, char *uri);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_CONTEXT_H */
diff --git a/connectors/jk/native/common/jk_global.h b/connectors/jk/native/common/jk_global.h
new file mode 100644
index 0000000..3e2d2ce
--- /dev/null
+++ b/connectors/jk/native/common/jk_global.h
@@ -0,0 +1,229 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Global definitions and include files that should exist     *
+ *              anywhere                                                   *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                               *
+ ***************************************************************************/
+
+#ifndef JK_GLOBAL_H
+#define JK_GLOBAL_H
+
+#ifndef NETWARE
+#if !defined(WIN32) && !defined(AS400)
+#include "portable.h"
+#else
+#define HAVE_VSNPRINTF
+#define HAVE_SNPRINTF
+#endif
+#endif
+
+#include "jk_version.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+#include <ctype.h>
+
+#ifdef _OSD_POSIX
+#include "ap_config.h"
+#endif
+
+#ifdef AS400
+#include "ap_config.h"
+#include "apr_strings.h"
+#include "apr_lib.h"
+extern char *strdup (const char *str);
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef WIN32
+    #include <windows.h>
+    #include <winsock.h>
+    #include <sys/timeb.h>
+#else
+    #include <unistd.h>
+    #if defined(NETWARE) && defined(__NOVELL_LIBC__)
+        #include "novsock2.h"
+        #define __sys_socket_h__
+        #define __netdb_h__
+        #define __netinet_in_h__
+        #define HAVE_VSNPRINTF
+        #define HAVE_SNPRINTF
+    #endif
+    #include <netdb.h>
+    #include <netinet/in.h>
+    #include <sys/socket.h>
+    #ifndef NETWARE
+        #include <netinet/tcp.h>
+        #include <arpa/inet.h>
+        #include <sys/un.h>
+        #if !defined(_OSD_POSIX) && !defined(AS400) && !defined(CYGWIN)
+            #include <sys/socketvar.h>
+        #endif
+        #if !defined(HPUX11) && !defined(AS400)
+            #include <sys/select.h>
+        #endif
+    #endif
+        
+    #include <sys/time.h>
+    #include <sys/ioctl.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define JK_WORKER_FILE_TAG      ("worker_file")
+#define JK_MOUNT_FILE_TAG       ("worker_mount_file")
+#define JK_LOG_LEVEL_TAG        ("log_level")
+#define JK_LOG_FILE_TAG         ("log_file")
+#define JK_WORKER_NAME_TAG      ("worker")
+
+#define JK_WORKER_FILE_DEF  ("workers.properties")
+#define JK_LOG_LEVEL_DEF    ("emerg")
+
+#define JK_TRUE  (1)
+#define JK_FALSE (0)
+
+#define JK_LF (10)
+#define JK_CR (13)
+
+#define JK_SESSION_IDENTIFIER "JSESSIONID"
+#define JK_PATH_SESSION_IDENTIFIER ";jsessionid"
+
+#if defined(WIN32) || defined(NETWARE)
+    #define JK_METHOD __stdcall
+    #define C_LEVEL_TRY_START       __try {
+    #define C_LEVEL_TRY_END         }
+    #define C_LEVEL_FINALLY_START   __finally {
+    #define C_LEVEL_FINALLY_END     }
+    #define PATH_SEPERATOR          (';')
+    #define FILE_SEPERATOR          ('\\')
+    #define PATH_ENV_VARIABLE       ("PATH")
+
+    /* incompatible names... */
+    #ifndef strcasecmp 
+        #define strcasecmp stricmp
+    #endif
+#else
+    #define JK_METHOD
+    #define C_LEVEL_TRY_START       
+    #define C_LEVEL_TRY_END         
+    #define C_LEVEL_FINALLY_START   
+    #define C_LEVEL_FINALLY_END     
+    #define PATH_SEPERATOR          (':')
+    #define FILE_SEPERATOR          ('/')
+    #define PATH_ENV_VARIABLE       ("LD_LIBRARY_PATH")
+#endif
+
+/*
+ * RECO STATUS
+ */
+
+#define RECO_NONE	0x00
+#define RECO_INITED	0x01
+#define RECO_FILLED	0x02
+
+/*
+ * JK options
+ */
+
+#define JK_OPT_FWDURIMASK           0x0003
+
+#define JK_OPT_FWDURICOMPAT         0x0001
+#define JK_OPT_FWDURICOMPATUNPARSED 0x0002
+#define JK_OPT_FWDURIESCAPED        0x0003
+
+#define JK_OPT_FWDURIDEFAULT        JK_OPT_FWDURICOMPAT
+
+#define JK_OPT_FWDKEYSIZE           0x0004
+
+#define JK_OPT_FWDDIRS              0x0008
+
+/* Check for EBCDIC systems */
+
+/* Check for Apache 2.0 running on an EBCDIC system */
+#if APR_CHARSET_EBCDIC 
+
+#include <util_ebcdic.h>
+
+#define USE_CHARSET_EBCDIC
+#define jk_xlate_to_ascii(b, l) ap_xlate_proto_to_ascii(b, l)
+#define jk_xlate_from_ascii(b, l) ap_xlate_proto_from_ascii(b, l)
+
+#else   /* APR_CHARSET_EBCDIC */
+
+/* Check for Apache 1.3 running on an EBCDIC system */
+#ifdef CHARSET_EBCDIC
+
+#define USE_CHARSET_EBCDIC
+#define jk_xlate_to_ascii(b, l) ebcdic2ascii(b, b, l)
+#define jk_xlate_from_ascii(b, l) ascii2ebcdic(b, b, l)
+
+#else /* CHARSET_EBCDIC */
+
+/* We're in on an ASCII system */
+
+#define jk_xlate_to_ascii(b, l)             /* NOOP */
+#define jk_xlate_from_ascii(b, l)           /* NOOP */
+
+#endif /* CHARSET_EBCDIC */
+
+#endif /* APR_CHARSET_EBCDIC */
+
+#ifdef WIN32
+/* For WIN32, emulate gettimeofday() using _ftime() */
+#define gettimeofday(tv,tz) { struct _timeb tb; _ftime(&tb); (tv)->tv_sec = tb.time; (tv)->tv_usec = tb.millitm * 1000; }
+#ifdef HAVE_APR
+#define snprintf apr_snprintf
+#define vsnprintf apr_vsnprintf
+#else
+/* define snprint to match windows version */
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#endif
+#endif
+
+/* Use apr snprintf() and vsnprintf() when needed */
+#if defined(HAVE_APR)
+#if !defined(HAVE_SNPRINTF)
+#define snprintf apr_snprintf
+#endif
+#if !defined(HAVE_VSNPRINTF)
+#define vsnprintf apr_vsnprintf
+#endif
+#endif
+
+/* XXXX There is a snprintf() and vsnprintf() in jk_util.c */
+/* if those work remove the #define. */
+#if defined(NETWARE) || defined(AS400)
+#define USE_SPRINTF
+#define USE_VSPRINTF
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_GLOBAL_H */
diff --git a/connectors/jk/native/common/jk_jni_worker.c b/connectors/jk/native/common/jk_jni_worker.c
new file mode 100644
index 0000000..34e6104
--- /dev/null
+++ b/connectors/jk/native/common/jk_jni_worker.c
@@ -0,0 +1,1226 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: In process JNI worker                                      *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Based on:                                                               *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#if !defined(WIN32) && !defined(NETWARE) && !defined(AS400)
+#include <dlfcn.h>
+#endif
+
+#include <jni.h>
+
+#include "jk_pool.h"
+#include "jk_jni_worker.h"
+#include "jk_util.h"
+
+#if defined LINUX && defined APACHE2_SIGHACK
+#include <pthread.h>
+#include <signal.h>
+#include <bits/signum.h>
+#endif
+
+#ifdef NETWARE
+#ifdef __NOVELL_LIBC__
+#include <dlfcn.h>
+#else
+#include <nwthread.h>
+#include <nwadv.h>
+#endif
+#endif
+
+#ifndef JNI_VERSION_1_1
+#define JNI_VERSION_1_1 0x00010001
+#endif
+
+/* probably on an older system that doesn't support RTLD_NOW or RTLD_LAZY.
+ * The below define is a lie since we are really doing RTLD_LAZY since the
+ * system doesn't support RTLD_NOW.
+ */
+#ifndef RTLD_NOW
+#define RTLD_NOW 1
+#endif
+
+#ifndef RTLD_GLOBAL
+#define RTLD_GLOBAL 0
+#endif
+
+#define null_check(e) if ((e) == 0) return JK_FALSE
+
+jint (JNICALL *jni_get_default_java_vm_init_args)(void *) = NULL;
+jint (JNICALL *jni_create_java_vm)(JavaVM **, JNIEnv **, void *) = NULL;
+#ifdef AS400
+jint (JNICALL *jni_get_created_java_vms)(JavaVM **, long, long *) = NULL;
+#else
+jint (JNICALL *jni_get_created_java_vms)(JavaVM **, int, int *) = NULL;
+#endif
+
+#define TC33_JAVA_BRIDGE_CLASS_NAME ("org/apache/tomcat/modules/server/JNIEndpoint")
+#define TC32_JAVA_BRIDGE_CLASS_NAME ("org/apache/tomcat/service/JNIEndpoint")
+
+static jk_worker_t *the_singleton_jni_worker = NULL;
+
+struct jni_worker {
+
+    int was_verified;
+    int was_initialized;
+
+    jk_pool_t p;
+    jk_pool_atom_t buf[TINY_POOL_SIZE];
+
+    /*
+     * JVM Object pointer.
+     */
+    JavaVM      *jvm;   
+
+    /*
+     * [V] JNIEnv used for boostraping from validate -> init w/o an attach
+     */
+    JNIEnv	*tmp_env;
+
+    /*
+     * Web Server to Java bridge, instance and class.
+     */
+    jobject     jk_java_bridge_object;
+    jclass      jk_java_bridge_class;
+
+    /*
+     * Java methods ids, to jump into the JVM
+     */
+    jmethodID   jk_startup_method;
+    jmethodID   jk_service_method;
+    jmethodID   jk_shutdown_method;
+
+    /*
+     * Command line for tomcat startup
+     */
+    char *tomcat_cmd_line;
+
+	/*
+	 * Bridge Type, Tomcat 32/33/40/41/5
+	 */
+	unsigned	bridge_type;
+	
+    /*
+     * Classpath
+     */
+    char *tomcat_classpath;
+
+    /*
+     * Full path to the jni javai/jvm dll
+     */
+    char *jvm_dll_path;
+
+    /*
+     * Initial Java heap size
+     */
+    unsigned tomcat_ms;
+
+    /*
+     * Max Java heap size
+     */
+    unsigned tomcat_mx;
+
+    /*
+     * Java system properties
+     */
+    char **sysprops;
+
+#ifdef JNI_VERSION_1_2
+    /*
+     * Java 2 initialization options (-X... , -verbose etc.)
+     */
+    char **java2opts;
+
+    /*
+     * Java 2 lax/strict option checking (bool)
+     */    
+    int java2lax;
+#endif
+
+    /*
+     * stdout and stderr file names for Java
+     */
+    char *stdout_name;
+    char *stderr_name;
+
+    char *name; 
+    jk_worker_t worker;
+};
+typedef struct jni_worker jni_worker_t;
+
+struct jni_endpoint {    
+    int attached;
+    JNIEnv *env;
+    jni_worker_t *worker;
+    
+    jk_endpoint_t endpoint;
+};
+typedef struct jni_endpoint jni_endpoint_t;
+
+
+static int load_jvm_dll(jni_worker_t *p,
+                        jk_logger_t *l);
+
+static int open_jvm(jni_worker_t *p,
+                    JNIEnv **env,
+                    jk_logger_t *l);
+
+static int open_jvm1(jni_worker_t *p,
+                    JNIEnv **env,
+                    jk_logger_t *l);
+
+#ifdef JNI_VERSION_1_2
+static int detect_jvm_version(jk_logger_t *l);
+
+static int open_jvm2(jni_worker_t *p,
+                    JNIEnv **env,
+                    jk_logger_t *l);
+#endif
+
+
+static int get_bridge_object(jni_worker_t *p,
+                             JNIEnv *env,
+                             jk_logger_t *l);
+
+static int get_method_ids(jni_worker_t *p,
+                          JNIEnv *env,
+                          jk_logger_t *l);
+
+static JNIEnv *attach_to_jvm(jni_worker_t *p, 
+                             jk_logger_t *l);
+
+static void detach_from_jvm(jni_worker_t *p, 
+                            jk_logger_t *l);
+
+
+/*
+   Duplicate string and convert it to ASCII on EBDIC based systems
+   Needed for at least AS/400 and BS2000 but what about other EBDIC systems ?
+*/
+static void *strdup_ascii(jk_pool_t *p, 
+                          char *s)
+{
+	char * rc;	
+	rc = jk_pool_strdup(p, s);
+
+#if defined(AS400) || defined(_OSD_POSIX)
+	jk_xlate_to_ascii(rc, strlen(rc));
+#endif
+
+    return rc;
+}
+
+#if defined LINUX && defined APACHE2_SIGHACK
+static void linux_signal_hack() 
+{
+    sigset_t newM;
+    sigset_t old;
+    
+    sigemptyset(&newM);
+    pthread_sigmask( SIG_SETMASK, &newM, &old );
+    
+    sigdelset(&old, SIGUSR1 );
+    sigdelset(&old, SIGUSR2 );
+    sigdelset(&old, SIGUNUSED );
+    sigdelset(&old, SIGRTMIN );
+    sigdelset(&old, SIGRTMIN + 1 );
+    sigdelset(&old, SIGRTMIN + 2 );
+    pthread_sigmask( SIG_SETMASK, &old, NULL );
+}
+
+static void print_signals( sigset_t *sset) {
+    int sig;
+    for (sig = 1; sig < 20; sig++) 
+	{ if (sigismember(sset, sig)) {printf( " %d", sig);} }
+    printf( "\n");
+}
+#endif
+
+static int JK_METHOD service(jk_endpoint_t *e,
+                             jk_ws_service_t *s,
+                             jk_logger_t *l,
+                             int *is_recoverable_error)
+{
+    jni_endpoint_t *p;
+    jint rc;
+
+    jk_log(l, JK_LOG_DEBUG, "Into service\n");
+    if(!e || !e->endpoint_private || !s) {
+	    jk_log(l, JK_LOG_EMERG, "In service, assert failed - invalid parameters\n");
+	    return JK_FALSE;
+    }
+
+    p = e->endpoint_private;
+
+    if(!is_recoverable_error) {
+	    return JK_FALSE;
+    }
+
+    if(!p->attached) { 
+        /* Try to attach */
+        if(!(p->env = attach_to_jvm(p->worker, l))) {
+	        jk_log(l, JK_LOG_EMERG, "Attach failed\n");  
+	        /*   Is it recoverable ?? */
+	        *is_recoverable_error = JK_TRUE;
+	        return JK_FALSE;
+	    } 
+        p->attached = JK_TRUE;
+    }
+
+    /* we are attached now */
+
+    /* 
+     * When we call the JVM we cannot know what happens
+     * So we can not recover !!!
+     */
+    *is_recoverable_error = JK_FALSE;
+	    
+    jk_log(l, JK_LOG_DEBUG, "In service, calling Tomcat...\n");
+
+    rc = (*(p->env))->CallIntMethod(p->env,
+                                    p->worker->jk_java_bridge_object,
+                                    p->worker->jk_service_method,
+    /* [V] For some reason gcc likes this pointer -> int -> jlong conversion, */
+    /*     but not the direct pointer -> jlong conversion. I hope it's okay.  */
+#ifdef AS400
+                                    s,
+                                    l
+#else
+                                    (jlong)(int)s,
+                                    (jlong)(int)l
+#endif
+);
+
+    /* [V] Righ now JNIEndpoint::service() only returns 1 or 0 */
+    if(rc) {
+	    jk_log(l, JK_LOG_DEBUG, 
+               "In service, Tomcat returned OK, done\n");
+	    return JK_TRUE;
+    } else {
+	    jk_log(l, JK_LOG_ERROR, 
+               "In service, Tomcat FAILED!\n");
+	    return JK_FALSE;
+    }
+}
+
+static int JK_METHOD done(jk_endpoint_t **e,
+                          jk_logger_t *l)
+{
+    jni_endpoint_t *p;
+
+    jk_log(l, JK_LOG_DEBUG, "Into done\n");
+    if(!e || !*e || !(*e)->endpoint_private) {
+	    jk_log(l, JK_LOG_EMERG, 
+               "In done, assert failed - invalid parameters\n");
+	    return JK_FALSE;
+    }
+
+    p = (*e)->endpoint_private;
+
+    if(p->attached) {
+        detach_from_jvm(p->worker,l);
+    }
+
+    free(p);
+    *e = NULL;
+    jk_log(l, JK_LOG_DEBUG, 
+           "Done ok\n");
+    return JK_TRUE;
+}
+
+static int JK_METHOD validate(jk_worker_t *pThis,
+                              jk_map_t *props,
+                              jk_worker_env_t *we,
+                              jk_logger_t *l)
+{
+    jni_worker_t *p;
+    int mem_config = 0;
+    int	btype      = 0;
+    char *str_config = NULL;
+    JNIEnv *env;
+
+    jk_log(l, JK_LOG_DEBUG, "Into validate\n");
+
+    if(! pThis || ! pThis->worker_private) {
+	    jk_log(l, JK_LOG_EMERG, "In validate, assert failed - invalid parameters\n");
+	    return JK_FALSE;
+    }
+
+    p = pThis->worker_private;
+
+    if(p->was_verified) {
+    	jk_log(l, JK_LOG_DEBUG, "validate, been here before, done\n");
+        return JK_TRUE;
+    }
+
+    if(jk_get_worker_mx(props, p->name, (unsigned int *)&mem_config)) {
+        p->tomcat_mx = mem_config;
+    }
+
+    if(jk_get_worker_ms(props, p->name, (unsigned int *)&mem_config)) {
+        p->tomcat_ms = mem_config;
+    }
+
+    if(jk_get_worker_classpath(props, p->name, &str_config)) {
+        p->tomcat_classpath = jk_pool_strdup(&p->p, str_config);
+    }
+
+    if(!p->tomcat_classpath) {
+        jk_log(l, JK_LOG_EMERG, "Fail-> no classpath\n");
+        return JK_FALSE;
+    }
+
+    if(jk_get_worker_bridge_type(props, p->name, (unsigned int *)&btype)) {
+    	p->bridge_type = btype;
+    }	
+
+    if(jk_get_worker_jvm_path(props, p->name, &str_config)) {
+        p->jvm_dll_path  = jk_pool_strdup(&p->p, str_config);
+    }
+
+    if(!p->jvm_dll_path || !jk_file_exists(p->jvm_dll_path)) {
+        jk_log(l, JK_LOG_EMERG, "Fail-> no jvm_dll_path\n");
+        return JK_FALSE;
+    }
+
+    if(jk_get_worker_cmd_line(props, p->name, &str_config)) {
+        p->tomcat_cmd_line  = jk_pool_strdup(&p->p, str_config);
+    }
+
+    if(jk_get_worker_stdout(props, p->name, &str_config)) {
+        p->stdout_name  = jk_pool_strdup(&p->p, str_config);
+    }
+
+    if(jk_get_worker_stderr(props, p->name, &str_config)) {
+        p->stderr_name  = jk_pool_strdup(&p->p, str_config);
+    }
+
+    if(jk_get_worker_sysprops(props, p->name, &str_config)) {
+        p->sysprops  = jk_parse_sysprops(&p->p, str_config);
+    }
+
+#ifdef JNI_VERSION_1_2
+    if(jk_get_worker_str_prop(props, p->name, "java2opts", &str_config)) {
+    	/* jk_log(l, JK_LOG_DEBUG, "Got opts: %s\n", str_config); */
+	    p->java2opts = jk_parse_sysprops(&p->p, str_config);
+    }
+    if(jk_get_worker_int_prop(props, p->name, "java2lax", &mem_config)) {
+        p->java2lax = mem_config ? JK_TRUE : JK_FALSE;
+    }
+#endif
+
+    if(jk_get_worker_libpath(props, p->name, &str_config)) {
+        jk_append_libpath(&p->p, str_config);
+    }
+
+    if(!load_jvm_dll(p, l)) {
+	    jk_log(l, JK_LOG_EMERG, "Fail-> can't load jvm dll\n");
+	    /* [V] no detach needed here */
+	    return JK_FALSE;
+    }
+
+    if(!open_jvm(p, &env, l)) {
+	    jk_log(l, JK_LOG_EMERG, "Fail-> can't open jvm\n");
+	    /* [V] no detach needed here */
+	    return JK_FALSE;
+    }
+
+    if(!get_bridge_object(p, env, l)) {
+        jk_log(l, JK_LOG_EMERG, "Fail-> can't get bridge object\n");
+        /* [V] the detach here may segfault on 1.1 JVM... */
+        detach_from_jvm(p, l);
+        return JK_FALSE;
+    }
+
+    if(!get_method_ids(p, env, l)) {
+        jk_log(l, JK_LOG_EMERG, "Fail-> can't get method ids\n");
+        /* [V] the detach here may segfault on 1.1 JVM... */
+        detach_from_jvm(p, l);
+        return JK_FALSE;
+    }
+
+    p->was_verified = JK_TRUE;
+    p->tmp_env = env;
+
+    jk_log(l, JK_LOG_DEBUG, 
+           "Done validate\n");
+
+    return JK_TRUE;
+}
+
+static int JK_METHOD init(jk_worker_t *pThis,
+                          jk_map_t *props,
+                          jk_worker_env_t *we,
+                          jk_logger_t *l)
+{
+    jni_worker_t *p;
+    JNIEnv *env;
+
+    jk_log(l, JK_LOG_DEBUG, "Into init\n");
+
+    if(! pThis || ! pThis->worker_private) {
+	    jk_log(l, JK_LOG_EMERG, "In init, assert failed - invalid parameters\n");
+	    return JK_FALSE;
+    }
+
+    p = pThis->worker_private;
+
+    if(p->was_initialized) {
+	    jk_log(l, JK_LOG_DEBUG, "init, done (been here!)\n");
+        return JK_TRUE;
+    }
+
+    if(!p->jvm ||
+       !p->jk_java_bridge_object ||
+       !p->jk_service_method     ||
+       !p->jk_startup_method     ||
+       !p->jk_shutdown_method) {
+	    jk_log(l, JK_LOG_EMERG, "Fail-> worker not set completely\n");
+	    return JK_FALSE;
+    }
+
+    /* [V] init is called from the same thread that called validate */
+    /* there is no need to attach to the JVM, just get the env */
+
+    /* if(env = attach_to_jvm(p,l)) { */
+    if((env = p->tmp_env)) {
+        jstring cmd_line = (jstring)NULL;
+        jstring stdout_name = (jstring)NULL;
+        jstring stderr_name = (jstring)NULL;
+        jint rc = 0;
+
+		/* AS400/BS2000 need EBCDIC to ASCII conversion for JNI */
+		
+        if(p->tomcat_cmd_line) {
+            cmd_line = (*env)->NewStringUTF(env, strdup_ascii(&p->p, p->tomcat_cmd_line));
+        }
+        if(p->stdout_name) {
+            stdout_name = (*env)->NewStringUTF(env, strdup_ascii(&p->p, p->stdout_name));
+        }
+        if(p->stderr_name) {
+            stderr_name = (*env)->NewStringUTF(env, strdup_ascii(&p->p, p->stderr_name));
+        }
+
+	    jk_log(l, JK_LOG_DEBUG, "In init, calling Tomcat to intialize itself...\n");
+        rc = (*env)->CallIntMethod(env,
+                                   p->jk_java_bridge_object,
+                                   p->jk_startup_method,
+                                   cmd_line,
+                                   stdout_name,
+                                   stderr_name);
+
+        detach_from_jvm(p, l); 
+
+	    if(rc) {
+	        p->was_initialized = JK_TRUE;
+	        jk_log(l, JK_LOG_DEBUG, 
+                   "In init, Tomcat initialized OK, done\n");
+	        return JK_TRUE;
+	    } else {
+	        jk_log(l, JK_LOG_EMERG, 
+                   "Fail-> could not initialize Tomcat\n");
+	        return JK_FALSE;
+	    }
+    } else {
+	    jk_log(l, JK_LOG_ERROR, 
+               "In init, FIXME: init didn't gen env from validate!\n");
+	    return JK_FALSE;
+    }
+}
+
+static int JK_METHOD get_endpoint(jk_worker_t *pThis,
+                                  jk_endpoint_t **pend,
+                                  jk_logger_t *l)
+{
+    /* [V] This slow, needs replacement */
+    jni_endpoint_t *p = (jni_endpoint_t *)malloc(sizeof(jni_endpoint_t));
+
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into get_endpoint\n");
+
+    if(!pThis || ! pThis->worker_private || !pend) {
+	    jk_log(l, JK_LOG_EMERG, 
+               "In get_endpoint, assert failed - invalid parameters\n");
+	    return JK_FALSE;
+    }
+
+    if(p) {
+        p->attached = JK_FALSE;
+        p->env = NULL;
+        p->worker = pThis->worker_private;
+        p->endpoint.endpoint_private = p;
+        p->endpoint.service = service;
+        p->endpoint.done = done;
+        *pend = &p->endpoint;
+	
+        return JK_TRUE;
+    } else {
+	    jk_log(l, JK_LOG_ERROR, "In get_endpoint, could not allocate endpoint\n");
+	    return JK_FALSE;
+    }
+}
+
+static int JK_METHOD destroy(jk_worker_t **pThis,
+                             jk_logger_t *l)
+{
+    jni_worker_t *p;
+    JNIEnv *env;
+
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into destroy\n");
+
+    if(!pThis || !*pThis || ! (*pThis)->worker_private) {
+	    jk_log(l, JK_LOG_EMERG, "In destroy, assert failed - invalid parameters\n");
+	    return JK_FALSE;
+    }
+
+    p = (*pThis)->worker_private;
+
+    if(!p->jvm) {
+	    jk_log(l, JK_LOG_DEBUG, "In destroy, JVM not intantiated\n");
+	    return JK_FALSE;
+    }
+
+    if(!p->jk_java_bridge_object || ! p->jk_shutdown_method) {
+        jk_log(l, JK_LOG_DEBUG, "In destroy, Tomcat not intantiated\n");
+	    return JK_FALSE;
+    }
+
+    if((env = attach_to_jvm(p,l))) {
+	    jk_log(l, JK_LOG_DEBUG, 
+               "In destroy, shutting down Tomcat...\n");
+        (*env)->CallVoidMethod(env,
+                               p->jk_java_bridge_object,
+                               p->jk_shutdown_method);
+        detach_from_jvm(p, l);
+    }
+
+    jk_close_pool(&p->p);
+    free(p);
+
+    jk_log(l, JK_LOG_DEBUG, "Done destroy\n");
+
+    return JK_TRUE;
+}
+
+int JK_METHOD jni_worker_factory(jk_worker_t **w,
+                                 const char *name,
+                                 jk_logger_t *l)
+{
+    jni_worker_t *private_data;
+
+    jk_log(l, JK_LOG_DEBUG, "Into jni_worker_factory\n");
+
+    if(!name || !w) {
+	    jk_log(l, JK_LOG_EMERG, 
+               "In jni_worker_factory, assert failed - invalid parameters\n");
+	    return JK_FALSE;
+    }
+
+    if(the_singleton_jni_worker) {
+	    jk_log(l, JK_LOG_DEBUG, 
+               "In jni_worker_factory, instance already created\n");
+        *w = the_singleton_jni_worker;
+	    return JK_TRUE;
+    }
+
+    private_data = (jni_worker_t *)malloc(sizeof(jni_worker_t ));
+
+    if(!private_data) {
+	    jk_log(l, JK_LOG_ERROR, 
+               "In jni_worker_factory, memory allocation error\n");
+	    return JK_FALSE;
+    }
+
+    jk_open_pool(&private_data->p,
+	             private_data->buf,
+                 sizeof(jk_pool_atom_t) * TINY_POOL_SIZE);
+
+    private_data->name = jk_pool_strdup(&private_data->p, name);
+
+    if(!private_data->name) {
+        jk_log(l, JK_LOG_ERROR, 
+               "In jni_worker_factory, memory allocation error\n");
+	    jk_close_pool(&private_data->p);
+        free(private_data);
+        return JK_FALSE;
+    }
+
+    private_data->was_verified          = JK_FALSE;
+    private_data->was_initialized       = JK_FALSE;
+    private_data->jvm                   = NULL;
+    private_data->tmp_env               = NULL;
+    private_data->jk_java_bridge_object = (jobject)NULL;
+    private_data->jk_java_bridge_class  = (jclass)NULL;
+    private_data->jk_startup_method     = (jmethodID)NULL;
+    private_data->jk_service_method     = (jmethodID)NULL;
+    private_data->jk_shutdown_method    = (jmethodID)NULL;
+    private_data->tomcat_cmd_line       = NULL;
+    private_data->tomcat_classpath      = NULL;
+    private_data->bridge_type      		= TC33_BRIDGE_TYPE;
+    private_data->jvm_dll_path          = NULL;
+    private_data->tomcat_ms             = 0;
+    private_data->tomcat_mx             = 0;
+    private_data->sysprops              = NULL;
+#ifdef JNI_VERSION_1_2
+    private_data->java2opts             = NULL;
+    private_data->java2lax              = JK_TRUE;
+#endif
+    private_data->stdout_name           = NULL;
+    private_data->stderr_name           = NULL;
+
+    private_data->worker.worker_private = private_data;
+    private_data->worker.validate       = validate;
+    private_data->worker.init           = init;
+    private_data->worker.get_endpoint   = get_endpoint;
+    private_data->worker.destroy        = destroy;
+
+    *w = &private_data->worker;
+    the_singleton_jni_worker = &private_data->worker;
+
+    jk_log(l, JK_LOG_DEBUG, "Done jni_worker_factory\n");
+    return JK_TRUE;
+}
+
+static int load_jvm_dll(jni_worker_t *p,
+                        jk_logger_t *l)
+{
+#ifdef WIN32
+    HINSTANCE hInst = LoadLibrary(p->jvm_dll_path);
+    if(hInst) {
+        (FARPROC)jni_create_java_vm = 
+            GetProcAddress(hInst, "JNI_CreateJavaVM");
+
+        (FARPROC)jni_get_created_java_vms = 
+            GetProcAddress(hInst, "JNI_GetCreatedJavaVMs");
+
+        (FARPROC)jni_get_default_java_vm_init_args = 
+            GetProcAddress(hInst, "JNI_GetDefaultJavaVMInitArgs");
+
+        jk_log(l, JK_LOG_DEBUG, 
+               "Loaded all JNI procs\n");
+
+        if(jni_create_java_vm && jni_get_default_java_vm_init_args && jni_get_created_java_vms) {
+            return JK_TRUE;
+        }
+
+        FreeLibrary(hInst);
+    }
+#elif defined(NETWARE) && !defined(__NOVELL_LIBC__)
+    int javaNlmHandle = FindNLMHandle("JVM");
+    if (0 == javaNlmHandle) {
+        /* if we didn't get a handle, try to load java and retry getting the */
+        /* handle */
+        spawnlp(P_NOWAIT, "JVM.NLM", NULL);
+        ThreadSwitchWithDelay();
+        javaNlmHandle = FindNLMHandle("JVM");
+        if (0 == javaNlmHandle)
+            printf("Error loading Java.");
+
+    }
+    if (0 != javaNlmHandle) {
+        jni_create_java_vm = ImportSymbol(GetNLMHandle(), "JNI_CreateJavaVM");
+        jni_get_created_java_vms = ImportSymbol(GetNLMHandle(), "JNI_GetCreatedJavaVMs");
+        jni_get_default_java_vm_init_args = ImportSymbol(GetNLMHandle(), "JNI_GetDefaultJavaVMInitArgs");
+    }
+    if(jni_create_java_vm && jni_get_default_java_vm_init_args && jni_get_created_java_vms) {
+        return JK_TRUE;
+    }
+#elif defined(AS400)
+    jk_log(l, JK_LOG_DEBUG,
+           "Direct reference to JNI entry points (no SRVPGM)\n");
+    jni_create_java_vm = &JNI_CreateJavaVM;
+    jni_get_default_java_vm_init_args = &JNI_GetDefaultJavaVMInitArgs;
+    jni_get_created_java_vms = &JNI_GetCreatedJavaVMs;
+#else
+    void *handle;
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into load_jvm_dll, load %s\n", p->jvm_dll_path);
+
+    handle = dlopen(p->jvm_dll_path, RTLD_NOW | RTLD_GLOBAL);
+
+    if(!handle) {
+        jk_log(l, JK_LOG_EMERG, 
+               "Can't load native library %s : %s\n", p->jvm_dll_path,
+               dlerror());
+    } else {
+        jni_create_java_vm = dlsym(handle, "JNI_CreateJavaVM");
+        jni_get_default_java_vm_init_args = dlsym(handle, "JNI_GetDefaultJavaVMInitArgs");
+        jni_get_created_java_vms =  dlsym(handle, "JNI_GetCreatedJavaVMs");
+
+        if(jni_create_java_vm && jni_get_default_java_vm_init_args &&
+           jni_get_created_java_vms) {
+    	    jk_log(l, JK_LOG_DEBUG, 
+                   "In load_jvm_dll, symbols resolved, done\n");
+            return JK_TRUE;
+        }
+	    jk_log(l, JK_LOG_EMERG, 
+               "Can't resolve JNI_CreateJavaVM or JNI_GetDefaultJavaVMInitArgs\n");
+        dlclose(handle);
+    }
+#endif
+    return JK_FALSE;
+}
+
+static int open_jvm(jni_worker_t *p,
+                    JNIEnv **env,
+                    jk_logger_t *l)
+{
+#ifdef JNI_VERSION_1_2
+    int jvm_version = detect_jvm_version(l);
+
+    switch(jvm_version) {
+	    case JNI_VERSION_1_1:
+	        return open_jvm1(p, env, l);
+        case JNI_VERSION_1_2:
+	        return open_jvm2(p, env, l);
+	    default:
+            return JK_FALSE;
+    }
+#else
+    /* [V] Make sure this is _really_ visible */
+    #warning -------------------------------------------------------
+    #warning NO JAVA 2 HEADERS! SUPPORT FOR JAVA 2 FEATURES DISABLED
+    #warning -------------------------------------------------------
+    return open_jvm1(p, env, l);
+#endif
+}
+
+static int open_jvm1(jni_worker_t *p,
+                    JNIEnv **env,
+                    jk_logger_t *l)
+{
+    JDK1_1InitArgs vm_args;  
+    JNIEnv *penv;
+    int err;
+    *env = NULL;
+
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into open_jvm1\n");
+
+    vm_args.version = JNI_VERSION_1_1;
+
+    if(0 != jni_get_default_java_vm_init_args(&vm_args)) {
+    	jk_log(l, JK_LOG_EMERG, "Fail-> can't get default vm init args\n"); 
+        return JK_FALSE;
+    }
+    jk_log(l, JK_LOG_DEBUG, 
+           "In open_jvm_dll, got default jvm args\n"); 
+
+    if(vm_args.classpath) {
+        unsigned len = strlen(vm_args.classpath) + 
+                       strlen(p->tomcat_classpath) + 
+                       3;
+        char *tmp = jk_pool_alloc(&p->p, len);
+        if(tmp) {
+            sprintf(tmp, "%s%c%s", 
+                    strdup_ascii(&p->p, p->tomcat_classpath), 
+                    PATH_SEPERATOR,
+                    vm_args.classpath);
+            p->tomcat_classpath = tmp;
+        } else {
+	        jk_log(l, JK_LOG_EMERG, 
+                   "Fail-> allocation error for classpath\n"); 
+            return JK_FALSE;
+        }
+    }
+    vm_args.classpath = p->tomcat_classpath;
+
+    if(p->tomcat_mx) {
+        vm_args.maxHeapSize = p->tomcat_mx;
+    }
+
+    if(p->tomcat_ms) {
+        vm_args.minHeapSize = p->tomcat_ms;
+    }
+
+    if(p->sysprops) {
+        /* No EBCDIC to ASCII conversion here for AS/400 - later */
+	vm_args.properties = p->sysprops;
+    }
+
+    jk_log(l, JK_LOG_DEBUG, "In open_jvm1, about to create JVM...\n");
+    if((err=jni_create_java_vm(&(p->jvm), 
+                               &penv, 
+                               &vm_args)) != 0) {
+	    jk_log(l, JK_LOG_EMERG, 
+               "Fail-> could not create JVM, code: %d \n", err); 
+        return JK_FALSE;
+    }
+    jk_log(l, JK_LOG_DEBUG, 
+           "In open_jvm1, JVM created, done\n");
+
+    *env = penv;
+
+    return JK_TRUE;
+}
+
+#ifdef JNI_VERSION_1_2
+static int detect_jvm_version(jk_logger_t *l)
+{
+    JNIEnv *env = NULL;
+    JDK1_1InitArgs vm_args;
+
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into detect_jvm_version\n");
+
+    /* [V] Idea: ask for 1.2. If the JVM is 1.1 it will return 1.1 instead  */
+    /*     Note: asking for 1.1 won't work, 'cause 1.2 JVMs will return 1.1 */
+    vm_args.version = JNI_VERSION_1_2;
+
+    if(0 != jni_get_default_java_vm_init_args(&vm_args)) {
+    	jk_log(l, JK_LOG_EMERG, "Fail-> can't get default vm init args\n"); 
+        return JK_FALSE;
+    }
+    jk_log(l, JK_LOG_DEBUG, 
+           "In detect_jvm_version, found: %X, done\n", vm_args.version);
+
+    return vm_args.version;
+}
+
+static char* build_opt_str(jk_pool_t *p, 
+                           char* opt_name, 
+                           char* opt_value, 
+                           jk_logger_t *l)
+{
+    unsigned len = strlen(opt_name) + strlen(opt_value) + 2;
+
+    /* [V] IMHO, these should not be deallocated as long as the JVM runs */
+    char *tmp = jk_pool_alloc(p, len);
+
+    if(tmp) {
+	    sprintf(tmp, "%s%s", opt_name, opt_value);
+	    return tmp;
+    } else {
+	    jk_log(l, JK_LOG_EMERG, 
+               "Fail-> build_opt_str allocation error for %s\n", opt_name);
+	    return NULL;
+    }
+}
+
+static char* build_opt_int(jk_pool_t *p, 
+                           char* opt_name, 
+                           int opt_value, 
+                           jk_logger_t *l)
+{
+    /* [V] this should suffice even for 64-bit int */
+    unsigned len = strlen(opt_name) + 20 + 2;
+    /* [V] IMHO, these should not be deallocated as long as the JVM runs */
+    char *tmp = jk_pool_alloc(p, len);
+
+    if(tmp) {
+	    sprintf(tmp, "%s%d", opt_name, opt_value);
+	    return tmp;
+    } else {
+	    jk_log(l, JK_LOG_EMERG, 
+               "Fail-> build_opt_int allocation error for %s\n", opt_name);
+	    return NULL;
+    }
+}
+
+static int open_jvm2(jni_worker_t *p,
+                    JNIEnv **env,
+                    jk_logger_t *l)
+{
+    JavaVMInitArgs vm_args;
+    JNIEnv *penv = NULL;
+    JavaVMOption options[100];
+    int optn = 0, err;
+    char* tmp;
+
+    *env = NULL;
+
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into open_jvm2\n");
+
+    vm_args.version = JNI_VERSION_1_2;
+    vm_args.options = options;
+
+/* AS/400 need EBCDIC to ASCII conversion to parameters passed to JNI */
+/* No conversion for ASCII based systems (what about BS2000 ?) */
+
+    if(p->tomcat_classpath) {
+    	jk_log(l, JK_LOG_DEBUG, "In open_jvm2, setting classpath to %s\n", p->tomcat_classpath);
+	    tmp = build_opt_str(&p->p, "-Djava.class.path=", p->tomcat_classpath, l);
+	    null_check(tmp);
+        options[optn++].optionString = strdup_ascii(&p->p, tmp);
+    }
+
+    if(p->tomcat_mx) {
+	    jk_log(l, JK_LOG_DEBUG, "In open_jvm2, setting max heap to %d\n", p->tomcat_mx);
+    	tmp = build_opt_int(&p->p, "-Xmx", p->tomcat_mx, l);
+	    null_check(tmp);
+        options[optn++].optionString = strdup_ascii(&p->p, tmp);
+    }
+
+    if(p->tomcat_ms) {
+    	jk_log(l, JK_LOG_DEBUG, "In open_jvm2, setting start heap to %d\n", p->tomcat_ms);
+        tmp = build_opt_int(&p->p, "-Xms", p->tomcat_ms, l);
+	    null_check(tmp);
+        options[optn++].optionString = strdup_ascii(&p->p, tmp);
+    }
+
+    if(p->sysprops) {
+	    int i = 0;
+	    while(p->sysprops[i]) {
+	        jk_log(l, JK_LOG_DEBUG, "In open_jvm2, setting %s\n", p->sysprops[i]);
+	        tmp = build_opt_str(&p->p, "-D", p->sysprops[i], l);
+	        null_check(tmp);
+	        options[optn++].optionString = strdup_ascii(&p->p, tmp);
+	        i++;
+	    }
+    }
+
+    if(p->java2opts) {
+	    int i=0;
+
+	    while(p->java2opts[i]) {
+	        jk_log(l, JK_LOG_DEBUG, "In open_jvm2, using option: %s\n", p->java2opts[i]);
+	        /* Pass it "as is" */
+	        options[optn++].optionString = strdup_ascii(&p->p, p->java2opts[i++]);
+	    }
+    }
+
+    vm_args.nOptions = optn;
+    
+    if(p->java2lax) {
+    	jk_log(l, JK_LOG_DEBUG, "In open_jvm2, the JVM will ignore unknown options\n");
+	    vm_args.ignoreUnrecognized = JNI_TRUE;
+    } else {
+    	jk_log(l, JK_LOG_DEBUG, "In open_jvm2, the JVM will FAIL if it finds unknown options\n");
+	    vm_args.ignoreUnrecognized = JNI_FALSE;
+    }
+
+    jk_log(l, JK_LOG_DEBUG, "In open_jvm2, about to create JVM...\n");
+
+    err=jni_create_java_vm(&(p->jvm), &penv, &vm_args);
+
+    if (JNI_EEXIST == err) {
+#ifdef AS400
+        long vmCount;
+#else
+        int  vmCount;
+#endif
+       jk_log(l, JK_LOG_DEBUG, "JVM alread instantiated."
+              "Trying to attach instead.\n");
+
+        jni_get_created_java_vms(&(p->jvm), 1, &vmCount);
+        if (NULL != p->jvm)
+            penv = attach_to_jvm(p, l);
+
+        if (NULL != penv)
+            err = 0;
+    }
+
+    if(err != 0) {
+    	jk_log(l, JK_LOG_EMERG, "Fail-> could not create JVM, code: %d \n", err); 
+        return JK_FALSE;
+    }
+    jk_log(l, JK_LOG_DEBUG, "In open_jvm2, JVM created, done\n");
+
+    *env = penv;
+
+    return JK_TRUE;
+}
+#endif
+
+static int get_bridge_object(jni_worker_t *p,
+                             JNIEnv *env,
+                             jk_logger_t *l)
+{
+	char *	btype;
+	char *	ctype;
+	
+    jmethodID  constructor_method_id;
+
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into get_bridge_object\n");
+
+	switch (p->bridge_type)
+	{
+		case TC32_BRIDGE_TYPE :
+			btype = TC32_JAVA_BRIDGE_CLASS_NAME;
+			break;
+			
+		case TC33_BRIDGE_TYPE :
+			btype = TC33_JAVA_BRIDGE_CLASS_NAME;
+			break;
+			
+		case TC40_BRIDGE_TYPE :
+		case TC41_BRIDGE_TYPE :
+		case TC50_BRIDGE_TYPE :
+	    	jk_log(l, JK_LOG_EMERG, "Bridge type %d not supported\n", p->bridge_type);
+	    	return JK_FALSE;
+	}
+	
+/* AS400/BS2000 need conversion from EBCDIC to ASCII before passing to JNI */
+/* for others, strdup_ascii is just jk_pool_strdup */
+
+    ctype = strdup_ascii(&p->p, btype);
+
+    p->jk_java_bridge_class = (*env)->FindClass(env, ctype);
+
+    if(!p->jk_java_bridge_class) {
+	    jk_log(l, JK_LOG_EMERG, "Can't find class %s\n", btype);
+	    return JK_FALSE;
+    }
+    jk_log(l, JK_LOG_DEBUG, 
+           "In get_bridge_object, loaded %s bridge class\n", btype);
+
+    constructor_method_id = (*env)->GetMethodID(env,
+                                                p->jk_java_bridge_class,
+                                                strdup_ascii(&p->p, "<init>"), /* method name */
+                                                strdup_ascii(&p->p, "()V"));   /* method sign */
+
+    if(!constructor_method_id) {
+	    p->jk_java_bridge_class = (jclass)NULL;
+	    jk_log(l, JK_LOG_EMERG, 
+               "Can't find constructor\n");
+	    return JK_FALSE;
+    }
+
+    p->jk_java_bridge_object = (*env)->NewObject(env,
+                                                 p->jk_java_bridge_class,
+                                                 constructor_method_id);
+    if(! p->jk_java_bridge_object) {
+	    p->jk_java_bridge_class = (jclass)NULL;
+	    jk_log(l, JK_LOG_EMERG, 
+               "Can't create new bridge object\n");
+	    return JK_FALSE;
+    }
+
+    p->jk_java_bridge_object = (jobject)(*env)->NewGlobalRef(env, p->jk_java_bridge_object);
+    if(! p->jk_java_bridge_object) {
+	    jk_log(l, JK_LOG_EMERG, "Can't create global ref to bridge object\n");
+	    p->jk_java_bridge_class = (jclass)NULL;
+        p->jk_java_bridge_object = (jobject)NULL;
+	    return JK_FALSE;
+    }
+
+    jk_log(l, JK_LOG_DEBUG, 
+           "In get_bridge_object, bridge built, done\n");
+    return JK_TRUE;
+}
+
+static int get_method_ids(jni_worker_t *p,
+                          JNIEnv *env,
+                          jk_logger_t *l)
+{
+
+/* AS400/BS2000 need conversion from EBCDIC to ASCII before passing to JNI */
+
+    p->jk_startup_method = (*env)->GetMethodID(env,
+                                               p->jk_java_bridge_class, 
+                                               strdup_ascii(&p->p, "startup"), 
+                                               strdup_ascii(&p->p, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I"));  
+
+    if(!p->jk_startup_method) {
+	jk_log(l, JK_LOG_EMERG, "Can't find startup()\n"); 
+	return JK_FALSE;
+    }
+
+    p->jk_service_method = (*env)->GetMethodID(env,
+                                               p->jk_java_bridge_class, 
+                                               strdup_ascii(&p->p, "service"), 
+                                               strdup_ascii(&p->p, "(JJ)I"));   
+
+    if(!p->jk_service_method) {
+	jk_log(l, JK_LOG_EMERG, "Can't find service()\n"); 
+        return JK_FALSE;
+    }
+
+    p->jk_shutdown_method = (*env)->GetMethodID(env,
+                                                p->jk_java_bridge_class, 
+                                                strdup_ascii(&p->p, "shutdown"), 
+                                                strdup_ascii(&p->p, "()V"));   
+
+    if(!p->jk_shutdown_method) {
+	jk_log(l, JK_LOG_EMERG, "Can't find shutdown()\n"); 
+        return JK_FALSE;
+    }    
+    
+    return JK_TRUE;
+}
+
+static JNIEnv *attach_to_jvm(jni_worker_t *p, jk_logger_t *l)
+{
+    JNIEnv *rc = NULL;
+    /* [V] This message is important. If there are signal mask issues,    *
+     *     the JVM usually hangs when a new thread tries to attach to it  */
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into attach_to_jvm\n");
+
+#if defined LINUX && defined APACHE2_SIGHACK
+    linux_signal_hack();
+#endif
+
+    if(0 == (*(p->jvm))->AttachCurrentThread(p->jvm,
+#ifdef JNI_VERSION_1_2
+					                        (void **)
+#endif
+                                             &rc,
+                                             NULL)) {
+	    jk_log(l, JK_LOG_DEBUG, 
+               "In attach_to_jvm, attached ok\n");
+        return rc;
+    }
+    jk_log(l, JK_LOG_ERROR, 
+           "In attach_to_jvm, cannot attach thread to JVM.\n");
+    return NULL;
+}
+
+/*
+static JNIEnv *attach_to_jvm(jni_worker_t *p)
+{
+    JNIEnv *rc = NULL;
+
+#ifdef LINUX
+    linux_signal_hack();
+#endif    
+
+    if(0 == (*(p->jvm))->AttachCurrentThread(p->jvm, 
+#ifdef JNI_VERSION_1_2 
+           (void **)
+#endif
+                                             &rc, 
+                                             NULL)) {
+        return rc;
+    }
+
+    return NULL;
+}
+*/
+static void detach_from_jvm(jni_worker_t *p, 
+                            jk_logger_t *l)
+{
+    if(!p->jvm || !(*(p->jvm))) {
+	    jk_log(l, JK_LOG_ERROR, 
+               "In detach_from_jvm, cannot detach from NULL JVM.\n");
+    }
+
+    if(0 == (*(p->jvm))->DetachCurrentThread(p->jvm)) {
+	    jk_log(l, JK_LOG_DEBUG, 
+               "In detach_from_jvm, detached ok\n");
+    } else {
+	    jk_log(l, JK_LOG_ERROR, 
+               "In detach_from_jvm, cannot detach from JVM.\n");
+    }
+}
diff --git a/connectors/jk/native/common/jk_jni_worker.h b/connectors/jk/native/common/jk_jni_worker.h
new file mode 100644
index 0000000..c34c8a9
--- /dev/null
+++ b/connectors/jk/native/common/jk_jni_worker.h
@@ -0,0 +1,43 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: jni worker header file                                 *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                               *
+ ***************************************************************************/
+
+#ifndef JK_JNI_WORKER_H
+#define JK_JNI_WORKER_H
+
+#include "jk_logger.h"
+#include "jk_service.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define JK_JNI_WORKER_NAME ("jni")
+
+int JK_METHOD jni_worker_factory(jk_worker_t **w,
+                                 const char *name,
+                                 jk_logger_t *l);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_JNI_WORKER_H */
diff --git a/connectors/jk/native/common/jk_lb_worker.c b/connectors/jk/native/common/jk_lb_worker.c
new file mode 100644
index 0000000..e0d0df0
--- /dev/null
+++ b/connectors/jk/native/common/jk_lb_worker.c
@@ -0,0 +1,609 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Load balancer worker, knows how to load balance among      *
+ *              several workers.                                           *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Based on:                                                               *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#include "jk_pool.h"
+#include "jk_service.h"
+#include "jk_util.h"
+#include "jk_worker.h"
+#include "jk_lb_worker.h"
+
+/*
+ * The load balancing code in this 
+ */
+
+
+/* 
+ * Time to wait before retry...
+ */
+#define WAIT_BEFORE_RECOVER (60*1) 
+#define ADDITINAL_WAIT_LOAD (20)
+
+struct worker_record {
+    char    *name;
+    double  lb_factor;
+    double  lb_value;
+    int     is_local_worker;
+    int     in_error_state;
+    int     in_recovering;
+    time_t  error_time;
+    jk_worker_t *w;
+};
+typedef struct worker_record worker_record_t;
+
+struct lb_worker {
+    worker_record_t *lb_workers;
+    unsigned num_of_workers;
+
+    jk_pool_t p;
+    jk_pool_atom_t buf[TINY_POOL_SIZE];
+
+    char *name; 
+    jk_worker_t worker;
+    int  in_local_worker_mode;
+    int  local_worker_only;
+    int  sticky_session;
+};
+typedef struct lb_worker lb_worker_t;
+
+struct lb_endpoint {    
+    jk_endpoint_t *e;
+    lb_worker_t *worker;
+    
+    jk_endpoint_t endpoint;
+};
+typedef struct lb_endpoint lb_endpoint_t;
+
+
+/* ========================================================================= */
+/* Retrieve the parameter with the given name                                */
+static char *get_path_param(jk_ws_service_t *s,
+                            const char *name)
+{
+    char *id_start = NULL;
+    for(id_start = strstr(s->req_uri, name) ; 
+        id_start ; 
+        id_start = strstr(id_start + 1, name)) {
+        if('=' == id_start[strlen(name)]) {
+            /*
+             * Session path-cookie was found, get it's value
+             */
+            id_start += (1 + strlen(name));
+            if(strlen(id_start)) {
+                char *id_end;
+                id_start = jk_pool_strdup(s->pool, id_start);
+                /* 
+                 * The query string is not part of req_uri, however
+                 * to be on the safe side lets remove the trailing query 
+                 * string if appended...
+                 */
+                if(id_end = strchr(id_start, '?')) { 
+                    *id_end = '\0';
+                }
+                /*
+                 * Remove any trailing path element.
+                 */
+                if((id_end = strchr(id_start, ';')) != NULL) {
+                    *id_end = '\0';
+                }
+                return id_start;
+            }
+        }
+    }
+  
+    return NULL;
+}
+
+/* ========================================================================= */
+/* Retrieve the cookie with the given name                                   */
+static char *get_cookie(jk_ws_service_t *s,
+                        const char *name)
+{
+    unsigned i;
+    char *result = NULL;
+
+    for(i = 0 ; i < s->num_headers ; i++) {
+        if(0 == strcasecmp(s->headers_names[i], "cookie")) {
+
+            char *id_start;
+            for(id_start = strstr(s->headers_values[i], name) ; 
+                id_start ; 
+                id_start = strstr(id_start + 1, name)) {
+                if(id_start == s->headers_values[i] ||
+                   id_start[-1] == ';' ||
+                   id_start[-1] == ',' ||
+                   isspace(id_start[-1]) ) {
+                    id_start += strlen(name);
+                    while(*id_start && isspace(*id_start))
+                        ++id_start;
+                    if(*id_start == '=' && id_start[1]) {
+                        /*
+                         * Session cookie was found, get it's value
+                         */
+                        char *id_end;
+                        ++id_start;
+                        id_start = jk_pool_strdup(s->pool, id_start);
+                        if((id_end = strchr(id_start, ';')) != NULL) {
+                            *id_end = '\0';
+                        }
+                        if((id_end = strchr(id_start, ',')) != NULL) {
+                            *id_end = '\0';
+                        }
+                        if(result == NULL) {
+                            result = id_start;
+                        } else {
+                            int osz = strlen(result)+1;
+                            int sz  = osz + strlen(id_start)+1;
+                            result = jk_pool_realloc(s->pool, sz, result, osz);
+                            strcat(result,";");
+                            strcat(result, id_start);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
+
+/* ========================================================================= */
+/* Retrieve session id from the cookie or the parameter                      */
+/* (parameter first)                                                         */
+static char *get_sessionid(jk_ws_service_t *s)
+{
+    char *val;
+    val = get_path_param(s, JK_PATH_SESSION_IDENTIFIER);
+    if(!val) {
+        val = get_cookie(s, JK_SESSION_IDENTIFIER);
+    }
+    return val;
+}
+
+static void close_workers(lb_worker_t *p, 
+                          int num_of_workers,
+                          jk_logger_t *l)
+{
+    int i = 0;
+    for(i = 0 ; i < num_of_workers ; i++) {
+        p->lb_workers[i].w->destroy(&(p->lb_workers[i].w),
+                                    l);
+    }
+}
+
+static double get_max_lb(lb_worker_t *p) 
+{
+    unsigned i;
+    double rc = 0.0;    
+
+    for(i = 0 ; i < p->num_of_workers ; i++) {
+        if(!p->lb_workers[i].in_error_state) {
+            if(p->lb_workers[i].lb_value > rc) {
+                rc = p->lb_workers[i].lb_value;
+            }
+        }            
+    }
+
+    return rc;
+
+}
+
+static worker_record_t *get_most_suitable_worker(lb_worker_t *p, 
+                                                 jk_ws_service_t *s, int attempt)
+{
+    worker_record_t *rc = NULL;
+    double lb_min = 0.0;    
+    unsigned i;
+    char *sessionid = NULL;
+
+    if (p->sticky_session) {
+        sessionid = get_sessionid(s);
+    }
+
+    while(sessionid) {
+        char *next = strchr(sessionid, ';');
+        char *session_route;
+        if(next) {
+            *next++ = '\0';
+        }
+        session_route = strchr(sessionid, '.');
+        if(session_route) {
+          ++session_route;
+          for(i = 0 ; i < p->num_of_workers ; i++) {
+            if(0 == strcmp(session_route, p->lb_workers[i].name)) {
+              /* First attempt will allways be to the
+                 correct host. If this is indeed down and no
+                 hope of recovery, we'll go to fail-over
+              */
+              if(attempt>0 && p->lb_workers[i].in_error_state) {
+                next = NULL; /* Double break; */
+                break;
+              } else {
+                return &(p->lb_workers[i]);
+              }
+            }
+          }
+        }
+        sessionid = next;
+    }
+
+    for(i = 0 ; i < p->num_of_workers ; i++) {
+        if (!p->in_local_worker_mode || p->lb_workers[i].is_local_worker || !p->local_worker_only) {
+            if(p->lb_workers[i].in_error_state) {
+                if(!p->lb_workers[i].in_recovering) {
+                    time_t now = time(0);
+                    if((now - p->lb_workers[i].error_time) > WAIT_BEFORE_RECOVER) {
+                        p->lb_workers[i].in_recovering  = JK_TRUE;
+                        p->lb_workers[i].error_time     = now;
+                        rc = &(p->lb_workers[i]);
+    
+                        break;
+                    }
+                }
+            } else {
+                if(p->lb_workers[i].lb_value < lb_min || !rc) {
+                    lb_min = p->lb_workers[i].lb_value;
+                    rc = &(p->lb_workers[i]);
+                    if (rc->is_local_worker) break;
+                }
+            }
+        }
+    }
+
+    if(rc && !rc->is_local_worker) {
+        rc->lb_value += rc->lb_factor;                
+    }
+
+    return rc;
+}
+    
+static int JK_METHOD service(jk_endpoint_t *e, 
+                             jk_ws_service_t *s,
+                             jk_logger_t *l,
+                             int *is_recoverable_error)
+{
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into jk_endpoint_t::service\n");
+
+    if(e && e->endpoint_private && s && is_recoverable_error) {
+        lb_endpoint_t *p = e->endpoint_private;
+        jk_endpoint_t *end = NULL;
+        int attempt=0;
+
+        /* you can not recover on another load balancer */
+        *is_recoverable_error = JK_FALSE;
+
+        /* set the recovery post, for LB mode */        
+        s->reco_buf = jk_b_new(s->pool);
+        jk_b_set_buffer_size(s->reco_buf, DEF_BUFFER_SZ);
+        jk_b_reset(s->reco_buf);
+        s->reco_status = RECO_INITED;                                                                                                                                
+        jk_log(l, JK_LOG_DEBUG, "Into jk_endpoint_t::service sticky_session=%d\n", 
+               p->worker->sticky_session);
+
+        while(1) {
+            worker_record_t *rec = get_most_suitable_worker(p->worker, s, attempt++);
+            int rc;
+
+            if(rec) {
+                int is_recoverable = JK_FALSE;
+                
+                s->jvm_route = jk_pool_strdup(s->pool,  rec->name);
+
+                rc = rec->w->get_endpoint(rec->w, &end, l);
+
+                jk_log(l, JK_LOG_DEBUG, "Into jk_endpoint_t::service worker=%s jvm_route=%s rc=%d\n", 
+                       rec->name, s->jvm_route, rc);
+
+                if(rc && end) {
+                    int src = end->service(end, s, l, &is_recoverable);
+                    end->done(&end, l);
+                    if(src ) {                        
+                        if(rec->in_recovering && rec->lb_value != 0) {
+                            rec->lb_value = get_max_lb(p->worker) + ADDITINAL_WAIT_LOAD;
+                        }
+                        rec->in_error_state = JK_FALSE;
+                        rec->in_recovering  = JK_FALSE;
+                        rec->error_time     = 0;                        
+                        return JK_TRUE;
+                    } 
+                }
+
+                /*
+                 * Service failed !!!
+                 *
+                 * Time for fault tolerance (if possible)...
+                 */
+
+                rec->in_error_state = JK_TRUE;
+                rec->in_recovering  = JK_FALSE;
+                rec->error_time     = time(0);
+
+                if(!is_recoverable) {
+                    /*
+                     * Error is not recoverable - break with an error.
+                     */
+                    jk_log(l, JK_LOG_ERROR, 
+                           "lb: unrecoverable error, request failed. Tomcat failed in the middle of request, we can't recover to another instance.\n");
+                    break;
+                }
+
+                /* 
+                 * Error is recoverable by submitting the request to
+                 * another worker... Lets try to do that.
+                 */
+                 jk_log(l, JK_LOG_DEBUG, 
+                        "lb: recoverable error... will try to recover on other host\n");
+            } else {
+                /* NULL record, no more workers left ... */
+                 jk_log(l, JK_LOG_ERROR, 
+                        "lb: All tomcat instances failed, no more workers left.\n");
+                 return JK_FALSE;
+                break;
+            }
+        }
+    }
+
+    jk_log(l, JK_LOG_ERROR, 
+           "lb: end of service with error\n");
+
+    return JK_FALSE;
+}
+
+static int JK_METHOD done(jk_endpoint_t **e,
+                          jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into jk_endpoint_t::done\n");
+
+    if(e && *e && (*e)->endpoint_private) {
+        lb_endpoint_t *p = (*e)->endpoint_private;
+
+        if(p->e) {
+            p->e->done(&p->e, l);
+        }
+
+        free(p);
+        *e = NULL;
+        return JK_TRUE;
+    }
+
+    jk_log(l, JK_LOG_ERROR, 
+           "In jk_endpoint_t::done: NULL Parameters\n");
+
+    return JK_FALSE;
+}
+
+static int JK_METHOD validate(jk_worker_t *pThis,
+                              jk_map_t *props,                            
+                              jk_worker_env_t *we,
+                              jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into jk_worker_t::validate\n");
+
+    if(pThis && pThis->worker_private) {        
+        lb_worker_t *p = pThis->worker_private;
+        char **worker_names;
+        unsigned num_of_workers;
+        p->in_local_worker_mode = JK_FALSE;
+        p->local_worker_only = jk_get_local_worker_only_flag(props, p->name);
+        p->sticky_session = jk_get_is_sticky_session(props, p->name);
+        
+        if(jk_get_lb_worker_list(props,
+                                 p->name,
+                                 &worker_names, 
+                                 &num_of_workers) && num_of_workers) {
+            unsigned i = 0;
+            unsigned j = 0;
+
+            p->lb_workers = jk_pool_alloc(&p->p, 
+                                          num_of_workers * sizeof(worker_record_t));
+
+            if(!p->lb_workers) {
+                return JK_FALSE;
+            }
+
+            for(i = 0 ; i < num_of_workers ; i++) {
+                p->lb_workers[i].name = jk_pool_strdup(&p->p, worker_names[i]);
+                p->lb_workers[i].lb_factor = jk_get_lb_factor(props, 
+                                                               worker_names[i]);
+                if(p->lb_workers[i].lb_factor < 0) {
+                    p->lb_workers[i].lb_factor = -1 * p->lb_workers[i].lb_factor;
+                }
+                if (0 < p->lb_workers[i].lb_factor) {
+                    p->lb_workers[i].lb_factor = 1/p->lb_workers[i].lb_factor;
+                }
+
+                p->lb_workers[i].is_local_worker = jk_get_is_local_worker(props, worker_names[i]);
+                if (p->lb_workers[i].is_local_worker) p->in_local_worker_mode = JK_TRUE;
+                /* 
+                 * Allow using lb in fault-tolerant mode.
+                 * A value of 0 means the worker will be used for all requests without
+                 * sessions
+                 */
+                p->lb_workers[i].lb_value = p->lb_workers[i].lb_factor;
+                p->lb_workers[i].in_error_state = JK_FALSE;
+                p->lb_workers[i].in_recovering  = JK_FALSE;
+                if(!wc_create_worker(p->lb_workers[i].name, 
+                                     props, 
+                                     &(p->lb_workers[i].w), 
+                                     we,
+                                     l) || !p->lb_workers[i].w) {
+                    break;
+                } else if (p->lb_workers[i].is_local_worker) {
+                    /*
+                     * If lb_value is 0 than move it at the beginning of the list
+                     */
+                    if (i != j) {
+                        worker_record_t tmp_worker;
+                        tmp_worker = p->lb_workers[j];
+                        p->lb_workers[j] = p->lb_workers[i];
+                        p->lb_workers[i] = tmp_worker;
+                    }
+                    j++;
+                }
+            }
+            
+            if (!p->in_local_worker_mode) {
+                p->local_worker_only = JK_FALSE;
+            }
+            
+            if(i != num_of_workers) {
+                close_workers(p, i, l);
+                jk_log(l, JK_LOG_DEBUG, 
+                       "In jk_worker_t::validate: Failed to create worker %s\n",
+                       p->lb_workers[i].name);
+
+            } else {
+                for (i = 0; i < num_of_workers; i++) {
+                    jk_log(l, JK_LOG_DEBUG,
+                        "Balanced worker %i has name %s\n",
+                        i, p->lb_workers[i].name);
+                }
+                jk_log(l, JK_LOG_DEBUG,
+                        "in_local_worker_mode: %s\n",
+                        (p->in_local_worker_mode ? "true" : "false"));
+                jk_log(l, JK_LOG_DEBUG,
+                        "local_worker_only: %s\n",
+                        (p->local_worker_only ? "true" : "false"));
+                p->num_of_workers = num_of_workers;
+                return JK_TRUE;
+            }
+        }        
+    }
+
+    jk_log(l, JK_LOG_ERROR, 
+           "In jk_worker_t::validate: NULL Parameters\n");
+
+    return JK_FALSE;
+}
+
+static int JK_METHOD init(jk_worker_t *pThis,
+                          jk_map_t *props, 
+                          jk_worker_env_t *we,
+                          jk_logger_t *log)
+{
+    /* Nothing to do for now */
+    return JK_TRUE;
+}
+
+static int JK_METHOD get_endpoint(jk_worker_t *pThis,
+                                  jk_endpoint_t **pend,
+                                  jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into jk_worker_t::get_endpoint\n");
+
+    if(pThis && pThis->worker_private && pend) {        
+        lb_endpoint_t *p = (lb_endpoint_t *)malloc(sizeof(lb_endpoint_t));
+        if(p) {
+            p->e = NULL;
+            p->worker = pThis->worker_private;
+            p->endpoint.endpoint_private = p;
+            p->endpoint.service = service;
+            p->endpoint.done = done;
+            *pend = &p->endpoint;
+
+            return JK_TRUE;
+        }
+        jk_log(l, JK_LOG_ERROR, 
+               "In jk_worker_t::get_endpoint, malloc failed\n");
+    } else {
+        jk_log(l, JK_LOG_ERROR, 
+               "In jk_worker_t::get_endpoint, NULL parameters\n");
+    }
+
+    return JK_FALSE;
+}
+
+static int JK_METHOD destroy(jk_worker_t **pThis,
+                             jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into jk_worker_t::destroy\n");
+
+    if(pThis && *pThis && (*pThis)->worker_private) {
+        lb_worker_t *private_data = (*pThis)->worker_private;
+
+        close_workers(private_data, 
+                      private_data->num_of_workers,
+                      l);
+
+        jk_close_pool(&private_data->p);
+        free(private_data);
+
+        return JK_TRUE;
+    }
+
+    jk_log(l, JK_LOG_ERROR, 
+           "In jk_worker_t::destroy, NULL parameters\n");
+    return JK_FALSE;
+}
+
+int JK_METHOD lb_worker_factory(jk_worker_t **w,
+                                const char *name,
+                                jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into lb_worker_factory\n");
+
+    if(NULL != name && NULL != w) {
+        lb_worker_t *private_data = 
+            (lb_worker_t *)malloc(sizeof(lb_worker_t));
+
+        if(private_data) {
+
+            jk_open_pool(&private_data->p, 
+                         private_data->buf, 
+                         sizeof(jk_pool_atom_t) * TINY_POOL_SIZE);
+
+            private_data->name = jk_pool_strdup(&private_data->p, name);          
+
+            if(private_data->name) {
+                private_data->lb_workers = NULL;
+                private_data->num_of_workers = 0;
+                private_data->worker.worker_private = private_data;
+                private_data->worker.validate       = validate;
+                private_data->worker.init           = init;
+                private_data->worker.get_endpoint   = get_endpoint;
+                private_data->worker.destroy        = destroy;
+
+                *w = &private_data->worker;
+                return JK_TRUE;
+            }
+
+            jk_close_pool(&private_data->p);
+            free(private_data);
+        }
+        jk_log(l, JK_LOG_ERROR, 
+               "In lb_worker_factory, malloc failed\n");
+    } else {
+        jk_log(l, JK_LOG_ERROR, 
+               "In lb_worker_factory, NULL parameters\n");
+    }
+
+    return JK_FALSE;
+}
+
diff --git a/connectors/jk/native/common/jk_lb_worker.h b/connectors/jk/native/common/jk_lb_worker.h
new file mode 100644
index 0000000..bf8aefd
--- /dev/null
+++ b/connectors/jk/native/common/jk_lb_worker.h
@@ -0,0 +1,43 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: load balance worker header file                            *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef JK_LB_WORKER_H
+#define JK_LB_WORKER_H
+
+#include "jk_logger.h"
+#include "jk_service.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define JK_LB_WORKER_NAME ("lb")
+
+int JK_METHOD lb_worker_factory(jk_worker_t **w,
+                                const char *name,
+                                jk_logger_t *l);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_LB_WORKER_H */
diff --git a/connectors/jk/native/common/jk_logger.h b/connectors/jk/native/common/jk_logger.h
new file mode 100644
index 0000000..ab9258d
--- /dev/null
+++ b/connectors/jk/native/common/jk_logger.h
@@ -0,0 +1,71 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Logger object definitions                                  *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef JK_LOGGER_H
+#define JK_LOGGER_H
+
+#include "jk_global.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct jk_logger jk_logger_t;
+struct jk_logger {
+    void *logger_private;
+    int  level;
+
+    int (JK_METHOD *log)(jk_logger_t *l,
+                         int level,
+                         const char *what);
+
+};
+
+struct file_logger {
+    FILE *logfile;
+    /* For Apache 2 APR piped logging */
+    void *jklogfp;
+};
+typedef struct file_logger file_logger_t;
+
+#define JK_LOG_DEBUG_LEVEL   0
+#define JK_LOG_INFO_LEVEL    1
+#define JK_LOG_ERROR_LEVEL   2
+#define JK_LOG_EMERG_LEVEL   3
+#define JK_LOG_REQUEST_LEVEL 4
+
+#define JK_LOG_DEBUG_VERB   "debug"
+#define JK_LOG_INFO_VERB    "info"
+#define JK_LOG_ERROR_VERB   "error"
+#define JK_LOG_EMERG_VERB   "emerg"
+
+#define JK_LOG_DEBUG   __FILE__,__LINE__,JK_LOG_DEBUG_LEVEL
+#define JK_LOG_INFO    __FILE__,__LINE__,JK_LOG_INFO_LEVEL
+#define JK_LOG_ERROR   __FILE__,__LINE__,JK_LOG_ERROR_LEVEL
+#define JK_LOG_EMERG   __FILE__,__LINE__,JK_LOG_EMERG_LEVEL
+#define JK_LOG_REQUEST __FILE__,0,JK_LOG_REQUEST_LEVEL
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_LOGGER_H */
diff --git a/connectors/jk/native/common/jk_map.c b/connectors/jk/native/common/jk_map.c
new file mode 100644
index 0000000..a03c01f
--- /dev/null
+++ b/connectors/jk/native/common/jk_map.c
@@ -0,0 +1,467 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: General purpose map object                                 *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+#ifdef AS400
+#include "apr_xlate.h"    
+#endif
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_map.h"
+#include "jk_util.h"
+
+#define CAPACITY_INC_SIZE (50)
+#define LENGTH_OF_LINE    (1024)
+
+struct jk_map {
+    jk_pool_t p;
+    jk_pool_atom_t buf[SMALL_POOL_SIZE];
+
+    const char **names;
+    const void **values;
+
+    unsigned capacity;
+    unsigned size;
+};
+
+static void trim_prp_comment(char *prp);
+static int trim(char *s);
+static int map_realloc(jk_map_t *m);
+
+int map_alloc(jk_map_t **m)
+{
+    if(m) {
+        return map_open(*m = (jk_map_t *)malloc(sizeof(jk_map_t)));
+    }
+    
+    return JK_FALSE;
+}
+
+int map_free(jk_map_t **m)
+{
+    int rc = JK_FALSE;
+
+    if(m && *m) {
+        map_close(*m);  
+        free(*m);
+        *m = NULL;
+    }
+    
+    return rc;
+}
+
+int map_open(jk_map_t *m)
+{
+    int rc = JK_FALSE;
+
+    if(m) {
+        jk_open_pool(&m->p, m->buf, sizeof(jk_pool_atom_t) * SMALL_POOL_SIZE);
+        m->capacity = 0;
+        m->size     = 0;
+        m->names    = NULL;
+        m->values   = NULL;
+        rc = JK_TRUE;
+    }
+    
+    return rc;
+}
+
+int map_close(jk_map_t *m)
+{
+    int rc = JK_FALSE;
+
+    if(m) {
+        jk_close_pool(&m->p);
+        rc = JK_TRUE;
+    }
+    
+    return rc;
+}
+
+void *map_get(jk_map_t *m,
+              const char *name,
+              const void *def)
+{
+    const void *rc = (void *)def;
+    
+    if(m && name) {
+        unsigned i;
+        for(i = 0 ; i < m->size ; i++) {
+            if(0 == strcmp(m->names[i], name)) {
+                rc = m->values[i];
+                break;
+            }
+        }
+    }
+
+    return (void *)rc; /* DIRTY */
+}
+
+int map_get_int(jk_map_t *m,
+                const char *name,
+                int def)
+{
+    char buf[100];
+    char *rc;
+    int  len;
+    int  int_res;
+    int  multit = 1;
+
+    sprintf(buf, "%d", def);
+    rc = map_get_string(m, name, buf);
+
+    len = strlen(rc);
+    if(len) {        
+        char *lastchar = rc + len - 1;
+        if('m' == *lastchar || 'M' == *lastchar) {
+            *lastchar = '\0';
+            multit = 1024 * 1024;
+        } else if('k' == *lastchar || 'K' == *lastchar) {
+            *lastchar = '\0';
+            multit = 1024;
+        }
+    }
+
+    int_res = atoi(rc);
+
+    return int_res * multit;
+}
+
+double map_get_double(jk_map_t *m,
+                      const char *name,
+                      double def)
+{
+    char buf[100];
+    char *rc;
+
+    sprintf(buf, "%f", def);
+    rc = map_get_string(m, name, buf);
+
+    return atof(rc);
+}
+
+char *map_get_string(jk_map_t *m,
+                    const char *name,
+                    const char *def)
+{
+    return map_get(m, name, def);
+}
+
+char **map_get_string_list(jk_map_t *m,
+                           const char *name,
+                           unsigned *list_len,
+                           const char *def)
+{
+    char *l = map_get_string(m, name, def);
+    char **ar = NULL;
+#if defined(AS400) || defined(_REENTRANT)
+    char *lasts;
+#endif
+
+    *list_len = 0;
+
+    if(l) {
+        unsigned capacity = 0;
+        unsigned idex = 0;    
+        char *v = jk_pool_strdup(&m->p, l);
+        
+        if(!v) {
+            return NULL;
+        }
+
+        /*
+         * GS, in addition to VG's patch, we now need to 
+         * strtok also by a "*"
+         */
+#if defined(AS400) || defined(_REENTRANT)
+        for(l = strtok_r(v, " \t,*", &lasts) ;
+            l ;
+	    l = strtok_r(NULL, " \t,*",&lasts)) 
+#else
+        for(l = strtok(v, " \t,*") ; 
+            l ; 
+	       l = strtok(NULL, " \t,*")) 
+#endif
+
+	   {
+        
+            if(idex == capacity) {
+                ar = jk_pool_realloc(&m->p, 
+                                     sizeof(char *) * (capacity + 5),
+                                     ar,
+                                     sizeof(char *) * capacity);
+                if(!ar) {
+                    return JK_FALSE;
+                }
+                capacity += 5;
+            }
+            ar[idex] = jk_pool_strdup(&m->p, l);
+            idex ++;
+        }
+        
+        *list_len = idex;
+    }
+
+    return ar;
+}
+
+int map_put(jk_map_t *m,
+            const char *name,
+            const void *value,
+            void **old)
+{
+    int rc = JK_FALSE;
+
+    if(m && name && old) {
+        unsigned i;
+        for(i = 0 ; i < m->size ; i++) {
+            if(0 == strcmp(m->names[i], name)) {
+                break;
+            }
+        }
+
+        if(i < m->size) {
+            *old = (void *) m->values[i]; /* DIRTY */
+            m->values[i] = value;
+            rc = JK_TRUE;
+        } else {
+            map_realloc(m);
+
+            if(m->size < m->capacity) {
+                m->values[m->size] = value;
+                m->names[m->size] = jk_pool_strdup(&m->p, name);
+                m->size ++;
+                rc = JK_TRUE;
+            }
+        }       
+    }
+
+    return rc;
+}
+
+int map_read_properties(jk_map_t *m,
+                        const char *f)
+{
+    int rc = JK_FALSE;
+
+    if(m && f) {
+#ifdef AS400
+        FILE *fp = fopen(f, "r, o_ccsid=0");
+#else
+        FILE *fp = fopen(f, "r");
+#endif        
+        
+        if(fp) {
+            char buf[LENGTH_OF_LINE + 1];            
+            char *prp;
+            
+            rc = JK_TRUE;
+
+            while(NULL != (prp = fgets(buf, LENGTH_OF_LINE, fp))) {
+                trim_prp_comment(prp);
+                if(trim(prp)) {
+                    char *v = strchr(prp, '=');
+                    if(v) {
+                        *v = '\0';
+                        v++;                        
+                        if(strlen(v) && strlen(prp)) {
+                            char *oldv = map_get_string(m, prp, NULL);
+                            v = map_replace_properties(v, m);
+                            if(oldv) {
+                                char *tmpv = jk_pool_alloc(&m->p, 
+                                                           strlen(v) + strlen(oldv) + 3);
+                                if(tmpv) {
+                                    char sep = '*';
+                                    if(jk_is_path_poperty(prp)) {
+                                        sep = PATH_SEPERATOR;
+                                    } else if(jk_is_cmd_line_poperty(prp)) {
+                                        sep = ' ';
+                                    }
+
+                                    sprintf(tmpv, "%s%c%s", 
+                                            oldv, sep, v);
+                                }                                
+                                v = tmpv;
+                            } else {
+                                v = jk_pool_strdup(&m->p, v);
+                            }
+                            if(v) {
+                                void *old = NULL;
+                                map_put(m, prp, v, &old);
+                            } else {
+                                rc = JK_FALSE;
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+            
+            fclose(fp);
+        }
+    }
+
+    return rc;
+}
+
+
+int map_size(jk_map_t *m)
+{
+    if(m) {
+        return m->size;
+    }
+
+    return -1;
+}
+
+char *map_name_at(jk_map_t *m,
+                  int idex)
+{
+    if(m && idex >= 0) {
+        return (char *)m->names[idex]; /* DIRTY */
+    }
+
+    return NULL;
+}
+
+void *map_value_at(jk_map_t *m,
+                   int idex)
+{
+    if(m && idex >= 0) {
+        return (void *) m->values[idex]; /* DIRTY */
+    }
+
+    return NULL;
+}
+
+static void trim_prp_comment(char *prp)
+{
+#ifdef AS400
+    char *comment;
+  /* lots of lines that translate a '#' realtime deleted   */
+    comment = strchr(prp, *APR_NUMBERSIGN); 
+#else
+    char *comment = strchr(prp, '#');
+#endif
+    if(comment) {
+        *comment = '\0';
+    }
+}
+
+static int trim(char *s)
+{
+    int i;
+
+    for(i = strlen(s) - 1 ; (i >= 0) && isspace(s[i]) ;  i--)
+        ;
+    
+    s[i + 1] = '\0';
+    
+    for(i = 0 ; ('\0' !=  s[i]) && isspace(s[i]) ; i++)
+        ;
+    
+    if(i > 0) {
+        strcpy(s, &s[i]);
+    }
+
+    return strlen(s);
+}
+
+static int map_realloc(jk_map_t *m)
+{
+    if(m->size == m->capacity) {
+        char **names;
+        void **values;
+        int  capacity = m->capacity + CAPACITY_INC_SIZE;
+
+        names = (char **)jk_pool_alloc(&m->p, sizeof(char *) * capacity);
+        values = (void **)jk_pool_alloc(&m->p, sizeof(void *) * capacity);
+        
+        if(values && names) {
+            if (m->capacity && m->names) 
+                memcpy(names, m->names, sizeof(char *) * m->capacity);
+
+            if (m->capacity && m->values)
+                memcpy(values, m->values, sizeof(void *) * m->capacity);
+
+            m->names = (const char **)names;
+            m->values = (const void **)values;
+            m->capacity = capacity;
+
+            return JK_TRUE;
+        }
+    }
+
+    return JK_FALSE;
+}
+
+/**
+ *  Replace $(property) in value.
+ * 
+ */
+char *map_replace_properties(const char *value, jk_map_t *m)
+{
+    char *rc = (char *)value;
+    char *env_start = rc;
+    int rec = 0;
+
+    while(env_start = strstr(env_start, "$(")) {
+        char *env_end = strstr(env_start, ")");
+        if( rec++ > 20 ) return rc;
+        if(env_end) {
+            char env_name[LENGTH_OF_LINE + 1] = ""; 
+            char *env_value;
+
+            *env_end = '\0';
+            strcpy(env_name, env_start + 2);
+            *env_end = ')';
+
+            env_value = map_get_string(m, env_name, NULL);
+	    if(!env_value) {
+	      env_value=getenv( env_name );
+	    }
+            if(env_value) {
+                int offset=0;
+                char *new_value = jk_pool_alloc(&m->p, 
+                                                (sizeof(char) * (strlen(rc) + strlen(env_value))));
+                if(!new_value) {
+                    break;
+                }
+                *env_start = '\0';
+                strcpy(new_value, rc);
+                strcat(new_value, env_value);
+                strcat(new_value, env_end + 1);
+		offset= env_start - rc + strlen( env_value );
+                rc = new_value;
+		/* Avoid recursive subst */
+                env_start = rc + offset; 
+            } else {
+                env_start = env_end;
+            }
+        } else {
+            break;
+        }
+    }
+
+    return rc;
+}
+
diff --git a/connectors/jk/native/common/jk_map.h b/connectors/jk/native/common/jk_map.h
new file mode 100644
index 0000000..59e1a50
--- /dev/null
+++ b/connectors/jk/native/common/jk_map.h
@@ -0,0 +1,91 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Map object header file                                     *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef JK_MAP_H
+#define JK_MAP_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+struct jk_map;
+typedef struct jk_map jk_map_t;
+
+int map_alloc(jk_map_t **m);
+
+int map_free(jk_map_t **m);
+
+int map_open(jk_map_t *m);
+
+int map_close(jk_map_t *m);
+
+void *map_get(jk_map_t *m,
+              const char *name,
+              const void *def);
+
+int map_get_int(jk_map_t *m,
+                const char *name,
+                int def);
+
+double map_get_double(jk_map_t *m,
+                      const char *name,
+                      double def);
+
+char *map_get_string(jk_map_t *m,
+                     const char *name,
+                     const char *def);
+
+char **map_get_string_list(jk_map_t *m,
+                           const char *name,
+                           unsigned *list_len,
+                           const char *def);
+
+int map_put(jk_map_t *m,
+            const char *name,
+            const void *value,
+            void **old);
+
+int map_read_properties(jk_map_t *m,
+                        const char *f);
+
+int map_size(jk_map_t *m);
+
+char *map_name_at(jk_map_t *m,
+                  int idex);
+
+void *map_value_at(jk_map_t *m,
+                   int idex);
+  
+/**
+ *  Replace $(property) in value.
+ * 
+ */
+char *map_replace_properties(const char *value, jk_map_t *m);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_MAP_H */
diff --git a/connectors/jk/native/common/jk_md5.c b/connectors/jk/native/common/jk_md5.c
new file mode 100644
index 0000000..8b4e6da
--- /dev/null
+++ b/connectors/jk/native/common/jk_md5.c
@@ -0,0 +1,487 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/*
+ * This is work is derived from material Copyright RSA Data Security, Inc.
+ *
+ * The RSA copyright statement and Licence for that original material is
+ * included below. This is followed by the Apache copyright statement and
+ * licence for the modifications made to that material.
+ */
+
+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+   rights reserved.
+
+   License to copy and use this software is granted provided that it
+   is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+   Algorithm" in all material mentioning or referencing this software
+   or this function.
+
+   License is also granted to make and use derivative works provided
+   that such works are identified as "derived from the RSA Data
+   Security, Inc. MD5 Message-Digest Algorithm" in all material
+   mentioning or referencing the derived work.
+
+   RSA Data Security, Inc. makes no representations concerning either
+   the merchantability of this software or the suitability of this
+   software for any particular purpose. It is provided "as is"
+   without express or implied warranty of any kind.
+
+   These notices must be retained in any copies of any part of this
+   documentation and/or software.
+ */
+
+/*
+ * The ap_MD5Encode() routine uses much code obtained from the FreeBSD 3.0
+ * MD5 crypt() function, which is licenced as follows:
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ */
+
+/***************************************************************************
+ * Description: MD5 encoding wrapper                                       *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+/*
+ * JK MD5 Encoding function (jk_MD5Encode)
+ *
+ * Jk delegate MD5 encoding to ap_MD5Encode when used in Apache Web-Server.
+ * When another web-server is used like NES/IIS, we should use corresponding calls.
+ * NES/IIS specialists will add the necessary code but until that, I reused the code
+ * from Apache HTTP server.
+ * 
+ * Nota: If you use an EBCDIC system without Apache, you'll have to use MD5 encoding
+ * corresponding call or have a ebcdic2ascii() functions somewhere.
+ * For example current AS/400 have MD5 encoding support APIs but olders not....
+ */
+ 
+#include "jk_global.h"
+#include "jk_md5.h"
+
+char * JK_METHOD jk_hextocstr(unsigned char *org, char * dst, int n)
+{
+    char * os = dst;
+    unsigned char v;
+    static unsigned char zitohex[] = "0123456789ABCDEF";
+
+    while (--n >= 0) {
+    v = *org++;
+    *dst++ = zitohex[v >> 4];
+    *dst++ = zitohex[v&0x0f];
+    }
+    *dst = 0;
+
+    return (os);
+}
+
+#ifndef USE_APACHE_MD5 
+
+/* Constants for MD5Transform routine.
+ */
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform(JK_UINT4 state[4], const unsigned char block[64]);
+static void Encode(unsigned char *output, const JK_UINT4 *input, unsigned int len);
+static void Decode(JK_UINT4 *output, const unsigned char *input, unsigned int len);
+static void jk_MD5Init(JK_MD5_CTX *context);
+static void jk_MD5Update(JK_MD5_CTX *context, const unsigned char *input, unsigned int inputLen);
+/*static void jk_MD5Final(unsigned char digest[JK_MD5_DIGESTSIZE], JK_MD5_CTX *context);*/
+
+static unsigned char PADDING[64] =
+{
+    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+   Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (JK_UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (JK_UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (JK_UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (JK_UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+static void jk_MD5Init(JK_MD5_CTX *context)
+{
+    context->count[0] = context->count[1] = 0;
+    /* Load magic initialization constants. */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xefcdab89;
+    context->state[2] = 0x98badcfe;
+    context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+   operation, processing another message block, and updating the
+   context.
+ */
+static void jk_MD5Update(JK_MD5_CTX *context, const unsigned char *input,
+			      unsigned int inputLen)
+{
+    unsigned int i, idx, partLen;
+
+    /* Compute number of bytes mod 64 */
+    idx = (unsigned int) ((context->count[0] >> 3) & 0x3F);
+
+    /* Update number of bits */
+    if ((context->count[0] += ((JK_UINT4) inputLen << 3))
+	< ((JK_UINT4) inputLen << 3)) {
+	context->count[1]++;
+    }
+    context->count[1] += (JK_UINT4) inputLen >> 29;
+
+    partLen = 64 - idx;
+
+    /* Transform as many times as possible. */
+#ifndef CHARSET_EBCDIC
+    if (inputLen >= partLen) {
+	memcpy(&context->buffer[idx], input, partLen);
+	MD5Transform(context->state, context->buffer);
+
+	for (i = partLen; i + 63 < inputLen; i += 64) {
+	    MD5Transform(context->state, &input[i]);
+	}
+
+	idx = 0;
+    }
+    else {
+	i = 0;
+    }
+
+    /* Buffer remaining input */
+    memcpy(&context->buffer[idx], &input[i], inputLen - i);
+#else /*CHARSET_EBCDIC*/
+    if (inputLen >= partLen) {
+	ebcdic2ascii(&context->buffer[idx], input, partLen);
+	MD5Transform(context->state, context->buffer);
+
+	for (i = partLen; i + 63 < inputLen; i += 64) {
+	    unsigned char inp_tmp[64];
+	    ebcdic2ascii(inp_tmp, &input[i], 64);
+	    MD5Transform(context->state, inp_tmp);
+	}
+
+	idx = 0;
+    }
+    else {
+	i = 0;
+    }
+
+    /* Buffer remaining input */
+    ebcdic2ascii(&context->buffer[idx], &input[i], inputLen - i);
+#endif /*CHARSET_EBCDIC*/
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+   the message digest and zeroizing the context.
+ */
+void JK_METHOD jk_MD5Final(unsigned char digest[16], JK_MD5_CTX *context)
+{
+    unsigned char bits[8];
+    unsigned int idx, padLen;
+
+
+    /* Save number of bits */
+    Encode(bits, context->count, 8);
+
+#ifdef CHARSET_EBCDIC
+    /* XXX: @@@: In order to make this no more complex than necessary,
+     * this kludge converts the bits[] array using the ascii-to-ebcdic
+     * table, because the following jk_MD5Update() re-translates
+     * its input (ebcdic-to-ascii).
+     * Otherwise, we would have to pass a "conversion" flag to jk_MD5Update()
+     */
+    ascii2ebcdic(bits,bits,8);
+
+    /* Since everything is converted to ascii within jk_MD5Update(), 
+     * the initial 0x80 (PADDING[0]) must be stored as 0x20 
+     */
+    ascii2ebcdic(PADDING,PADDING,1);
+#endif /*CHARSET_EBCDIC*/
+
+    /* Pad out to 56 mod 64. */
+    idx = (unsigned int) ((context->count[0] >> 3) & 0x3f);
+    padLen = (idx < 56) ? (56 - idx) : (120 - idx);
+    jk_MD5Update(context, (const unsigned char *)PADDING, padLen);
+
+    /* Append length (before padding) */
+    jk_MD5Update(context, (const unsigned char *)bits, 8);
+
+    /* Store state in digest */
+    Encode(digest, context->state, 16);
+
+    /* Zeroize sensitive information. */
+    memset(context, 0, sizeof(*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block. */
+static void MD5Transform(JK_UINT4 state[4], const unsigned char block[64])
+{
+    JK_UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+    Decode(x, block, 64);
+
+    /* Round 1 */
+    FF(a, b, c, d, x[0], S11, 0xd76aa478);	/* 1 */
+    FF(d, a, b, c, x[1], S12, 0xe8c7b756);	/* 2 */
+    FF(c, d, a, b, x[2], S13, 0x242070db);	/* 3 */
+    FF(b, c, d, a, x[3], S14, 0xc1bdceee);	/* 4 */
+    FF(a, b, c, d, x[4], S11, 0xf57c0faf);	/* 5 */
+    FF(d, a, b, c, x[5], S12, 0x4787c62a);	/* 6 */
+    FF(c, d, a, b, x[6], S13, 0xa8304613);	/* 7 */
+    FF(b, c, d, a, x[7], S14, 0xfd469501);	/* 8 */
+    FF(a, b, c, d, x[8], S11, 0x698098d8);	/* 9 */
+    FF(d, a, b, c, x[9], S12, 0x8b44f7af);	/* 10 */
+    FF(c, d, a, b, x[10], S13, 0xffff5bb1);	/* 11 */
+    FF(b, c, d, a, x[11], S14, 0x895cd7be);	/* 12 */
+    FF(a, b, c, d, x[12], S11, 0x6b901122);	/* 13 */
+    FF(d, a, b, c, x[13], S12, 0xfd987193);	/* 14 */
+    FF(c, d, a, b, x[14], S13, 0xa679438e);	/* 15 */
+    FF(b, c, d, a, x[15], S14, 0x49b40821);	/* 16 */
+
+    /* Round 2 */
+    GG(a, b, c, d, x[1], S21, 0xf61e2562);	/* 17 */
+    GG(d, a, b, c, x[6], S22, 0xc040b340);	/* 18 */
+    GG(c, d, a, b, x[11], S23, 0x265e5a51);	/* 19 */
+    GG(b, c, d, a, x[0], S24, 0xe9b6c7aa);	/* 20 */
+    GG(a, b, c, d, x[5], S21, 0xd62f105d);	/* 21 */
+    GG(d, a, b, c, x[10], S22, 0x2441453);	/* 22 */
+    GG(c, d, a, b, x[15], S23, 0xd8a1e681);	/* 23 */
+    GG(b, c, d, a, x[4], S24, 0xe7d3fbc8);	/* 24 */
+    GG(a, b, c, d, x[9], S21, 0x21e1cde6);	/* 25 */
+    GG(d, a, b, c, x[14], S22, 0xc33707d6);	/* 26 */
+    GG(c, d, a, b, x[3], S23, 0xf4d50d87);	/* 27 */
+    GG(b, c, d, a, x[8], S24, 0x455a14ed);	/* 28 */
+    GG(a, b, c, d, x[13], S21, 0xa9e3e905);	/* 29 */
+    GG(d, a, b, c, x[2], S22, 0xfcefa3f8);	/* 30 */
+    GG(c, d, a, b, x[7], S23, 0x676f02d9);	/* 31 */
+    GG(b, c, d, a, x[12], S24, 0x8d2a4c8a);	/* 32 */
+
+    /* Round 3 */
+    HH(a, b, c, d, x[5], S31, 0xfffa3942);	/* 33 */
+    HH(d, a, b, c, x[8], S32, 0x8771f681);	/* 34 */
+    HH(c, d, a, b, x[11], S33, 0x6d9d6122);	/* 35 */
+    HH(b, c, d, a, x[14], S34, 0xfde5380c);	/* 36 */
+    HH(a, b, c, d, x[1], S31, 0xa4beea44);	/* 37 */
+    HH(d, a, b, c, x[4], S32, 0x4bdecfa9);	/* 38 */
+    HH(c, d, a, b, x[7], S33, 0xf6bb4b60);	/* 39 */
+    HH(b, c, d, a, x[10], S34, 0xbebfbc70);	/* 40 */
+    HH(a, b, c, d, x[13], S31, 0x289b7ec6);	/* 41 */
+    HH(d, a, b, c, x[0], S32, 0xeaa127fa);	/* 42 */
+    HH(c, d, a, b, x[3], S33, 0xd4ef3085);	/* 43 */
+    HH(b, c, d, a, x[6], S34, 0x4881d05);	/* 44 */
+    HH(a, b, c, d, x[9], S31, 0xd9d4d039);	/* 45 */
+    HH(d, a, b, c, x[12], S32, 0xe6db99e5);	/* 46 */
+    HH(c, d, a, b, x[15], S33, 0x1fa27cf8);	/* 47 */
+    HH(b, c, d, a, x[2], S34, 0xc4ac5665);	/* 48 */
+
+    /* Round 4 */
+    II(a, b, c, d, x[0], S41, 0xf4292244);	/* 49 */
+    II(d, a, b, c, x[7], S42, 0x432aff97);	/* 50 */
+    II(c, d, a, b, x[14], S43, 0xab9423a7);	/* 51 */
+    II(b, c, d, a, x[5], S44, 0xfc93a039);	/* 52 */
+    II(a, b, c, d, x[12], S41, 0x655b59c3);	/* 53 */
+    II(d, a, b, c, x[3], S42, 0x8f0ccc92);	/* 54 */
+    II(c, d, a, b, x[10], S43, 0xffeff47d);	/* 55 */
+    II(b, c, d, a, x[1], S44, 0x85845dd1);	/* 56 */
+    II(a, b, c, d, x[8], S41, 0x6fa87e4f);	/* 57 */
+    II(d, a, b, c, x[15], S42, 0xfe2ce6e0);	/* 58 */
+    II(c, d, a, b, x[6], S43, 0xa3014314);	/* 59 */
+    II(b, c, d, a, x[13], S44, 0x4e0811a1);	/* 60 */
+    II(a, b, c, d, x[4], S41, 0xf7537e82);	/* 61 */
+    II(d, a, b, c, x[11], S42, 0xbd3af235);	/* 62 */
+    II(c, d, a, b, x[2], S43, 0x2ad7d2bb);	/* 63 */
+    II(b, c, d, a, x[9], S44, 0xeb86d391);	/* 64 */
+
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+
+    /* Zeroize sensitive information. */
+    memset(x, 0, sizeof(x));
+}
+
+/* Encodes input (JK_UINT4) into output (unsigned char). Assumes len is
+   a multiple of 4.
+ */
+static void Encode(unsigned char *output, const JK_UINT4 *input, unsigned int len)
+{
+    unsigned int i, j;
+    JK_UINT4 k;
+
+    for (i = 0, j = 0; j < len; i++, j += 4) {
+	k = input[i];
+	output[j] = (unsigned char) (k & 0xff);
+	output[j + 1] = (unsigned char) ((k >> 8) & 0xff);
+	output[j + 2] = (unsigned char) ((k >> 16) & 0xff);
+	output[j + 3] = (unsigned char) ((k >> 24) & 0xff);
+    }
+}
+
+/* Decodes input (unsigned char) into output (JK_UINT4). Assumes len is
+ * a multiple of 4.
+ */
+static void Decode(JK_UINT4 *output, const unsigned char *input, unsigned int len)
+{
+    unsigned int i, j;
+
+    for (i = 0, j = 0; j < len; i++, j += 4)
+	output[i] = ((JK_UINT4) input[j]) | (((JK_UINT4) input[j + 1]) << 8) |
+	    (((JK_UINT4) input[j + 2]) << 16) | (((JK_UINT4) input[j + 3]) << 24);
+}
+
+/*
+ * The following MD5 password encryption code was largely borrowed from
+ * the FreeBSD 3.0 /usr/src/lib/libcrypt/crypt.c file, which is
+ * licenced as stated at the top of this file.
+ */
+static void jk_to64(char *s, unsigned long v, int n)
+{
+    static unsigned char itoa64[] =         /* 0 ... 63 => ASCII - 64 */
+	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+    while (--n >= 0) {
+	*s++ = itoa64[v&0x3f];
+	v >>= 6;
+    }
+}
+
+char * JK_METHOD jk_md5(const unsigned char *org, const unsigned char *org2, char *dst)
+{
+	JK_MD5_CTX  ctx;
+	char		buf[JK_MD5_DIGESTSIZE + 1];
+
+    jk_MD5Init(&ctx);
+    jk_MD5Update(&ctx, org, strlen((const char *)org));
+
+	if (org2 != NULL)
+		jk_MD5Update(&ctx, org2, strlen((const char *)org2));
+
+    jk_MD5Final((unsigned char *)buf, &ctx);
+	return (jk_hextocstr((unsigned char *)buf, dst, JK_MD5_DIGESTSIZE));
+}
+
+#else /* USE_APACHE_MD5 */
+
+#include "httpd.h"
+#include "http_config.h"
+
+#ifdef STANDARD20_MODULE_STUFF
+
+#include "apr_md5.h"
+#define  AP_MD5_CTX 	apr_md5_ctx_t
+#define  ap_MD5Init 	apr_md5_init
+#define  ap_MD5Update 	apr_md5_update
+#define  ap_MD5Final 	apr_md5_final
+
+#else /* STANDARD20_MODULE_STUFF */
+
+#include "ap_md5.h"
+
+#endif /* STANDARD20_MODULE_STUFF */
+
+char * JK_METHOD jk_md5(const unsigned char *org, const unsigned char *org2, char *dst)
+{
+	AP_MD5_CTX ctx;
+	char	   buf[JK_MD5_DIGESTSIZE + 1];
+
+    ap_MD5Init(&ctx);
+    ap_MD5Update(&ctx, org, strlen((const char *)org));
+
+	if (org2 != NULL)
+		ap_MD5Update(&ctx, org2, strlen((const char *)org2));
+
+    ap_MD5Final((unsigned char *)buf, &ctx);
+	return (jk_hextocstr((unsigned char *)buf, dst, JK_MD5_DIGESTSIZE));
+}
+
+#endif /* USE_APACHE_MD5 */
+
+/* Test values:
+ * ""                  D4 1D 8C D9 8F 00 B2 04  E9 80 09 98 EC F8 42 7E
+ * "a"                 0C C1 75 B9 C0 F1 B6 A8  31 C3 99 E2 69 77 26 61
+ * "abc                90 01 50 98 3C D2 4F B0  D6 96 3F 7D 28 E1 7F 72
+ * "message digest"    F9 6B 69 7D 7C B7 93 8D  52 5A 2F 31 AA F1 61 D0
+ *
+ */
+
+#ifdef TEST_JKMD5
+
+main(int argc, char ** argv)
+{
+	char xxx[(2 * JK_MD5_DIGESTSIZE) + 1];
+
+	if (argc > 1)
+		printf("%s => %s\n", argv[1], jk_md5(argv[1], NULL, xxx));
+}
+
+#endif 
diff --git a/connectors/jk/native/common/jk_md5.h b/connectors/jk/native/common/jk_md5.h
new file mode 100644
index 0000000..4038d75
--- /dev/null
+++ b/connectors/jk/native/common/jk_md5.h
@@ -0,0 +1,83 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/*
+ * This is work is derived from material Copyright RSA Data Security, Inc.
+ *
+ * The RSA copyright statement and Licence for that original material is
+ * included below. This is followed by the Apache copyright statement and
+ * licence for the modifications made to that material.
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+   rights reserved.
+
+   License to copy and use this software is granted provided that it
+   is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+   Algorithm" in all material mentioning or referencing this software
+   or this function.
+
+   License is also granted to make and use derivative works provided
+   that such works are identified as "derived from the RSA Data
+   Security, Inc. MD5 Message-Digest Algorithm" in all material
+   mentioning or referencing the derived work.
+
+   RSA Data Security, Inc. makes no representations concerning either
+   the merchantability of this software or the suitability of this
+   software for any particular purpose. It is provided "as is"
+   without express or implied warranty of any kind.
+
+   These notices must be retained in any copies of any part of this
+   documentation and/or software.
+*/
+
+#ifndef JK_APACHE_MD5_H
+#define JK_APACHE_MD5_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* MD5.H - header file for MD5.C */
+
+#define JK_MD5_DIGESTSIZE 16
+
+/* JK_UINT4 defines a four byte word */
+typedef unsigned int JK_UINT4;
+
+/* MD5 context. */
+typedef struct {
+    JK_UINT4 state[4];		/* state (ABCD) */
+    JK_UINT4 count[2];		/* number of bits, modulo 2^64 (lsb first) */
+    unsigned char buffer[64];	/* input buffer */
+} JK_MD5_CTX;
+
+/*
+ * Define the Magic String prefix that identifies a password as being
+ * hashed using our algorithm.
+ */
+#define JK_MD5PW_ID "$apr1$"
+#define JK_MD5PW_IDLEN 6
+
+char * JK_METHOD jk_hextocstr(unsigned char *org, char * dst, int n);
+char * JK_METHOD jk_md5(const unsigned char *org, const unsigned char *org2, char *dst);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* !JK_APACHE_MD5_H */
diff --git a/connectors/jk/native/common/jk_msg_buff.c b/connectors/jk/native/common/jk_msg_buff.c
new file mode 100644
index 0000000..1ffc5f6
--- /dev/null
+++ b/connectors/jk/native/common/jk_msg_buff.c
@@ -0,0 +1,453 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Data marshaling. XDR like                                  *
+ * Author:      Costin <costin@costin.dnt.ro>                              *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+#include "jk_pool.h"
+#include "jk_connect.h"
+#include "jk_util.h"
+#include "jk_sockbuf.h"
+#include "jk_msg_buff.h"
+#include "jk_logger.h"
+
+struct jk_msg_buf {
+    jk_pool_t *pool;
+
+    unsigned char *buf;
+    int pos; 
+    int len;
+    int maxlen;
+};
+
+
+/*
+ * Simple marshaling code.
+ */
+
+/* XXX what's above this line can go to .h XXX */
+void jk_b_dump(jk_msg_buf_t *msg, 
+               char *err) 
+{
+    int i=0;
+	printf("%s %d/%d/%d %x %x %x %x - %x %x %x %x - %x %x %x %x - %x %x %x %x\n", err, msg->pos, msg->len, msg->maxlen,  
+	       msg->buf[i++],msg->buf[i++],msg->buf[i++],msg->buf[i++],
+	       msg->buf[i++],msg->buf[i++],msg->buf[i++],msg->buf[i++],
+	       msg->buf[i++],msg->buf[i++],msg->buf[i++],msg->buf[i++],
+	       msg->buf[i++],msg->buf[i++],msg->buf[i++],msg->buf[i++]);
+
+	i = msg->pos - 4;
+    if(i < 0) {
+        i=0;
+    }
+	
+    printf("        %x %x %x %x - %x %x %x %x --- %x %x %x %x - %x %x %x %x\n", 
+	       msg->buf[i++],msg->buf[i++],msg->buf[i++],msg->buf[i++],
+	       msg->buf[i++],msg->buf[i++],msg->buf[i++],msg->buf[i++],
+	       msg->buf[i++],msg->buf[i++],msg->buf[i++],msg->buf[i++],
+	       msg->buf[i++],msg->buf[i++],msg->buf[i++],msg->buf[i++]);
+
+}
+
+void jk_b_reset(jk_msg_buf_t *msg) 
+{
+    msg->len = 4;
+    msg->pos = 4;
+}
+
+
+static void jk_b_set_long(jk_msg_buf_t *msg,
+                  int pos,
+                  unsigned long val)
+{   
+    msg->buf[pos]       = (unsigned char)((val >> 24) & 0xFF);
+    msg->buf[pos + 1]   = (unsigned char)((val >> 16) & 0xFF);
+    msg->buf[pos + 2]   = (unsigned char)((val >> 8) & 0xFF);
+    msg->buf[pos + 3]   = (unsigned char)(val & 0xFF);
+}
+
+
+int jk_b_append_long(jk_msg_buf_t *msg,
+                    unsigned long val)
+{
+    if(msg->len + 4 > msg->maxlen) {
+        return -1;
+    }
+
+    jk_b_set_long(msg, msg->len, val);
+
+    msg->len += 4;
+
+    return 0;
+}
+
+
+static void jk_b_set_int(jk_msg_buf_t *msg, 
+                  int pos, 
+                  unsigned short val) 
+{
+    msg->buf[pos]       = (unsigned char)((val >> 8) & 0xFF);
+    msg->buf[pos + 1]   = (unsigned char)(val & 0xFF);
+}
+
+
+int jk_b_append_int(jk_msg_buf_t *msg, 
+                    unsigned short val) 
+{
+    if(msg->len + 2 > msg->maxlen) {
+	    return -1;
+    }
+
+    jk_b_set_int(msg, msg->len, val);
+
+    msg->len += 2;
+
+    return 0;
+}
+
+
+static void jk_b_set_byte(jk_msg_buf_t *msg, 
+                   int pos, 
+                   unsigned char val) 
+{
+    msg->buf[pos]= val;
+}
+
+int jk_b_append_byte(jk_msg_buf_t *msg, 
+                     unsigned char val)
+{
+    if(msg->len + 1 > msg->maxlen) {
+	    return -1;
+    }
+
+    jk_b_set_byte(msg, msg->len, val);
+
+    msg->len += 1;
+
+    return 0;
+}
+
+
+void jk_b_end(jk_msg_buf_t *msg, int protoh) 
+{
+    /* 
+     * Ugly way to set the size in the right position 
+     */
+    jk_b_set_int(msg, 2, (unsigned short )(msg->len - 4)); /* see protocol */
+    jk_b_set_int(msg, 0, (unsigned short) protoh);
+}
+
+
+jk_msg_buf_t *jk_b_new(jk_pool_t *p) 
+{
+    jk_msg_buf_t *msg = 
+            (jk_msg_buf_t *)jk_pool_alloc(p, sizeof(jk_msg_buf_t));
+
+    if(!msg) {
+        return NULL;
+    }
+
+    msg->pool = p;
+    
+    return msg;
+}
+
+int jk_b_set_buffer(jk_msg_buf_t *msg, 
+                    char *data, 
+                    int buffSize) 
+{
+    if(!msg) {
+        return -1;
+    }
+
+    msg->len = 0;
+    msg->buf = (unsigned char *)data;
+    msg->maxlen = buffSize;
+    
+    return 0;
+}
+
+
+int jk_b_set_buffer_size(jk_msg_buf_t *msg, 
+                         int buffSize) 
+{
+    unsigned char *data = (unsigned char *)jk_pool_alloc(msg->pool, buffSize);
+    
+    if(!data) {
+	    return -1;
+    }
+
+    jk_b_set_buffer(msg, (char *)data, buffSize);
+    return 0;
+}
+
+unsigned char *jk_b_get_buff(jk_msg_buf_t *msg) 
+{
+    return msg->buf;
+}
+
+unsigned int jk_b_get_pos(jk_msg_buf_t *msg) 
+{
+    return msg->pos;
+}
+
+void jk_b_set_pos(jk_msg_buf_t *msg,
+                          int pos) 
+{
+    msg->pos = pos;
+}
+
+unsigned int jk_b_get_len(jk_msg_buf_t *msg) 
+{
+    return msg->len;
+}
+
+void jk_b_set_len(jk_msg_buf_t *msg, 
+                  int len) 
+{
+    msg->len=len;
+}
+
+int jk_b_get_size(jk_msg_buf_t *msg) 
+{
+    return msg->maxlen;
+}
+
+#ifdef AS400
+int jk_b_append_asciistring(jk_msg_buf_t *msg, 
+                       const char *param) 
+{
+    int len;
+
+    if(!param) {
+	    jk_b_append_int( msg, 0xFFFF );
+	    return 0; 
+    }
+
+    len = strlen(param);
+    if(msg->len + len + 2  > msg->maxlen) {
+	    return -1;
+    }
+
+    /* ignore error - we checked once */
+    jk_b_append_int(msg, (unsigned short )len);
+
+    /* We checked for space !!  */
+    strncpy((char *)msg->buf + msg->len , param, len+1);    /* including \0 */
+    msg->len += len + 1;
+
+    return 0;
+}
+#endif
+
+int jk_b_append_string(jk_msg_buf_t *msg, 
+                       const char *param) 
+{
+    int len;
+
+    if(!param) {
+	    jk_b_append_int( msg, 0xFFFF );
+	    return 0; 
+    }
+
+    len = strlen(param);
+    if(msg->len + len + 2  > msg->maxlen) {
+	    return -1;
+    }
+
+    /* ignore error - we checked once */
+    jk_b_append_int(msg, (unsigned short )len);
+
+    /* We checked for space !!  */
+    strncpy((char *)msg->buf + msg->len , param, len+1);    /* including \0 */
+#if defined(AS400) || defined(_OSD_POSIX)
+    /* convert from EBCDIC if needed */
+    jk_xlate_to_ascii((char *)msg->buf + msg->len, len+1);
+#endif
+    msg->len += len + 1;
+
+    return 0;
+}
+
+
+int jk_b_append_bytes(jk_msg_buf_t         *msg,
+                      const unsigned char  *param,
+					  int                   len)
+{
+    if (! len) {
+        return 0;
+    }
+
+    if (msg->len + len > msg->maxlen) {
+        return -1;
+    }
+
+    /* We checked for space !!  */
+    memcpy((char *)msg->buf + msg->len, param, len); 
+    msg->len += len;
+
+    return 0;
+}
+
+unsigned long jk_b_get_long(jk_msg_buf_t *msg)
+{
+    unsigned long i;
+    if(msg->pos + 3 > msg->len) {
+        printf( "Read after end \n");
+        return -1;
+    }
+    i  = ((msg->buf[(msg->pos++)] & 0xFF)<<24);
+    i |= ((msg->buf[(msg->pos++)] & 0xFF)<<16);
+    i |= ((msg->buf[(msg->pos++)] & 0xFF)<<8);
+    i |= ((msg->buf[(msg->pos++)] & 0xFF));
+    return i;
+}
+
+unsigned long jk_b_pget_long(jk_msg_buf_t *msg,
+                             int pos)
+{
+    unsigned long i;
+    i  = ((msg->buf[(pos++)] & 0xFF)<<24);
+    i |= ((msg->buf[(pos++)] & 0xFF)<<16);
+    i |= ((msg->buf[(pos++)] & 0xFF)<<8);
+    i |= ((msg->buf[(pos  )] & 0xFF));
+    return i;
+}
+
+
+unsigned short jk_b_get_int(jk_msg_buf_t *msg) 
+{
+    int i;
+    if(msg->pos + 1 > msg->len) {
+	    printf( "Read after end \n");
+	    return -1;
+    }
+    i  = ((msg->buf[(msg->pos++)] & 0xFF)<<8);
+    i += ((msg->buf[(msg->pos++)] & 0xFF));
+    return i;
+}
+
+unsigned short jk_b_pget_int(jk_msg_buf_t *msg, 
+                             int pos) 
+{
+    int i;
+	i  = ((msg->buf[pos++] & 0xFF)<<8);
+    i += ((msg->buf[pos]   & 0xFF));
+    return i;
+}
+
+unsigned char jk_b_get_byte(jk_msg_buf_t *msg) 
+{
+    unsigned char rc;
+    if(msg->pos > msg->len) {
+	    printf("Read after end \n");
+	    return -1;
+    }
+    rc = msg->buf[msg->pos++];
+    
+    return rc;
+}
+
+unsigned char jk_b_pget_byte(jk_msg_buf_t *msg, 
+                             int pos) 
+{
+    return msg->buf[pos];
+}
+
+
+unsigned char *jk_b_get_string(jk_msg_buf_t *msg) 
+{
+    int size = jk_b_get_int(msg);
+    int start = msg->pos;
+
+    if((size < 0 ) || (size + start > msg->maxlen)) { 
+	    jk_b_dump(msg, "After get int"); 
+	    printf("ERROR\n" );
+	    return (unsigned char *)"ERROR"; /* XXX */
+    }
+
+    msg->pos += size;
+    msg->pos++;  /* terminating NULL */
+    
+    return (unsigned char *)(msg->buf + start); 
+}
+
+int jk_b_get_bytes(jk_msg_buf_t *msg, unsigned char * buf, int len)
+{
+    int start = msg->pos;
+
+    if((len < 0 ) || (len + start > msg->maxlen)) {
+        jk_b_dump(msg, "After get bytes");
+        printf("ERROR\n" ); 
+        return (-1);
+    }
+    
+	memcpy(buf, msg->buf + start, len);
+    msg->pos += len;
+	return (len);
+}
+
+
+/** Shame-less copy from somewhere.
+    assert (src != dst)
+ */
+static void swap_16(unsigned char *src, unsigned char *dst) 
+{
+    *dst++ = *(src + 1 );
+    *dst= *src;
+}
+
+/** Helpie dump function 
+ */
+void jk_dump_buff(jk_logger_t *l,
+                      const char *file,
+                      int line,
+                      int level,
+                      char * what,
+                      jk_msg_buf_t * msg)
+{
+#ifdef USE_ALSO_BODY
+        jk_log(l, file, line, level, "%s #%d %.*s\n",
+                  what,
+                  jk_b_get_len(msg),
+                  jk_b_get_len(msg),
+                  jk_b_get_buff(msg));
+#else
+jk_log(l, file, line, level, "%s #%d\n", what, jk_b_get_len(msg));
+#endif
+}
+
+
+int jk_b_copy(jk_msg_buf_t *smsg,
+              jk_msg_buf_t *dmsg)
+{
+	if (smsg == NULL || dmsg == NULL)
+		return (-1);
+
+	if (dmsg->maxlen < smsg->len)
+		return (-2);
+
+	memcpy(dmsg->buf, smsg->buf, smsg->len);
+	dmsg->len = smsg->len;
+
+	return (smsg->len);
+}
+
diff --git a/connectors/jk/native/common/jk_msg_buff.h b/connectors/jk/native/common/jk_msg_buff.h
new file mode 100644
index 0000000..0e73d5c
--- /dev/null
+++ b/connectors/jk/native/common/jk_msg_buff.h
@@ -0,0 +1,198 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Data marshaling. XDR like                                  *
+ * Author:      Costin <costin@costin.dnt.ro>                              *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef JK_MSG_BUF_H
+#define JK_MSG_BUF_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define DEF_BUFFER_SZ (8 * 1024)
+
+/* XXX replace all return values with error codes */
+#define ERR_BAD_PACKET -5
+
+/*
+RPC details:
+
+  - one parameter  - use a structure for more. The method
+    is encoded as part of the request
+  - one or no result
+  - 
+
+
+
+ */
+
+struct jk_msg_buf;
+typedef struct jk_msg_buf jk_msg_buf_t;
+
+/* -------------------- Setup routines -------------------- */
+
+/** Allocate a buffer.
+ */
+jk_msg_buf_t *jk_b_new(jk_pool_t *p); 
+
+/** Set up a buffer with an existing buffer
+ */
+int jk_b_set_buffer(jk_msg_buf_t *msg, 
+                    char *data, 
+                    int buffSize );
+
+/*
+ * Set up a buffer with a new buffer of buffSize
+ */
+int jk_b_set_buffer_size(jk_msg_buf_t *msg, 
+                         int buffSize);
+
+/*
+ * Finalize the buffer before sending - set length fields, etc
+ */
+void jk_b_end(jk_msg_buf_t *msg,
+			  int protoh);
+
+/*
+ * Recycle the buffer - z for a new invocation 
+ */
+void jk_b_reset(jk_msg_buf_t *msg );
+
+/*
+ * Return the buffer body 
+ */ 
+unsigned char *jk_b_get_buff(jk_msg_buf_t *msg);
+
+/* 
+ * Return the current reading position
+ */
+unsigned int jk_b_get_pos(jk_msg_buf_t *msg);
+
+/*
+ * Buffer size 
+ */
+int jk_b_get_size(jk_msg_buf_t *msg);
+
+void jk_b_set_len(jk_msg_buf_t *msg, 
+                  int len);
+
+void jk_b_set_pos(jk_msg_buf_t *msg, 
+                  int pos);
+
+/*
+ * Get the  message length for incomming buffers
+ *   or the current length for outgoing
+ */
+unsigned int jk_b_get_len(jk_msg_buf_t *msg);
+
+/*
+ * Dump the buffer header
+ *   @param err Message text
+ */
+void jk_b_dump(jk_msg_buf_t *msg, 
+               char *err); 
+
+/* -------------------- Real encoding -------------------- */
+
+int jk_b_append_byte(jk_msg_buf_t *msg, 
+                     unsigned char val);
+
+int jk_b_append_bytes(jk_msg_buf_t *        msg, 
+                      const unsigned char * param,
+					  int                   len);
+
+int jk_b_append_int(jk_msg_buf_t *msg, 
+                    unsigned short val);
+
+int jk_b_append_long(jk_msg_buf_t *msg,
+                     unsigned long val);
+
+int jk_b_append_string(jk_msg_buf_t *msg, 
+                       const char *param);
+
+#ifdef AS400
+int jk_b_append_asciistring(jk_msg_buf_t *msg, 
+                       const char *param);
+#endif
+
+int jk_b_append_bytes(jk_msg_buf_t         *msg,
+                      const unsigned char  *param,
+                      int                   len);
+
+/* -------------------- Decoding -------------------- */
+
+/** Get a byte from the current position 
+ */
+unsigned char jk_b_get_byte(jk_msg_buf_t *msg);
+
+/** Get an int from the current position
+ */
+unsigned short jk_b_get_int(jk_msg_buf_t *msg);
+
+/** Get a long from the current position
+ */
+unsigned long jk_b_get_long(jk_msg_buf_t *msg);
+
+/** Get a String from the current position
+ */
+unsigned char *jk_b_get_string(jk_msg_buf_t *msg);
+
+/** Get Bytes from the current position
+ */
+int jk_b_get_bytes(jk_msg_buf_t *msg, 
+				   unsigned char * buf, 
+				   int len);
+
+/** Get a byte from an arbitrary position
+ */
+unsigned char jk_b_pget_byte(jk_msg_buf_t *msg,
+                             int pos);
+
+/** Get an int from an arbitrary position 
+ */
+unsigned short jk_b_pget_int(jk_msg_buf_t *msg, 
+                             int pos);
+
+/** Get a long from an arbitrary position 
+ */
+unsigned long jk_b_pget_long(jk_msg_buf_t *msg, 
+                             int pos);
+
+/* --------------------- Help ------------------------ */
+void jk_dump_buff(jk_logger_t *l, 
+                      const char *file,
+                      int line,
+                      int level,
+                      char * what,
+                      jk_msg_buf_t * msg);
+
+/** Copy a msg buf into another one
+  */
+int jk_b_copy(jk_msg_buf_t *smsg,
+              jk_msg_buf_t *dmsg);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_MSG_BUF_H */
diff --git a/connectors/jk/native/common/jk_mt.h b/connectors/jk/native/common/jk_mt.h
new file mode 100644
index 0000000..17b4a1e
--- /dev/null
+++ b/connectors/jk/native/common/jk_mt.h
@@ -0,0 +1,80 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Multi thread portability code for JK                       *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef _JK_MT_H
+#define _JK_MT_H
+
+#include "jk_global.h"
+
+/*
+ * All WIN32 code is MT, UNIX code that uses pthreads is marked by the POSIX 
+ * _REENTRANT define.
+ */
+#if defined (WIN32) || defined(_REENTRANT)
+
+    /*
+     * Marks execution under MT compilation
+     */
+    #define _MT_CODE
+
+    #ifdef WIN32
+
+        #include <windows.h>
+
+        typedef CRITICAL_SECTION JK_CRIT_SEC;
+
+        #define JK_INIT_CS(x, rc) InitializeCriticalSection(x); rc = JK_TRUE;
+        #define JK_DELETE_CS(x, rc) DeleteCriticalSection(x); rc = JK_TRUE;
+        #define JK_ENTER_CS(x, rc) EnterCriticalSection(x); rc = JK_TRUE;
+        #define JK_LEAVE_CS(x, rc) LeaveCriticalSection(x); rc = JK_TRUE;
+
+    #else /* Unix pthreads */
+
+        #include <pthread.h>
+
+        typedef pthread_mutex_t	JK_CRIT_SEC;
+
+        #define JK_INIT_CS(x, rc)\
+            if(pthread_mutex_init(x, NULL)) rc = JK_FALSE; else rc = JK_TRUE; 
+
+        #define JK_DELETE_CS(x, rc)\
+            if(pthread_mutex_destroy(x)) rc = JK_FALSE; else rc = JK_TRUE; 
+
+        #define JK_ENTER_CS(x, rc)\
+            if(pthread_mutex_lock(x)) rc = JK_FALSE; else rc = JK_TRUE; 
+
+        #define JK_LEAVE_CS(x, rc)\
+            if(pthread_mutex_unlock(x)) rc = JK_FALSE; else rc = JK_TRUE; 
+    #endif /* Unix pthreads */
+
+#else /* Not an MT code */
+
+    typedef void *JK_CRIT_SEC;
+
+    #define JK_INIT_CS(x, rc) rc = JK_TRUE;
+    #define JK_DELETE_CS(x, rc) rc = JK_TRUE;
+    #define JK_ENTER_CS(x, rc) rc = JK_TRUE;
+    #define JK_LEAVE_CS(x, rc) rc = JK_TRUE;
+
+#endif /* Not an MT code */
+
+#endif /* _JK_MT_H */
diff --git a/connectors/jk/native/common/jk_nwmain.c b/connectors/jk/native/common/jk_nwmain.c
new file mode 100644
index 0000000..c8d2ea7
--- /dev/null
+++ b/connectors/jk/native/common/jk_nwmain.c
@@ -0,0 +1,108 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Netware Wrapper                                            *
+ * Author:      Mike Anderson <mmander@novell.com>                         *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifdef NETWARE
+/*
+ * NATIVE_MAIN
+ */
+
+/*
+ * INCLUDES
+ */
+
+#include <stdio.h>
+
+/* Apache 2/APR uses NOVELL_LIBC which has a different way of handling
+ * "library" nlms.  If we aren't on LIBC, use the old method
+ */
+
+#ifndef __NOVELL_LIBC__
+#include <nwthread.h>
+#include <netdb.h>
+
+NETDB_DEFINE_CONTEXT
+
+/*
+ * main ()
+ *
+ * Main entry point -- don't do much more than I've provided
+ *
+ * Entry:
+ *
+ * Exit:
+ *    Nothing
+ */
+
+void main ()
+{
+   ExitThread (TSR_THREAD, 0);
+}
+#else /* __NOVELL_LIBC__ */
+
+/* Since we are on LibC, we need to handle our own startup and shutdown */
+
+#include <netware.h>
+#include "novsock2.h"
+
+int _NonAppStart
+(
+    void        *NLMHandle,
+    void        *errorScreen,
+    const char  *cmdLine,
+    const char  *loadDirPath,
+    size_t      uninitializedDataLength,
+    void        *NLMFileHandle,
+    int         (*readRoutineP)( int conn, void *fileHandle, size_t offset,
+                    size_t nbytes, size_t *bytesRead, void *buffer ),
+    size_t      customDataOffset,
+    size_t      customDataSize,
+    int         messageCount,
+    const char  **messages
+)
+{
+#pragma unused(cmdLine)
+#pragma unused(loadDirPath)
+#pragma unused(uninitializedDataLength)
+#pragma unused(NLMFileHandle)
+#pragma unused(readRoutineP)
+#pragma unused(customDataOffset)
+#pragma unused(customDataSize)
+#pragma unused(messageCount)
+#pragma unused(messages)
+
+    WSADATA wsaData;
+    
+    return WSAStartup((WORD) MAKEWORD(2, 0), &wsaData);
+}
+
+void _NonAppStop( void )
+{
+    WSACleanup();
+}
+
+int  _NonAppCheckUnload( void )
+{
+	return 0;
+}
+#endif /* __NOVELL_LIBC__ */
+
+#endif
diff --git a/connectors/jk/native/common/jk_pool.c b/connectors/jk/native/common/jk_pool.c
new file mode 100644
index 0000000..9ff0884
--- /dev/null
+++ b/connectors/jk/native/common/jk_pool.c
@@ -0,0 +1,180 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Simple memory pool                                         *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#include "jk_pool.h"
+
+#define DEFAULT_DYNAMIC 10
+
+
+static void *jk_pool_dyn_alloc(jk_pool_t *p, 
+                               size_t size);
+
+
+void jk_open_pool(jk_pool_t *p,
+                  jk_pool_atom_t *buf,
+                  unsigned size)
+{
+    p->pos  = 0;
+    p->size = size;
+    p->buf  = (char *)buf;
+
+    p->dyn_pos = 0;
+    p->dynamic = NULL;
+    p->dyn_size = 0;
+}
+
+void jk_close_pool(jk_pool_t *p)
+{
+    if(p) {
+        jk_reset_pool(p);
+        if(p->dynamic) {
+            free(p->dynamic);
+        }
+    }
+}
+
+void jk_reset_pool(jk_pool_t *p)
+{
+    if(p && p->dyn_pos && p->dynamic) {
+        unsigned i;
+        for(i = 0 ; i < p->dyn_pos ; i++) {
+            if(p->dynamic[i]) {
+                free(p->dynamic[i]);
+            }
+        }
+    }
+
+    p->dyn_pos  = 0;
+    p->pos      = 0;
+}
+
+void *jk_pool_alloc(jk_pool_t *p, 
+                    size_t size)
+{
+    void *rc = NULL;
+
+    if(p && size > 0) {
+        /* Round size to the upper mult of 8 (or 16 on iSeries) */
+	size--;
+#ifdef AS400
+        size /= 16;
+        size = (size + 1) * 16;
+#else
+        size /= 8;
+        size = (size + 1) * 8;
+#endif
+        if((p->size - p->pos) >= size) {
+            rc = &(p->buf[p->pos]);
+            p->pos += size;
+        } else {
+            rc = jk_pool_dyn_alloc(p, size);
+        }
+    }
+
+    return rc;
+}
+
+void *jk_pool_realloc(jk_pool_t *p, 
+                      size_t sz,
+                      const void *old,
+                      size_t old_sz)
+{
+    void *rc;
+
+    if(!p || (!old && old_sz)) {
+        return NULL;
+    }
+
+    rc = jk_pool_alloc(p, sz);
+    if(rc) {
+        memcpy(rc, old, old_sz);
+    }
+
+    return rc;
+}
+
+void *jk_pool_strdup(jk_pool_t *p, 
+                     const char *s)
+{
+    char *rc = NULL;
+    if(s && p) {
+        size_t size = strlen(s);
+    
+        if(!size)  {
+            return "";
+        }
+
+        size++;
+        rc = jk_pool_alloc(p, size);
+        if(rc) {
+            memcpy(rc, s, size);
+        }
+    }
+
+    return rc;
+}
+
+void jk_dump_pool(jk_pool_t *p, 
+                  FILE *f)
+{
+    fprintf(f, "Dumping for pool [%x]\n", p);
+    fprintf(f, "size             [%d]\n", p->size);
+    fprintf(f, "pos              [%d]\n", p->pos);
+    fprintf(f, "buf              [%x]\n", p->buf);  
+    fprintf(f, "dyn_size         [%d]\n", p->dyn_size);
+    fprintf(f, "dyn_pos          [%d]\n", p->dyn_pos);
+    fprintf(f, "dynamic          [%x]\n", p->dynamic);
+
+    fflush(f);
+}
+
+static void *jk_pool_dyn_alloc(jk_pool_t *p, 
+                               size_t size)
+{
+    void *rc = NULL;
+
+    if(p->dyn_size == p->dyn_pos) {
+        unsigned new_dyn_size = p->dyn_size + DEFAULT_DYNAMIC;
+        void **new_dynamic = (void **)malloc(new_dyn_size * sizeof(void *));
+        if(new_dynamic) {
+            if(p->dynamic) {
+                memcpy(new_dynamic, 
+                       p->dynamic, 
+                       p->dyn_size * sizeof(void *));
+
+                free(p->dynamic);
+            }
+
+            p->dynamic = new_dynamic;
+            p->dyn_size = new_dyn_size;
+        } else {
+            return NULL;
+        }
+    } 
+
+    rc = p->dynamic[p->dyn_pos] = malloc(size);
+    if(p->dynamic[p->dyn_pos]) {
+        p->dyn_pos ++;
+    }
+
+    return rc;
+}
diff --git a/connectors/jk/native/common/jk_pool.h b/connectors/jk/native/common/jk_pool.h
new file mode 100644
index 0000000..468a5a3
--- /dev/null
+++ b/connectors/jk/native/common/jk_pool.h
@@ -0,0 +1,116 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Memory Pool object header file                             *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+#ifndef _JK_POOL_H
+#define _JK_POOL_H
+
+#include "jk_global.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @file jk_pool.h
+ * @brief Jk memory allocation
+ *
+ * Similar with apr_pools, but completely unsynchronized.
+ * XXX use same names
+ * 
+ */
+
+/*
+ * The pool atom (basic pool alocation unit) is an 8 byte long. 
+ * Each allocation (even for 1 byte) will return a round up to the 
+ * number of atoms. 
+ * 
+ * This is to help in alignment of 32/64 bit machines ...
+ * G.S
+ */
+#ifdef WIN32
+    typedef __int64     jk_pool_atom_t;
+#elif defined(AIX)
+    typedef long long   jk_pool_atom_t;
+#elif defined(SOLARIS)
+    typedef long long   jk_pool_atom_t;
+#elif defined(LINUX)
+    typedef long long   jk_pool_atom_t;
+#elif defined(FREEBSD)
+    typedef long long   jk_pool_atom_t;
+#elif defined(OS2)
+    typedef long long   jk_pool_atom_t;
+#elif defined(NETWARE)
+    typedef long long   jk_pool_atom_t;
+#elif defined(HPUX11)
+    typedef long long   jk_pool_atom_t;
+#elif defined(IRIX)
+    typedef long long   jk_pool_atom_t;
+#elif defined(AS400)
+    typedef void *	jk_pool_atom_t;
+#else
+    typedef long long   jk_pool_atom_t;
+#endif
+
+/* 
+ * Pool size in number of pool atoms.
+ */
+#define TINY_POOL_SIZE 256                  /* Tiny 1/4K atom pool. */
+#define SMALL_POOL_SIZE 512                 /* Small 1/2K atom pool. */
+#define BIG_POOL_SIZE   2*SMALL_POOL_SIZE   /* Bigger 1K atom pool. */
+#define HUGE_POOL_SIZE  2*BIG_POOL_SIZE     /* Huge 2K atom pool. */
+
+  /** XXX Move it to impl, make it incomplete 
+   */
+struct jk_pool {
+    unsigned size;      
+    unsigned pos;       
+    char     *buf;      
+    unsigned dyn_size;  
+    unsigned dyn_pos;   
+    void     **dynamic; 
+};
+
+typedef struct jk_pool jk_pool_t;
+
+void jk_open_pool(jk_pool_t *p,
+                  jk_pool_atom_t *buf,
+                  unsigned size);
+
+void jk_close_pool(jk_pool_t *p);
+
+void jk_reset_pool(jk_pool_t *p);
+
+void *jk_pool_alloc(jk_pool_t *p, 
+                    size_t sz);
+
+void *jk_pool_realloc(jk_pool_t *p, 
+                      size_t sz,
+                      const void *old,
+                      size_t old_sz);
+
+void *jk_pool_strdup(jk_pool_t *p, 
+                     const char *s);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _JK_POOL_H */
diff --git a/connectors/jk/native/common/jk_service.h b/connectors/jk/native/common/jk_service.h
new file mode 100644
index 0000000..e8ea471
--- /dev/null
+++ b/connectors/jk/native/common/jk_service.h
@@ -0,0 +1,414 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Definitions of the objects used during the service step.   *
+ *              These are the web server (ws) the worker and the connection*
+ *              JVM connection point                                       *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Dan Milstein <danmil@shore.net>                            *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+#ifndef JK_SERVICE_H
+#define JK_SERVICE_H
+
+#include "jk_map.h"
+#include "jk_global.h"
+#include "jk_logger.h"
+#include "jk_pool.h"
+#include "jk_uri_worker_map.h"
+#include "jk_msg_buff.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+    
+/*
+ * Env Information to be provided to worker at init time
+ * With AJP14 support we need to have access to many informations
+ * about web-server, uri to worker map....
+ */
+
+struct jk_worker_env {
+
+    /* The URI to WORKER map, will be feeded by AJP14 autoconf feature */
+    jk_uri_worker_map_t *uri_to_worker;
+    
+    int num_of_workers;
+    char *first_worker;
+
+    /* Web-Server we're running on (Apache/IIS/NES) */
+    char          		*server_name;
+    
+    /* Virtual server handled - "*" is all virtual */
+    char                *virtual;   
+};
+typedef struct jk_worker_env jk_worker_env_t;
+
+struct jk_ws_service;
+struct jk_endpoint;
+struct jk_worker;
+typedef struct jk_ws_service jk_ws_service_t;
+typedef struct jk_endpoint   jk_endpoint_t;
+typedef struct jk_worker     jk_worker_t;
+
+/*
+ * The web server service 'class'.  An instance of this class is created
+ * for each request which is forwarded from the web server to the servlet
+ * container.  Contains the basic information about the request
+ * (e.g. protocol, req_uri, etc), and also contains a series of methods
+ * which provide access to core web server functionality (start_response,
+ * read, write).  This class might be more accurately called ws_request.
+ *
+ * As with all the core jk classes, this is essentially an abstract base
+ * class which is implemented/extended by classes which are specific to a
+ * particular web server.  By using an abstract base class in this manner,
+ * workers can be written for different protocols (e.g. ajp12, ajp13, ajp14)
+ * without the workers having to worry about which web server they are
+ * talking to.
+ *
+ * This particular OO-in-C system uses a 'ws_private' pointer to point to
+ * the platform-specific data.  So in the subclasses, the methods do most
+ * of their work by getting their hands on the ws_private pointer and then
+ * using that to get at the correctly formatted data and functions for
+ * their platform.
+ *
+ * Try imagining this as a 'public abstract class', and the ws_private
+ * pointer as a sort of extra 'this' reference.  Or imagine that you are
+ * seeing the internal vtables of your favorite OO language.  Whatever
+ * works for you.
+ *
+ * See apache1.3/mod_jk.c and iis/jk_isapi_plugin.c for examples.  
+ */
+struct jk_ws_service {
+  
+    /* 
+     * A 'this' pointer which is used by the subclasses of this class to
+     * point to data which is specific to a given web server platform
+     * (e.g. Apache or IIS).  
+     */
+    void *ws_private;
+    
+    /*
+     * Provides memory management.  All data specific to this request is
+     * allocated within this pool, which can then be reclaimed at the end
+     * of the request handling cycle. 
+     *
+     * Alive as long as the request is alive.  
+     */
+    jk_pool_t *pool;
+
+    /* 
+     * CGI Environment needed by servlets
+     */
+    char    *method;        
+    char    *protocol;      
+    char    *req_uri;       
+    char    *remote_addr;   
+    char    *remote_host;   
+    char    *remote_user;   
+    char    *auth_type;     
+    char    *query_string;  
+    char    *server_name;   
+    unsigned server_port;   
+    char    *server_software;
+    unsigned content_length;    /* integer that represents the content  */
+                                /* length should be 0 if unknown.        */
+    unsigned is_chunked;        /* 1 if content length is unknown (chunked rq) */
+    unsigned no_more_chunks;    /* 1 if last chunk has been read */
+    unsigned content_read;      /* number of bytes read */
+
+    /*
+     * SSL information
+     *
+     * is_ssl       - True if request is in ssl connection
+     * ssl_cert     - If available, base64 ASN.1 encoded client certificates.
+     * ssl_cert_len - Length of ssl_cert, 0 if certificates are not available.
+     * ssl_cipher   - The ssl cipher suite in use.
+     * ssl_session  - The ssl session string
+     *
+     * In some servers it is impossible to extract all this information, in this 
+     * case, we are passing NULL.
+     */
+    int      is_ssl;
+    char     *ssl_cert;
+    unsigned ssl_cert_len;
+    char     *ssl_cipher;
+    char     *ssl_session;
+
+	/*
+	 * SSL extra information for Servlet 2.3 API
+	 * 
+	 * ssl_key_size - ssl key size in use
+	 */
+	int		ssl_key_size;
+	
+    /*
+     * Headers, names and values.
+     */
+    char    **headers_names;    /* Names of the request headers  */
+    char    **headers_values;   /* Values of the request headers */
+    unsigned num_headers;       /* Number of request headers     */
+
+
+    /*
+     * Request attributes. 
+     *
+     * These attributes that were extracted from the web server and are 
+     * sent to Tomcat.
+     *
+     * The developer should be able to read them from the ServletRequest
+     * attributes. Tomcat is required to append org.apache.tomcat. to 
+     * these attrinbute names.
+     */
+    char    **attributes_names;    /* Names of the request attributes  */
+    char    **attributes_values;   /* Values of the request attributes */
+    unsigned num_attributes;       /* Number of request attributes     */
+
+    /*
+     * The jvm route is in use when the adapter load balance among
+     * several JVMs. It is the ID of a specific JVM in the load balance
+     * group. We are using this variable to implement JVM session 
+     * affinity
+     */
+    char    *jvm_route;
+
+    /* Temp solution for auth. For native1 it'll be sent on each request,
+       if an option is present. For native2 it'll be sent with the first
+       request. On java side, both cases will work. For tomcat3.2 or
+       a version that doesn't support secret - don't set the secret,
+       and it'll work.
+     */
+    char    *secret;        
+    /*
+     * Callbacks into the web server.  For each, the first argument is
+     * essentially a 'this' pointer.  All return JK_TRUE on success
+     * and JK_FALSE on failure.
+     */
+
+    /*
+     * Area to get POST data for fail-over recovery in POST
+     */
+    jk_msg_buf_t    *reco_buf; 
+    int		    reco_status;
+
+    /*
+     * Send the response headers to the browser.
+     */
+    int (JK_METHOD *start_response)(jk_ws_service_t *s,
+                                    int status,
+                                    const char *reason,
+                                    const char * const *header_names,
+                                    const char * const *header_values,
+                                    unsigned num_of_headers);
+
+    /*
+     * Read a chunk of the request body into a buffer.  Attempt to read len
+     * bytes into the buffer.  Write the number of bytes actually read into
+     * actually_read.  
+     */
+    int (JK_METHOD *read)(jk_ws_service_t *s,
+                          void *buffer,
+                          unsigned len,
+                          unsigned *actually_read);
+
+    /*
+     * Write a chunk of response data back to the browser.
+     */
+    int (JK_METHOD *write)(jk_ws_service_t *s,
+                           const void *buffer,
+                           unsigned len);
+};
+
+/*
+ * The endpoint 'class', which represents one end of a connection to the
+ * servlet engine.  Basically, supports nothing other than forwarding the
+ * request to the servlet engine.  Endpoints can be persistent (as with
+ * ajp13/ajp14, where a single connection is reused many times), or can last for a
+ * single request (as with ajp12, where a new connection is created for
+ * every request).
+ *
+ * An endpoint for a given protocol is obtained by the web server plugin
+ * from a worker object for that protocol.  See below for details.
+ *
+ * As with all the core jk classes, this is essentially an abstract base
+ * class which is implemented/extended by classes which are specific to a
+ * particular protocol.  By using an abstract base class in this manner,
+ * plugins can be written for different servers (e.g. IIS, Apache) without
+ * the plugins having to worry about which protocol they are talking.
+ *
+ * This particular OO-in-C system uses a 'endpoint_private' pointer to
+ * point to the protocol-specific data/functions.  So in the subclasses, the
+ * methods do most of their work by getting their hands on the
+ * endpoint_private pointer and then using that to get at the functions for
+ * their protocol.
+ *
+ * Try imagining this as a 'public abstract class', and the
+ * endpoint_private pointer as a sort of extra 'this' reference.  Or
+ * imagine that you are seeing the internal vtables of your favorite OO
+ * language.  Whatever works for you.
+ *
+ * See jk_ajp13_worker.c/jk_ajp14_worker.c and jk_ajp12_worker.c for examples.  
+ */
+struct jk_endpoint {
+  
+    /* 
+     * A 'this' pointer which is used by the subclasses of this class to
+     * point to data/functions which are specific to a given protocol 
+     * (e.g. ajp12 or ajp13 or ajp14).  
+     */
+    void *endpoint_private;
+
+    /*
+     * Forward a request to the servlet engine.  The request is described
+     * by the jk_ws_service_t object.  I'm not sure exactly how
+     * is_recoverable_error is being used.  
+     */
+    int (JK_METHOD *service)(jk_endpoint_t *e, 
+                             jk_ws_service_t *s,
+                             jk_logger_t *l,
+                             int *is_recoverable_error);
+
+    /*
+     * Called when this particular endpoint has finished processing a
+     * request.  For some protocols (e.g. ajp12), this frees the memory
+     * associated with the endpoint.  For others (e.g. ajp13/ajp14), this can
+     * return the endpoint to a cache of already opened endpoints.  
+     *
+     * Note that the first argument is *not* a 'this' pointer, but is
+     * rather a pointer to a 'this' pointer.  This is necessary, because
+     * we may need to free this object.
+     */
+    int (JK_METHOD *done)(jk_endpoint_t **p,
+                          jk_logger_t *l);
+};
+
+/*
+ * The worker 'class', which represents something to which the web server
+ * can delegate requests. 
+ *
+ * This can mean communicating with a particular servlet engine instance,
+ * using a particular protocol.  A single web server instance may have
+ * multiple workers communicating with a single servlet engine (it could be
+ * using ajp12 for some requests and ajp13/ajp14 for others).  Or, a single web
+ * server instance could have multiple workers communicating with different
+ * servlet engines using the same protocol (it could be load balancing
+ * among many engines, using ajp13/ajp14 for all communication).
+ *
+ * There is also a load balancing worker (jk_lb_worker.c), which itself
+ * manages a group of workers.
+ *
+ * Web servers are configured to forward requests to a given worker.  To
+ * handle those requests, the worker's get_endpoint method is called, and
+ * then the service() method of that endpoint is called.
+ *
+ * As with all the core jk classes, this is essentially an abstract base
+ * class which is implemented/extended by classes which are specific to a
+ * particular protocol (or request-handling system).  By using an abstract
+ * base class in this manner, plugins can be written for different servers
+ * (e.g. IIS, Apache) without the plugins having to worry about which
+ * protocol they are talking.
+ *
+ * This particular OO-in-C system uses a 'worker_private' pointer to
+ * point to the protocol-specific data/functions.  So in the subclasses, the
+ * methods do most of their work by getting their hands on the
+ * worker_private pointer and then using that to get at the functions for
+ * their protocol.
+ *
+ * Try imagining this as a 'public abstract class', and the
+ * worker_private pointer as a sort of extra 'this' reference.  Or
+ * imagine that you are seeing the internal vtables of your favorite OO
+ * language.  Whatever works for you.
+ *
+ * See jk_ajp14_worker.c, jk_ajp13_worker.c and jk_ajp12_worker.c for examples.  
+ */
+struct jk_worker {
+
+    /* 
+     * A 'this' pointer which is used by the subclasses of this class to
+     * point to data/functions which are specific to a given protocol 
+     * (e.g. ajp12 or ajp13 or ajp14).  
+     */
+    void *worker_private;
+
+    /*
+     * For all of the below (except destroy), the first argument is
+     * essentially a 'this' pointer.  
+     */
+
+    /*
+     * Given a worker which is in the process of being created, and a list
+     * of configuration options (or 'properties'), check to see if it the
+     * options are.  This will always be called before the init() method.
+     * The init/validate distinction is a bit hazy to me.
+     * See jk_ajp13_worker.c/jk_ajp14_worker.c and jk_worker.c->wc_create_worker() 
+     */
+    int (JK_METHOD *validate)(jk_worker_t *w,
+                              jk_map_t *props, 
+                              jk_worker_env_t *we,
+                              jk_logger_t *l);
+
+    /*
+     * Do whatever initialization needs to be done to start this worker up.
+     * Configuration options are passed in via the props parameter.  
+     */
+    int (JK_METHOD *init)(jk_worker_t *w,
+                          jk_map_t *props, 
+                          jk_worker_env_t *we,
+                          jk_logger_t *l);
+
+
+    /*
+     * Obtain an endpoint to service a particular request.  A pointer to
+     * the endpoint is stored in pend.  
+     */
+    int (JK_METHOD *get_endpoint)(jk_worker_t *w,
+                                  jk_endpoint_t **pend,
+                                  jk_logger_t *l);
+
+    /*
+     * Shutdown this worker.  The first argument is not a 'this' pointer,
+     * but rather a pointer to 'this', so that the object can be free'd (I
+     * think -- though that doesn't seem to be happening.  Hmmm).  
+     */
+    int (JK_METHOD *destroy)(jk_worker_t **w,
+                             jk_logger_t *l);
+};
+
+/*
+ * Essentially, an abstract base class (or factory class) with a single
+ * method -- think of it as createWorker() or the Factory Method Design
+ * Pattern.  There is a different worker_factory function for each of the
+ * different types of workers.  The set of all these functions is created
+ * at startup from the list in jk_worker_list.h, and then the correct one
+ * is chosen in jk_worker.c->wc_create_worker().  See jk_worker.c and
+ * jk_ajp13_worker.c/jk_ajp14_worker.c for examples.
+ *
+ * This allows new workers to be written without modifing the plugin code
+ * for the various web servers (since the only link is through
+ * jk_worker_list.h).  
+ */
+typedef int (JK_METHOD *worker_factory)(jk_worker_t **w,
+                                        const char *name,
+                                        jk_logger_t *l);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_SERVICE_H */
diff --git a/connectors/jk/native/common/jk_sockbuf.c b/connectors/jk/native/common/jk_sockbuf.c
new file mode 100644
index 0000000..6a36133
--- /dev/null
+++ b/connectors/jk/native/common/jk_sockbuf.c
@@ -0,0 +1,197 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Simple buffer object to handle buffered socket IO          *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#include "jk_global.h"
+#include "jk_sockbuf.h"
+
+static int fill_buffer(jk_sockbuf_t *sb);
+
+int jk_sb_open(jk_sockbuf_t *sb,
+               int sd)
+{
+    if(sb && sd >= 0) {
+        sb->end   = 0;
+        sb->start = 0;
+        sb->sd    = sd;
+        return JK_TRUE;
+    }
+
+    return JK_FALSE;
+}
+
+int jk_sb_write(jk_sockbuf_t *sb,
+                const void *buf, 
+                unsigned sz)
+{
+    if(sb && buf && sz) {
+        if((SOCKBUF_SIZE - sb->end) >= sz) {
+            memcpy(sb->buf + sb->end, buf, sz);
+            sb->end += sz;
+        } else {
+            if(!jk_sb_flush(sb)) {
+                return JK_FALSE;
+            }
+            if(sz > SOCKBUF_SIZE) {
+                return (send(sb->sd, (char *)buf, sz, 0) == (int)sz);
+            } 
+            
+            memcpy(sb->buf + sb->end, buf, sz);
+            sb->end += sz;
+        }
+
+        return JK_TRUE;
+    }
+
+    return JK_FALSE;
+}
+
+int jk_sb_flush(jk_sockbuf_t *sb)
+{
+    if(sb) {
+        int save_out = sb->end;
+        sb->end = sb->start = 0;
+        if(save_out) {            
+            return send(sb->sd, sb->buf, save_out, 0) == save_out;
+        }
+        return JK_TRUE;
+    }
+
+    return JK_FALSE;
+}
+
+
+int jk_sb_read(jk_sockbuf_t *sb,
+               char **buf, 
+               unsigned sz,
+               unsigned *ac)
+{
+    if(sb && buf && ac) {
+        unsigned avail;
+
+        *ac = 0;
+        *buf = NULL;
+
+        if(sb->end == sb->start) {
+            sb->end = sb->start = 0;
+            if(fill_buffer(sb) < 0) {
+                return JK_FALSE;
+            }
+        }
+        
+        *buf = sb->buf + sb->start;
+        avail = sb->end - sb->start;
+        if(avail > sz) {
+            *ac = sz;
+        } else {
+            *ac = avail;
+        }
+        sb->start += *ac;
+
+        return JK_TRUE;
+    }
+
+    return JK_FALSE;
+}
+
+int jk_sb_gets(jk_sockbuf_t *sb,
+               char **ps)
+{
+    int ret;
+    if(sb) {
+        while(1) {
+            unsigned i;
+            for(i = sb->start ; i < sb->end ; i++) {
+                if(JK_LF == sb->buf[i]) {
+                    if(i > sb->start && JK_CR == sb->buf[i - 1]) {
+                        sb->buf[i - 1] = '\0';
+                    } else {
+                        sb->buf[i] = '\0';
+                    }
+                    *ps = sb->buf + sb->start;
+                    sb->start = (i + 1);
+                    return JK_TRUE;
+                }
+            }
+            if((ret = fill_buffer(sb)) < 0) {
+                return JK_FALSE;
+            } else if (ret == 0) {
+                *ps = sb->buf + sb->start;
+               if ((SOCKBUF_SIZE - sb->end) > 0) {
+                    sb->buf[sb->end] = '\0';
+                } else {
+                    sb->buf[sb->end-1] = '\0';
+                }
+                return JK_TRUE;
+            }
+        }
+    }
+
+    return JK_FALSE;
+}
+
+/*
+ * Read data from the socket into the associated buffer, and update the
+ * start and end indices.  May move the data currently in the buffer.  If
+ * new data is read into the buffer (or if it is already full), returns 1.
+ * If EOF is received on the socket, returns 0.  In case of error returns
+ * -1.  
+ */
+static int fill_buffer(jk_sockbuf_t *sb)
+{
+    int ret;
+    
+    /*
+     * First move the current data to the beginning of the buffer
+     */
+    if(sb->start < sb->end) {
+        if(sb->start > 0) {
+            unsigned to_copy = sb->end - sb->start;
+            memmove(sb->buf, sb->buf + sb->start, to_copy);
+            sb->start = 0;
+            sb->end = to_copy;
+        }
+    } else {
+        sb->start = sb->end = 0;
+    }
+    
+    /*
+     * In the unlikely case where the buffer is already full, we won't be
+     * reading anything and we'd be calling recv with a 0 count.  
+     */
+    if ((SOCKBUF_SIZE - sb->end) > 0) {
+	/*
+	 * Now, read more data
+	 */
+	ret = recv(sb->sd, 
+		   sb->buf + sb->end, 
+		   SOCKBUF_SIZE - sb->end, 0);   
+	
+	/* 0 is EOF/SHUTDOWN, -1 is SOCK_ERROR */
+	if (ret <= 0) {
+	    return ret;
+	} 
+	
+	sb->end += ret;
+    }
+    
+    return 1;
+}
diff --git a/connectors/jk/native/common/jk_sockbuf.h b/connectors/jk/native/common/jk_sockbuf.h
new file mode 100644
index 0000000..e79039e
--- /dev/null
+++ b/connectors/jk/native/common/jk_sockbuf.h
@@ -0,0 +1,51 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Socket buffer header file                                  *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#include "jk_global.h"
+
+#define SOCKBUF_SIZE (8*1024)
+
+struct jk_sockbuf {
+    char buf[SOCKBUF_SIZE];
+    unsigned start;
+    unsigned end;
+    int  sd;
+};
+typedef struct jk_sockbuf jk_sockbuf_t;
+
+int jk_sb_open(jk_sockbuf_t *sb,
+               int sd);
+
+int jk_sb_write(jk_sockbuf_t *sb,
+                const void *buf, 
+                unsigned sz);
+
+int jk_sb_read(jk_sockbuf_t *sb,
+               char **buf, 
+               unsigned sz,
+               unsigned *ac);
+
+int jk_sb_flush(jk_sockbuf_t *sb);
+
+int jk_sb_gets(jk_sockbuf_t *sb,
+               char **ps);
+
diff --git a/connectors/jk/native/common/jk_uri_worker_map.c b/connectors/jk/native/common/jk_uri_worker_map.c
new file mode 100644
index 0000000..b87969a
--- /dev/null
+++ b/connectors/jk/native/common/jk_uri_worker_map.c
@@ -0,0 +1,581 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: URI to worker map object.                                  *
+ * Maps can be                                                             *
+ *                                                                         *
+ * Exact Context -> /exact/uri=worker e.g. /examples/do*=ajp12             *
+ * Context Based -> /context/*=worker e.g. /examples/*=ajp12               *
+ * Context and suffix ->/context/*.suffix=worker e.g. /examples/*.jsp=ajp12*
+ *                                                                         *
+ * This lets us either partition the work among the web server and the     *
+ * servlet container.                                                      *
+ *                                                                         *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#include "jk_pool.h"
+#include "jk_util.h"
+#include "jk_uri_worker_map.h"
+
+#define MATCH_TYPE_EXACT    (0)
+#define MATCH_TYPE_CONTEXT  (1)
+#define MATCH_TYPE_SUFFIX   (2)
+#define MATCH_TYPE_GENERAL_SUFFIX (3) /* match all URIs of the form *ext */
+/* match all context path URIs with a path component suffix */
+#define MATCH_TYPE_CONTEXT_PATH (4)
+
+struct uri_worker_record {
+    /* Original uri for logging */
+    char *uri;
+
+    /* Name of worker mapped */
+    char *worker_name;
+
+    /* Suffix of uri */
+    char *suffix;
+
+    /* Base context */
+    char *context;
+    
+    /* char length of the context */
+    unsigned ctxt_len;
+
+    int match_type;
+};
+typedef struct uri_worker_record uri_worker_record_t;
+
+struct jk_uri_worker_map {
+    /* Memory Pool */
+    jk_pool_t           p;
+    jk_pool_atom_t      buf[SMALL_POOL_SIZE];
+
+    /* Temp Pool */
+    jk_pool_t tp; 
+    jk_pool_atom_t tbuf[SMALL_POOL_SIZE];
+
+    /* map URI->WORKER */
+    uri_worker_record_t **maps;
+
+    /* Map Number */
+    unsigned            size;
+
+    /* Map Capacity */
+    unsigned            capacity;
+};
+
+
+/*
+ * We are now in a security nightmare, it maybe that somebody sent 
+ * us a uri that looks like /top-secret.jsp. and the web server will 
+ * fumble and return the jsp content. 
+ *
+ * To solve that we will check for path info following the suffix, we 
+ * will also check that the end of the uri is not ".suffix.",
+ * ".suffix/", or ".suffix ".
+ */
+static int check_security_fraud(jk_uri_worker_map_t *uw_map, 
+                                const char *uri, 
+                                jk_logger_t *l)
+{
+    unsigned i;    
+
+    for(i = 0 ; i < uw_map->size ; i++) {
+        if(MATCH_TYPE_SUFFIX == uw_map->maps[i]->match_type) {
+            char *suffix_start;
+            for(suffix_start = strstr(uri, uw_map->maps[i]->suffix) ;
+                suffix_start ;
+                suffix_start = strstr(suffix_start + 1, uw_map->maps[i]->suffix)) {
+                
+                if('.' != *(suffix_start - 1)) {
+                    continue;
+                } else {
+                    char *after_suffix = suffix_start + strlen(uw_map->maps[i]->suffix);
+                
+                    if((('.' == *after_suffix) || ('/' == *after_suffix) || (' ' == *after_suffix)) &&
+                       (0 == strncmp(uw_map->maps[i]->context, uri, uw_map->maps[i]->ctxt_len))) {
+                        /* 
+                         * Security violation !!!
+                         * this is a fraud.
+                         */
+                        return i;
+                    }
+                }
+            }
+        }
+    }
+
+    return -1;
+}
+
+
+int uri_worker_map_alloc(jk_uri_worker_map_t **uw_map,
+                         jk_map_t *init_data,
+                         jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into jk_uri_worker_map_t::uri_worker_map_alloc\n");    
+
+    if(init_data && uw_map) {
+        return uri_worker_map_open(*uw_map = (jk_uri_worker_map_t *)malloc(sizeof(jk_uri_worker_map_t)),
+                                   init_data,
+                                   l);
+    }
+
+    jk_log(l, JK_LOG_ERROR, 
+           "In jk_uri_worker_map_t::uri_worker_map_alloc, NULL parameters\n");    
+
+    return JK_FALSE;
+}
+
+int uri_worker_map_free(jk_uri_worker_map_t **uw_map,
+                        jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into jk_uri_worker_map_t::uri_worker_map_free\n");    
+
+    if(uw_map && *uw_map) {
+        uri_worker_map_close(*uw_map, l);  
+        free(*uw_map);
+        *uw_map = NULL;
+	return JK_TRUE;
+    }
+    else 
+    	jk_log(l, JK_LOG_ERROR, 
+           "In jk_uri_worker_map_t::uri_worker_map_free, NULL parameters\n");    
+
+    return JK_FALSE;
+}
+
+/*
+ * Ensure there will be memory in context info to store Context Bases
+ */
+
+#define UW_INC_SIZE 4   /* 4 URI->WORKER STEP */
+
+static int uri_worker_map_realloc(jk_uri_worker_map_t *uw_map)
+{
+    if (uw_map->size == uw_map->capacity) {
+        uri_worker_record_t **uwr;
+        int  capacity = uw_map->capacity + UW_INC_SIZE;
+
+        uwr = (uri_worker_record_t **)jk_pool_alloc(&uw_map->p, sizeof(uri_worker_record_t *) * capacity);
+
+        if (! uwr)
+            return JK_FALSE;
+
+        if (uw_map->capacity && uw_map->maps)
+            memcpy(uwr, uw_map->maps, sizeof(uri_worker_record_t *) * uw_map->capacity);
+
+        uw_map->maps = uwr;
+        uw_map->capacity = capacity;
+    }
+
+    return JK_TRUE;
+}
+
+
+int uri_worker_map_add(jk_uri_worker_map_t *uw_map, 
+                       char *puri, 
+                       char *pworker,
+                       jk_logger_t *l)
+{
+    uri_worker_record_t *uwr;
+    char                *uri;
+    char                *worker;
+
+    if (uri_worker_map_realloc(uw_map) == JK_FALSE)
+        return JK_FALSE;
+
+    uwr = (uri_worker_record_t *)jk_pool_alloc(&uw_map->p, sizeof(uri_worker_record_t));
+
+    if (! uwr) {
+        jk_log(l, JK_LOG_ERROR, "jk_uri_worker_map_t::uri_worker_map_add, can't alloc map entry\n");
+        return JK_FALSE;
+    }
+
+    uri = jk_pool_strdup(&uw_map->p, puri);
+    worker = jk_pool_strdup(&uw_map->p, pworker);
+
+    if (!uri || ! worker) {
+        jk_log(l, JK_LOG_ERROR, "jk_uri_worker_map_t::uri_worker_map_add, can't alloc uri/worker strings\n");
+        return JK_FALSE;
+    }
+
+    if ('/' == uri[0]) {
+        char *asterisk = strchr(uri, '*');
+
+        if (asterisk) {
+            uwr->uri = jk_pool_strdup(&uw_map->p, uri);
+
+            if (!uwr->uri) {
+                jk_log(l, JK_LOG_ERROR, "jk_uri_worker_map_t::uri_worker_map_add, can't alloc uri string\n");
+                return JK_FALSE;
+            }
+            
+            /*
+             * Now, lets check that the pattern is /context/*.suffix
+             * or /context/*
+             * we need to have a '/' then a '*' and the a '.' or a
+             * '/' then a '*'
+             */
+             asterisk--;
+             if ('/' == asterisk[0]) {
+                if ( 0 == strncmp("/*/",uri,3) ) {
+                    /* general context path */
+                    asterisk[1] = '\0';
+                    uwr->worker_name = worker;
+                    uwr->context = uri;
+                    uwr->suffix  = asterisk + 2;
+                    uwr->match_type = MATCH_TYPE_CONTEXT_PATH;
+                    jk_log(l, JK_LOG_DEBUG,
+                           "Into jk_uri_worker_map_t::uri_worker_map_open, "
+                           "general context path rule %s*%s=%s was added\n",
+                           uri, asterisk + 2, worker);
+                } else if ('.' == asterisk[2]) {
+                    /* suffix rule */
+                    asterisk[1]      = 
+                    asterisk[2]      = '\0';
+                    uwr->worker_name = worker;
+                    uwr->context     = uri;
+                    uwr->suffix      = asterisk + 3;
+                    uwr->match_type  = MATCH_TYPE_SUFFIX;
+                    jk_log(l, JK_LOG_DEBUG,
+                           "Into jk_uri_worker_map_t::uri_worker_map_open, "
+			   "suffix rule %s.%s=%s was added\n",
+                            uri, asterisk + 3, worker); 
+		} else if ('\0' != asterisk[2]) {
+		    /* general suffix rule */
+		    asterisk[1] = '\0';
+		    uwr->worker_name = worker;
+		    uwr->context = uri;
+		    uwr->suffix  = asterisk + 2;
+		    uwr->match_type = MATCH_TYPE_GENERAL_SUFFIX;
+		    jk_log(l, JK_LOG_DEBUG,
+			   "Into jk_uri_worker_map_t::uri_worker_map_open, "
+			   "general suffix rule %s*%s=%s was added\n",
+			   uri, asterisk + 2, worker);
+		} else {
+		    /* context based */
+		    asterisk[1]      = '\0';
+		    uwr->worker_name = worker;
+		    uwr->context     = uri;
+		    uwr->suffix      = NULL;
+		    uwr->match_type  = MATCH_TYPE_CONTEXT;
+		    jk_log(l, JK_LOG_DEBUG,
+			   "Into jk_uri_worker_map_t::uri_worker_map_open, "
+			   "match rule %s=%s was added\n",
+			   uri, worker);
+                }
+            } else {
+                /* Something like : JkMount /servlets/exampl* ajp13 */
+                uwr->uri         = uri;
+                uwr->worker_name = worker;
+                uwr->context     = uri;
+                uwr->suffix      = NULL;
+                uwr->match_type  = MATCH_TYPE_EXACT;
+                jk_log(l, JK_LOG_DEBUG,
+                       "Into jk_uri_worker_map_t::uri_worker_map_open, exact rule %s=%s was added\n",
+                        uri, worker);
+            }
+
+        } else {
+            /* Something like:  JkMount /login/j_security_check ajp13 */
+            uwr->uri         = uri;
+            uwr->worker_name = worker;
+            uwr->context     = uri;
+            uwr->suffix      = NULL;
+            uwr->match_type  = MATCH_TYPE_EXACT;
+            jk_log(l, JK_LOG_DEBUG,
+                   "Into jk_uri_worker_map_t::uri_worker_map_open, exact rule %s=%s was added\n",
+                    uri, worker);
+        }
+        uwr->ctxt_len = strlen(uwr->context);
+    } else {
+        /*
+         * JFC: please check...
+         * Not sure what to do, but I try to prevent problems.
+         * I have fixed jk_mount_context() in apaches/mod_jk.c so we should
+         * not arrive here when using Apache.
+         */
+        jk_log(l, JK_LOG_ERROR,
+               "jk_uri_worker_map_t::uri_worker_map_add, invalid context %s\n",
+               uri);
+        return JK_FALSE;
+    }
+
+    uw_map->maps[uw_map->size] = uwr;
+    uw_map->size++;
+
+    return JK_TRUE;
+}
+
+int uri_worker_map_open(jk_uri_worker_map_t *uw_map,
+                        jk_map_t *init_data,
+                        jk_logger_t *l)
+{
+    int rc = JK_TRUE;
+
+    jk_log(l, JK_LOG_DEBUG, "Into jk_uri_worker_map_t::uri_worker_map_open\n");
+
+    uw_map->size     = 0;
+    uw_map->capacity = 0;
+
+    if (uw_map) {
+        int sz;
+
+        rc = JK_TRUE;
+        jk_open_pool(&uw_map->p, 
+                     uw_map->buf, 
+                     sizeof(jk_pool_atom_t) * SMALL_POOL_SIZE);
+        jk_open_pool(&uw_map->tp,
+                     uw_map->tbuf,
+                     sizeof(jk_pool_atom_t) * SMALL_POOL_SIZE);
+
+        uw_map->size = 0;
+        uw_map->maps = NULL;
+        
+        sz = map_size(init_data);
+
+        jk_log(l, JK_LOG_DEBUG, "jk_uri_worker_map_t::uri_worker_map_open, rule map size is %d\n", sz);
+
+        if (sz > 0) {
+            int i;
+            for(i = 0; i < sz ; i++) {
+                if (uri_worker_map_add(uw_map, map_name_at(init_data, i), map_value_at(init_data, i), l) == JK_FALSE) {
+                    rc = JK_FALSE;
+                    break;
+                }
+            }
+
+            if (i == sz) {
+                jk_log(l, JK_LOG_DEBUG, "Into jk_uri_worker_map_t::uri_worker_map_open, there are %d rules\n", uw_map->size);    
+            } else {
+                jk_log(l, JK_LOG_ERROR, "jk_uri_worker_map_t::uri_worker_map_open, There was a parsing error\n");
+                rc = JK_FALSE;
+            }
+        }       
+
+        if (rc == JK_FALSE) {
+            jk_log(l, JK_LOG_ERROR, "jk_uri_worker_map_t::uri_worker_map_open, there was an error, freing buf\n");
+            jk_close_pool(&uw_map->p);
+            jk_close_pool(&uw_map->tp);
+        }
+    }
+    
+    jk_log(l, JK_LOG_DEBUG, "jk_uri_worker_map_t::uri_worker_map_open, done\n");
+    return rc;
+}
+
+/* returns the index of the last occurrence of the 'ch' character
+   if ch=='\0' returns the length of the string str  */
+int last_index_of(const char *str,char ch)
+{
+    const char *str_minus_one=str-1;
+    const char *s=str+strlen(str);
+    while(s!=str_minus_one && ch!=*s) {
+	--s;
+    }
+    return (s-str);
+}
+
+int uri_worker_map_close(jk_uri_worker_map_t *uw_map,
+                         jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into jk_uri_worker_map_t::uri_worker_map_close\n"); 
+
+    if(uw_map) {
+        jk_close_pool(&uw_map->p);
+        jk_close_pool(&uw_map->tp);
+	return JK_TRUE;
+    }
+
+    jk_log(l, JK_LOG_ERROR, 
+           "jk_uri_worker_map_t::uri_worker_map_close, NULL parameter\n"); 
+
+    return JK_FALSE;
+}
+
+void jk_no2slash(char *name)
+{
+    char *d, *s;
+
+    s = d = name;
+
+#if defined(HAVE_UNC_PATHS) 
+    /* Check for UNC names.  Leave leading two slashes. */
+    if (s[0] == '/' && s[1] == '/')
+        *d++ = *s++;
+#endif
+
+    while (*s) {
+        if ((*d++ = *s) == '/') {
+            do {
+                ++s;
+            } while (*s == '/');
+        }
+        else {
+            ++s;
+        }
+    }
+    *d = '\0';
+}
+
+
+char *map_uri_to_worker(jk_uri_worker_map_t *uw_map,
+                        char *uri,
+                        jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into jk_uri_worker_map_t::map_uri_to_worker\n");    
+
+    if(uw_map && uri && '/' == uri[0]) {
+        unsigned i;
+        unsigned best_match = -1;
+        unsigned longest_match = 0;
+        char *url_rewrite = strstr(uri, JK_PATH_SESSION_IDENTIFIER);
+        
+        if(url_rewrite) {
+            *url_rewrite = '\0';
+        }
+        jk_no2slash(uri);
+
+        jk_log(l, JK_LOG_DEBUG, "Attempting to map URI '%s'\n", uri);
+        for(i = 0 ; i < uw_map->size ; i++) {
+            uri_worker_record_t *uwr = uw_map->maps[i];
+
+            if(uwr->ctxt_len < longest_match) {
+                continue; /* can not be a best match anyway */
+            }
+
+            if(0 == strncmp(uwr->context, 
+                            uri, 
+                            uwr->ctxt_len)) {
+                if(MATCH_TYPE_EXACT == uwr->match_type) {
+                    if(strlen(uri) == uwr->ctxt_len) {
+			jk_log(l,
+			       JK_LOG_DEBUG,
+			       "jk_uri_worker_map_t::map_uri_to_worker, "
+			       "Found an exact match %s -> %s\n",
+			       uwr->worker_name,
+			       uwr->context );
+                        return uwr->worker_name;
+                    }
+                } else if(MATCH_TYPE_CONTEXT == uwr->match_type) {
+                    if(uwr->ctxt_len > longest_match) {
+			jk_log(l,
+			       JK_LOG_DEBUG,
+			       "jk_uri_worker_map_t::map_uri_to_worker, "
+			       "Found a context match %s -> %s\n",
+			       uwr->worker_name,
+			       uwr->context );
+                        longest_match = uwr->ctxt_len;
+                        best_match = i;
+                    }
+		} else if(MATCH_TYPE_GENERAL_SUFFIX == uwr->match_type) {
+                    int suffix_start=last_index_of(uri,uwr->suffix[0]);
+                    if (suffix_start>=0 && 0==strcmp(uri+suffix_start,uwr->suffix)) {
+			if(uwr->ctxt_len >= longest_match) {
+			    jk_log(l,
+				   JK_LOG_DEBUG,
+				   "jk_uri_worker_map_t::map_uri_to_worker, "
+				   "Found a general suffix match %s -> *%s\n",
+				   uwr->worker_name,
+				   uwr->suffix );
+			    longest_match = uwr->ctxt_len;
+			    best_match = i;
+			}
+                    }
+                } else if(MATCH_TYPE_CONTEXT_PATH == uwr->match_type) {
+                    char *suffix_path = NULL;
+                    if (strlen(uri) > 1 && (suffix_path = strchr(uri+1,'/')) != NULL) {
+                        if (0 == strncmp(suffix_path,uwr->suffix,strlen(uwr->suffix))) {
+                            if(uwr->ctxt_len >= longest_match) {
+                                jk_log(l,
+                                       JK_LOG_DEBUG,
+                                       "jk_uri_worker_map_t::map_uri_to_worker, "
+                                       "Found a general context path match %s -> *%s\n",
+                                       uwr->worker_name,
+                                       uwr->suffix );   
+                                longest_match = uwr->ctxt_len;
+                                best_match = i;
+                            }
+                        }
+                    }
+                } else /* suffix match */ {
+                    int suffix_start;
+                    
+                    for(suffix_start = strlen(uri) - 1 ; 
+                        suffix_start > 0 && '.' != uri[suffix_start]; 
+                        suffix_start--) 
+                        ;
+                    if('.' == uri[suffix_start]) {
+                        const char *suffix = uri + suffix_start + 1;
+
+                        /* for WinXX, fix the JsP != jsp problems */
+#ifdef WIN32                        
+                        if(0 == strcasecmp(suffix, uwr->suffix))  {
+#else
+                        if(0 == strcmp(suffix, uwr->suffix)) {
+#endif
+                            if(uwr->ctxt_len >= longest_match) {
+				jk_log(l,
+				       JK_LOG_DEBUG,
+				       "jk_uri_worker_map_t::map_uri_to_worker, Found a suffix match %s -> *.%s\n",
+				       uwr->worker_name,
+				       uwr->suffix );
+                                longest_match = uwr->ctxt_len;
+                                best_match = i;
+                            }
+                        }
+                    }                                       
+                }
+            }
+        }
+
+        if(-1 != best_match) {
+            return uw_map->maps[best_match]->worker_name;
+        } else {
+            /*
+             * We are now in a security nightmare, it maybe that somebody sent 
+             * us a uri that looks like /top-secret.jsp. and the web server will 
+             * fumble and return the jsp content. 
+             *
+             * To solve that we will check for path info following the suffix, we 
+             * will also check that the end of the uri is not .suffix.
+             */
+            int fraud = check_security_fraud(uw_map, uri, l);
+
+            if(fraud >= 0) {
+                jk_log(l, JK_LOG_EMERG, 
+                       "In jk_uri_worker_map_t::map_uri_to_worker, found a security fraud in '%s'\n",
+                       uri);    
+                return uw_map->maps[fraud]->worker_name;
+            }
+       }        
+    } else {
+        jk_log(l, JK_LOG_ERROR, 
+               "In jk_uri_worker_map_t::map_uri_to_worker, wrong parameters\n");    
+    }
+
+    jk_log(l, JK_LOG_DEBUG, 
+           "jk_uri_worker_map_t::map_uri_to_worker, done without a match\n"); 
+
+    return NULL;
+}
diff --git a/connectors/jk/native/common/jk_uri_worker_map.h b/connectors/jk/native/common/jk_uri_worker_map.h
new file mode 100644
index 0000000..f67e330
--- /dev/null
+++ b/connectors/jk/native/common/jk_uri_worker_map.h
@@ -0,0 +1,67 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: URI to worker mapper header file                           *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef JK_URI_WORKER_MAP_H
+#define JK_URI_WORKER_MAP_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_logger.h"
+
+struct jk_uri_worker_map;
+typedef struct jk_uri_worker_map jk_uri_worker_map_t;
+
+int uri_worker_map_alloc(jk_uri_worker_map_t **uw_map,
+                         jk_map_t *init_data,
+                         jk_logger_t *l);
+
+int uri_worker_map_free(jk_uri_worker_map_t **uw_map,
+                        jk_logger_t *l);
+
+int uri_worker_map_open(jk_uri_worker_map_t *uw_map,
+                        jk_map_t *init_data,
+                        jk_logger_t *l);
+
+int uri_worker_map_add(jk_uri_worker_map_t *uw_map,
+                       char *puri,
+                       char *pworker,
+                       jk_logger_t *l);
+
+int uri_worker_map_close(jk_uri_worker_map_t *uw_map,
+                         jk_logger_t *l);
+
+void jk_no2slash(char *name);
+
+char *map_uri_to_worker(jk_uri_worker_map_t *uw_map,
+                        char *uri,
+                        jk_logger_t *l);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_URI_WORKER_MAP_H */
diff --git a/connectors/jk/native/common/jk_util.c b/connectors/jk/native/common/jk_util.c
new file mode 100644
index 0000000..075c1b7
--- /dev/null
+++ b/connectors/jk/native/common/jk_util.c
@@ -0,0 +1,992 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Utility functions (mainly configuration)                   *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+
+#include "jk_util.h"
+#include "jk_ajp12_worker.h"
+
+#define SYSPROPS_OF_WORKER          ("sysprops")
+#define STDERR_OF_WORKER            ("stderr")
+#define STDOUT_OF_WORKER            ("stdout")
+#define SECRET_OF_WORKER            ("secret")
+#define CONF_OF_WORKER              ("conf")
+#define MX_OF_WORKER                ("mx")
+#define MS_OF_WORKER                ("ms")
+#define CP_OF_WORKER                ("class_path")
+#define BRIDGE_OF_WORKER			("bridge")
+#define JVM_OF_WORKER               ("jvm_lib")
+#define LIBPATH_OF_WORKER           ("ld_path")
+#define CMD_LINE_OF_WORKER          ("cmd_line")
+#define NATIVE_LIB_OF_WORKER        ("native_lib")
+#define PREFIX_OF_WORKER            ("worker")
+#define HOST_OF_WORKER              ("host")
+#define PORT_OF_WORKER              ("port")
+#define TYPE_OF_WORKER              ("type")
+#define CACHE_OF_WORKER             ("cachesize")
+#define CACHE_TIMEOUT_OF_WORKER     ("cache_timeout")
+#define RECOVERY_OPTS_OF_WORKER     ("recovery_options")
+#define CONNECT_TIMEOUT_OF_WORKER 	("connect_timeout")
+#define PREPOST_TIMEOUT_OF_WORKER 	("prepost_timeout")
+#define REPLY_TIMEOUT_OF_WORKER 	("reply_timeout")
+#define SOCKET_TIMEOUT_OF_WORKER    ("socket_timeout")
+#define SOCKET_KEEPALIVE_OF_WORKER  ("socket_keepalive")
+#define LOAD_FACTOR_OF_WORKER       ("lbfactor")
+#define BALANCED_WORKERS            ("balanced_workers")
+#define STICKY_SESSION              ("sticky_session")
+#define LOCAL_WORKER_ONLY_FLAG      ("local_worker_only")
+#define LOCAL_WORKER_FLAG           ("local_worker")
+#define WORKER_AJP12                ("ajp12")
+#define DEFAULT_WORKER_TYPE         JK_AJP12_WORKER_NAME
+#define SECRET_KEY_OF_WORKER        ("secretkey")
+
+#define DEFAULT_WORKER              JK_AJP12_WORKER_NAME
+#define WORKER_LIST_PROPERTY_NAME   ("worker.list")
+#define DEFAULT_LB_FACTOR           (1.0)
+#define LOG_FORMAT          		("log_format")
+
+#define TOMCAT32_BRIDGE_NAME   		("tomcat32")
+#define TOMCAT33_BRIDGE_NAME   		("tomcat33")
+#define TOMCAT40_BRIDGE_NAME   		("tomcat40")
+#define TOMCAT41_BRIDGE_NAME   		("tomcat41")
+#define TOMCAT50_BRIDGE_NAME   		("tomcat5")
+
+#define HUGE_BUFFER_SIZE (8*1024)
+#define LOG_LINE_SIZE    (1024)
+
+/* 
+ * define the log format, we're using by default the one from error.log 
+ *
+ * [Mon Mar 26 19:44:48 2001] [jk_uri_worker_map.c (155)]: Into jk_uri_worker_map_t::uri_worker_map_alloc
+ * log format used by apache in error.log
+ */
+#ifndef JK_TIME_FORMAT 
+#define JK_TIME_FORMAT "[%a %b %d %H:%M:%S %Y] "
+#endif
+
+const char * jk_log_fmt = JK_TIME_FORMAT;
+
+static void set_time_str(char * str, int len)
+{
+    time_t      t = time(NULL);
+    struct tm   *tms;
+
+    tms = localtime(&t);
+    strftime(str, len, jk_log_fmt, tms);
+}
+
+/* Write at most n characters to the buffer in str, return the
+ * number of chars written or -1 if the buffer would have been
+ * overflowed.
+ *
+ * This is portable to any POSIX-compliant system that has /dev/null
+ */
+#if !defined(HAVE_VSNPRINTF) && !defined(HAVE_APR)
+static FILE *f=NULL;
+int vsnprintf( char *str, size_t n, const char *fmt, va_list ap )
+{
+       int res;
+
+       if (f == NULL)
+               f = fopen("/dev/null","w");
+       if (f == NULL)
+               return -1;
+
+       setvbuf( f, str, _IOFBF, n );
+
+       res = vfprintf( f, fmt, ap );
+
+       if ( res > 0 && res < n ) {
+               res = vsprintf( str, fmt, ap );
+       }
+       return res;
+}
+#endif
+#if !defined(HAVE_SNPRINTF) && !defined(HAVE_APR)
+int snprintf( char *str, size_t n, const char *fmt, ... )
+{
+        va_list ap;
+        int res;
+ 
+        va_start( ap, fmt );
+        res = vsnprintf( str, n, fmt, ap );
+        va_end( ap );
+        return res;
+}
+#endif
+
+static int JK_METHOD log_to_file(jk_logger_t *l,                                 
+                                 int level,
+                                 const char *what)
+{
+    if( l &&
+        (l->level <= level || level == JK_LOG_REQUEST_LEVEL) &&
+        l->logger_private && what) {
+        unsigned sz = strlen(what);
+        if(sz) {
+            file_logger_t *p = l->logger_private;
+            fwrite(what, 1, sz, p->logfile);
+            /* [V] Flush the dam' thing! */
+            fflush(p->logfile);
+        }
+
+        return JK_TRUE;
+    }
+
+    return JK_FALSE;
+}
+
+int jk_parse_log_level(const char *level)
+{
+    if(0 == strcasecmp(level, JK_LOG_INFO_VERB)) {
+        return JK_LOG_INFO_LEVEL;
+    }
+
+    if(0 == strcasecmp(level, JK_LOG_ERROR_VERB)) {
+        return JK_LOG_ERROR_LEVEL;
+    }
+
+    if(0 == strcasecmp(level, JK_LOG_EMERG_VERB)) {
+        return JK_LOG_EMERG_LEVEL;
+    }
+
+    return JK_LOG_DEBUG_LEVEL;
+}
+
+int jk_open_file_logger(jk_logger_t **l,
+                        const char *file,
+                        int level)
+{
+    if(l && file) {     
+        jk_logger_t *rc = (jk_logger_t *)malloc(sizeof(jk_logger_t));
+        file_logger_t *p = (file_logger_t *)malloc(sizeof(file_logger_t));
+        if(rc && p) {
+            rc->log = log_to_file;
+            rc->level = level;
+            rc->logger_private = p;
+#ifdef AS400
+            p->logfile = fopen(file, "a+, o_ccsid=0");
+#else
+            p->logfile = fopen(file, "a+");
+#endif
+            if(p->logfile) {
+                *l = rc;
+                return JK_TRUE;
+            }           
+        }
+        if(rc) {
+            free(rc);
+        }
+        if(p) {
+            free(p);
+        }
+
+        *l = NULL;
+    }
+    return JK_FALSE;
+}
+
+int jk_close_file_logger(jk_logger_t **l)
+{
+    if(l && *l) {
+        file_logger_t *p = (*l)->logger_private;
+        fflush(p->logfile);
+        fclose(p->logfile);
+        free(p);
+        free(*l);
+        *l = NULL;
+
+        return JK_TRUE;
+    }
+    return JK_FALSE;
+}
+
+int jk_log(jk_logger_t *l,
+           const char *file,
+           int line,
+           int level,
+           const char *fmt, ...)
+{
+    int rc = 0;
+    if(!l || !file || !fmt) {
+        return -1;
+    }
+
+    if((l->level <= level) || (level == JK_LOG_REQUEST_LEVEL)) {
+#ifdef NETWARE
+/* On NetWare, this can get called on a thread that has a limited stack so */
+/* we will allocate and free the temporary buffer in this function         */
+        char *buf;
+#else
+        char buf[HUGE_BUFFER_SIZE];
+#endif
+        char *f = (char *)(file + strlen(file) - 1);
+        va_list args;
+        int used = 0;
+
+        while(f != file && '\\' != *f && '/' != *f) {
+            f--;
+        }
+        if(f != file) {
+            f++;
+        }
+
+#ifdef USE_SPRINTF /* until we get a snprintf function */
+#ifdef NETWARE
+        buf = (char *) malloc(HUGE_BUFFER_SIZE);
+        if (NULL == buf)
+           return -1;
+#endif
+    set_time_str(buf, HUGE_BUFFER_SIZE);
+    used = strlen(buf);
+        if(line)
+            used += sprintf(&buf[used], " [%s (%d)]: ", f, line);
+#else 
+    set_time_str(buf, HUGE_BUFFER_SIZE);
+    used = strlen(buf);
+        if(line)
+            used += snprintf(&buf[used], HUGE_BUFFER_SIZE, " [%s (%d)]: ", f, line);        
+#endif
+        if(used < 0) {
+            return 0; /* [V] not sure what to return... */
+        }
+    
+        va_start(args, fmt);
+#ifdef USE_VSPRINTF /* until we get a vsnprintf function */
+        rc = vsprintf(buf + used, fmt, args);
+#else 
+        rc = vsnprintf(buf + used, HUGE_BUFFER_SIZE - used, fmt, args);
+#endif
+        va_end(args);
+        l->log(l, level, buf);
+#ifdef NETWARE
+        free(buf);
+#endif
+    }
+    
+    return rc;
+}
+
+char *jk_get_worker_type(jk_map_t *m,
+                         const char *wname)
+{
+    char buf[1024];
+
+    if(!m || !wname) {
+        return NULL;
+    }
+
+    sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, TYPE_OF_WORKER);
+
+    return map_get_string(m, buf, DEFAULT_WORKER_TYPE);
+}
+
+char *jk_get_worker_secret(jk_map_t *m,
+                           const char *wname)
+{
+    char buf[1024];
+    char *secret;
+
+    if(!m || !wname) {
+        return NULL;
+    }
+
+    sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, SECRET_OF_WORKER);
+
+    secret=map_get_string(m, buf, NULL);
+
+    return secret;
+}
+
+/* [V] I suggest that the following general purpose functions be used.       */
+/*     More should be added (double etc.), but now these were enough for me. */
+/*     Functions that can be simulated with these should be "deprecated".    */
+
+int jk_get_worker_str_prop(jk_map_t *m,
+                           const char *wname,
+                           const char *pname,
+                           char **prop)
+{
+    char buf[1024];
+
+    if(m && prop && wname && pname) {
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, pname);
+        *prop = map_get_string(m, buf, NULL);
+        if(*prop) {
+            return JK_TRUE;
+        }
+    }
+    return JK_FALSE;
+}
+
+int jk_get_worker_int_prop(jk_map_t *m,
+                           const char *wname,
+                           const char *pname,
+                           int *prop)
+{
+    char buf[1024];
+
+    if(m && prop && wname && pname) {
+        int i;
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, pname);
+        i = map_get_int(m, buf, -1);
+        if(-1 != i) {
+            *prop = i;
+            return JK_TRUE;
+        }
+    }
+    return JK_FALSE;
+}
+
+char *jk_get_worker_host(jk_map_t *m,
+                         const char *wname,
+                         const char *def)
+{
+    char buf[1024];
+
+    if(!m || !wname) {
+        return NULL;
+    }
+
+    sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, HOST_OF_WORKER);
+
+    return map_get_string(m, buf, def);
+}
+
+int jk_get_worker_port(jk_map_t *m,
+                       const char *wname,
+                       int def)
+{
+    char buf[1024];
+
+    if(!m || !wname) {
+        return -1;
+    }
+
+    sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, PORT_OF_WORKER);
+
+    return map_get_int(m, buf, def);
+}
+
+int jk_get_worker_cache_size(jk_map_t *m, 
+                             const char *wname,
+                             int def)
+{
+    char buf[1024];
+
+    if(!m || !wname) {
+        return -1;
+    }
+
+    sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, CACHE_OF_WORKER);
+
+    return map_get_int(m, buf, def);
+}
+
+int jk_get_worker_socket_timeout(jk_map_t *m,
+                                 const char *wname,
+                                 int def)
+{
+    char buf[1024];
+
+    if(!m || !wname) {
+        return -1;
+    }
+
+    sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, SOCKET_TIMEOUT_OF_WORKER);
+
+    return map_get_int(m, buf, def);
+}
+
+int jk_get_worker_socket_keepalive(jk_map_t *m,
+                                   const char *wname,
+                                   int def)
+{
+    char buf[1024];
+
+    if(!m || !wname) {
+        return -1;
+    }
+
+    sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, SOCKET_KEEPALIVE_OF_WORKER);
+
+    return map_get_int(m, buf, def);
+}
+
+int jk_get_worker_cache_timeout(jk_map_t *m,
+                                const char *wname,
+                                int def)
+{
+    char buf[1024];
+
+    if(!m || !wname) {
+        return -1;
+    }
+
+    sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, CACHE_TIMEOUT_OF_WORKER);
+
+    return map_get_int(m, buf, def);
+}
+
+int jk_get_worker_connect_timeout(jk_map_t *m,
+                                  const char *wname,
+                                  int def)
+{
+    char buf[1024];
+
+    if(!m || !wname) {
+        return -1;
+    }
+
+    sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, CONNECT_TIMEOUT_OF_WORKER);
+
+    return map_get_int(m, buf, def);
+}
+
+int jk_get_worker_prepost_timeout(jk_map_t *m,
+                                  const char *wname,
+                                  int def)
+{
+    char buf[1024];
+
+    if(!m || !wname) {
+        return -1;
+    }
+
+    sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, PREPOST_TIMEOUT_OF_WORKER);
+
+    return map_get_int(m, buf, def);
+}
+
+int jk_get_worker_reply_timeout(jk_map_t *m,
+                                const char *wname,
+                                int def)
+{
+    char buf[1024];
+
+    if(!m || !wname) {
+        return -1;
+    }
+
+    sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, REPLY_TIMEOUT_OF_WORKER);
+
+    return map_get_int(m, buf, def);
+}
+
+int jk_get_worker_recovery_opts(jk_map_t *m,
+                                const char *wname,
+                                int def)
+{
+    char buf[1024];
+
+    if(!m || !wname) {
+        return -1;
+    }
+
+    sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, RECOVERY_OPTS_OF_WORKER);
+
+    return map_get_int(m, buf, def);
+}
+
+char * jk_get_worker_secret_key(jk_map_t *m,
+                                const char *wname)
+{
+    char buf[1024];
+
+    if(!m || !wname) {
+        return NULL;
+    }
+
+    sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, SECRET_KEY_OF_WORKER);
+    return map_get_string(m, buf, NULL);
+}
+
+int jk_get_worker_list(jk_map_t *m,
+                       char ***list,
+                       unsigned *num_of_wokers)
+{
+    if(m && list && num_of_wokers) {
+        char **ar = map_get_string_list(m, 
+                                        WORKER_LIST_PROPERTY_NAME, 
+                                        num_of_wokers, 
+                                        DEFAULT_WORKER);
+        if(ar)  {
+            *list = ar;     
+            return JK_TRUE;
+        }
+        *list = NULL;   
+        *num_of_wokers = 0;
+    }
+
+    return JK_FALSE;
+}
+
+void jk_set_log_format(const char * logformat)
+{
+    jk_log_fmt = (logformat) ? logformat : JK_TIME_FORMAT;
+}
+
+double jk_get_lb_factor(jk_map_t *m, 
+                        const char *wname)
+{
+    char buf[1024];
+    
+    if(!m || !wname) {
+        return DEFAULT_LB_FACTOR;
+    }
+
+    sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, LOAD_FACTOR_OF_WORKER);
+
+    return map_get_double(m, buf, DEFAULT_LB_FACTOR);
+}
+
+int jk_get_is_sticky_session(jk_map_t *m,
+                            const char *wname) {
+    int rc = JK_TRUE;
+    char buf[1024];
+    if (m && wname) {
+        int value;
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, STICKY_SESSION);
+        value = map_get_int(m, buf, 1);
+        if (!value) rc = JK_FALSE;
+    }
+    return rc;
+}
+
+int jk_get_is_local_worker(jk_map_t *m,
+                            const char *wname) {
+    int rc = JK_FALSE;
+    char buf[1024];
+    if (m && wname) {
+        int value;
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, LOCAL_WORKER_FLAG);
+        value = map_get_int(m, buf, 0);
+        if (value) rc = JK_TRUE;
+    }
+    return rc;
+}
+
+int jk_get_local_worker_only_flag(jk_map_t *m,
+                                  const char *lb_wname) 
+{
+    int rc = JK_FALSE;
+    char buf[1024];
+    if (m && lb_wname) {
+        int value;
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, lb_wname, LOCAL_WORKER_ONLY_FLAG);
+        value = map_get_int(m, buf, 0);
+        if (value) rc = JK_TRUE;
+    }
+    return rc;
+}
+
+int jk_get_lb_worker_list(jk_map_t *m, 
+                          const char *lb_wname,
+                          char ***list, 
+                          unsigned *num_of_wokers)
+{
+    char buf[1024];
+
+    if(m && list && num_of_wokers && lb_wname) {
+        char **ar = NULL;
+
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, lb_wname, BALANCED_WORKERS);
+        ar = map_get_string_list(m, buf, num_of_wokers, NULL);
+        if(ar)  {
+            *list = ar;     
+            return JK_TRUE;
+        }
+        *list = NULL;   
+        *num_of_wokers = 0;
+    }
+
+    return JK_FALSE;
+}
+
+int jk_get_worker_mx(jk_map_t *m, 
+                     const char *wname,
+                     unsigned *mx)
+{
+    char buf[1024];
+    
+    if(m && mx && wname) {
+        int i;
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, MX_OF_WORKER);
+
+        i = map_get_int(m, buf, -1);
+        if(-1 != i) {
+            *mx = (unsigned)i;
+            return JK_TRUE;
+        }
+    }
+
+    return JK_FALSE;
+}
+
+int jk_get_worker_ms(jk_map_t *m, 
+                     const char *wname,
+                     unsigned *ms)
+{
+    char buf[1024];
+    
+    if(m && ms && wname) {
+        int i;
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, MS_OF_WORKER);
+
+        i = map_get_int(m, buf, -1);
+        if(-1 != i) {
+            *ms = (unsigned)i;
+            return JK_TRUE;
+        }
+    }
+
+    return JK_FALSE;
+}
+
+int jk_get_worker_classpath(jk_map_t *m, 
+                            const char *wname, 
+                            char **cp)
+{
+    char buf[1024];
+    
+    if(m && cp && wname) {
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, CP_OF_WORKER);
+
+        *cp = map_get_string(m, buf, NULL);
+        if(*cp) {
+            return JK_TRUE;
+        }
+    }
+
+    return JK_FALSE;
+}
+
+int jk_get_worker_bridge_type(jk_map_t *m, 
+                              const char *wname,
+                              unsigned *bt)
+{
+    char buf[1024];
+	char *type;
+	    
+    if(m && bt && wname) {
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, BRIDGE_OF_WORKER);
+
+        type = map_get_string(m, buf, NULL);
+        
+        if(type) {
+        	if (! strcasecmp(type, TOMCAT32_BRIDGE_NAME))
+        		*bt = TC32_BRIDGE_TYPE;
+        	else if (! strcasecmp(type, TOMCAT33_BRIDGE_NAME))
+        		*bt = TC33_BRIDGE_TYPE;
+        	else if (! strcasecmp(type, TOMCAT40_BRIDGE_NAME))
+        		*bt = TC40_BRIDGE_TYPE;
+        	else if (! strcasecmp(type, TOMCAT41_BRIDGE_NAME))
+        		*bt = TC41_BRIDGE_TYPE;
+        	else if (! strcasecmp(type, TOMCAT50_BRIDGE_NAME))
+        		*bt = TC50_BRIDGE_TYPE;
+        		
+            return JK_TRUE;
+        }
+    }
+
+    return JK_FALSE;
+}
+
+int jk_get_worker_jvm_path(jk_map_t *m, 
+                           const char *wname, 
+                           char **vm_path)
+{
+    char buf[1024];
+    
+    if(m && vm_path && wname) {
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, JVM_OF_WORKER);
+
+        *vm_path = map_get_string(m, buf, NULL);
+        if(*vm_path) {
+            return JK_TRUE;
+        }
+    }
+
+    return JK_FALSE;
+}
+
+/* [V] This is unused. currently. */
+int jk_get_worker_callback_dll(jk_map_t *m, 
+                               const char *wname, 
+                               char **cb_path)
+{
+    char buf[1024];
+
+    if(m && cb_path && wname) {
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, NATIVE_LIB_OF_WORKER);
+
+        *cb_path = map_get_string(m, buf, NULL);
+        if(*cb_path) {
+            return JK_TRUE;
+        }
+    }
+
+    return JK_FALSE;
+}
+
+int jk_get_worker_cmd_line(jk_map_t *m, 
+                           const char *wname, 
+                           char **cmd_line)
+{
+    char buf[1024];
+
+    if(m && cmd_line && wname) {
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, CMD_LINE_OF_WORKER);
+
+        *cmd_line = map_get_string(m, buf, NULL);
+        if(*cmd_line) {
+            return JK_TRUE;
+        }
+    }
+
+    return JK_FALSE;
+}
+
+
+int jk_file_exists(const char *f)
+{
+    if(f) {
+        struct stat st;
+#ifdef AS400
+        if((0 == stat(f, &st)) && (st.st_mode & _S_IFREG)) {
+#else
+        if((0 == stat(f, &st)) && (st.st_mode & S_IFREG)) {
+#endif
+            return JK_TRUE;
+        }
+    }
+    return JK_FALSE;
+}
+
+static int jk_is_some_property(const char *prp_name, const char *suffix)
+{
+    if (prp_name && suffix) {
+        size_t prp_name_len = strlen(prp_name);
+        size_t suffix_len = strlen(suffix);
+        if (prp_name_len >= suffix_len) {
+            const char *prp_suffix = prp_name + prp_name_len - suffix_len;
+            if(0 == strcmp(suffix, prp_suffix)) {
+                return JK_TRUE;
+            }
+        }
+    }
+
+    return JK_FALSE;
+}
+
+int jk_is_path_poperty(const char *prp_name)
+{
+    return jk_is_some_property(prp_name, "path");
+}
+
+int jk_is_cmd_line_poperty(const char *prp_name)
+{
+    return jk_is_some_property(prp_name, CMD_LINE_OF_WORKER);
+}
+
+int jk_get_worker_stdout(jk_map_t *m, 
+                         const char *wname, 
+                         char **stdout_name)
+
+{
+    char buf[1024];
+
+    if(m && stdout_name && wname) {
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, STDOUT_OF_WORKER);
+
+        *stdout_name = map_get_string(m, buf, NULL);
+        if(*stdout_name) {
+            return JK_TRUE;
+        }
+    }
+
+    return JK_FALSE;
+}
+
+int jk_get_worker_stderr(jk_map_t *m, 
+                         const char *wname, 
+                         char **stderr_name)
+
+{
+    char buf[1024];
+
+    if(m && stderr_name && wname) {
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, STDERR_OF_WORKER);
+
+        *stderr_name = map_get_string(m, buf, NULL);
+        if(*stderr_name) {
+            return JK_TRUE;
+        }
+    }
+
+    return JK_FALSE;
+}
+
+int jk_get_worker_sysprops(jk_map_t *m, 
+                           const char *wname, 
+                           char **sysprops)
+
+{
+    char buf[1024];
+
+    if(m && sysprops && wname) {
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, SYSPROPS_OF_WORKER);
+
+        *sysprops = map_get_string(m, buf, NULL);
+        if(*sysprops) {
+            return JK_TRUE;
+        }
+    }
+
+    return JK_FALSE;
+}
+
+int jk_get_worker_libpath(jk_map_t *m, 
+                          const char *wname, 
+                          char **libpath)
+{
+    char buf[1024];
+
+    if(m && libpath && wname) {
+        sprintf(buf, "%s.%s.%s", PREFIX_OF_WORKER, wname, LIBPATH_OF_WORKER);
+
+        *libpath = map_get_string(m, buf, NULL);
+        if(*libpath) {
+            return JK_TRUE;
+        }
+    }
+
+    return JK_FALSE;
+}
+
+char **jk_parse_sysprops(jk_pool_t *p, 
+                         const char *sysprops)
+{
+    char **rc = NULL;
+#if defined(AS400) || defined(_REENTRANT)
+    char *lasts;
+#endif
+
+    if(p && sysprops) {
+        char *prps = jk_pool_strdup(p, sysprops);
+        if(prps && strlen(prps)) {
+            unsigned num_of_prps;
+
+            for(num_of_prps = 1; *sysprops ; sysprops++) {
+                if('*' == *sysprops) {
+                    num_of_prps++;
+                }
+            }            
+
+            rc = jk_pool_alloc(p, (num_of_prps + 1) * sizeof(char *));
+            if(rc) {
+                unsigned i = 0;
+#if defined(AS400) || defined(_REENTRANT)
+                char *tmp = strtok_r(prps, "*", &lasts);
+#else
+                char *tmp = strtok(prps, "*");
+#endif
+
+                while(tmp && i < num_of_prps) {
+                    rc[i] = tmp;
+#if defined(AS400) || defined(_REENTRANT)
+                    tmp = strtok_r(NULL, "*", &lasts);
+#else
+                    tmp = strtok(NULL, "*");
+#endif
+                    i++;
+                }
+                rc[i] = NULL;
+            }
+        }
+    }
+
+    return rc;
+}
+
+void jk_append_libpath(jk_pool_t *p, 
+                       const char *libpath)
+{
+    char *env = NULL;
+    char *current = getenv(PATH_ENV_VARIABLE);
+
+    if(current) {
+        env = jk_pool_alloc(p, strlen(PATH_ENV_VARIABLE) + 
+                               strlen(current) + 
+                               strlen(libpath) + 5);
+        if(env) {
+            sprintf(env, "%s=%s%c%s", 
+                    PATH_ENV_VARIABLE, 
+                    libpath, 
+                    PATH_SEPERATOR, 
+                    current);
+        }
+    } else {
+        env = jk_pool_alloc(p, strlen(PATH_ENV_VARIABLE) +                               
+                               strlen(libpath) + 5);
+        if(env) {
+            sprintf(env, "%s=%s", PATH_ENV_VARIABLE, libpath);
+        }
+    }
+
+    if(env) {
+        putenv(env);
+    }
+}
+
+void jk_init_ws_service(jk_ws_service_t *s)
+{
+    s->ws_private           = NULL;
+    s->pool                 = NULL;
+    s->method               = NULL;
+    s->protocol             = NULL;
+    s->req_uri              = NULL;
+    s->remote_addr          = NULL;
+    s->remote_host          = NULL;
+    s->remote_user          = NULL;
+    s->auth_type            = NULL;
+    s->query_string         = NULL;
+    s->server_name          = NULL;
+    s->server_port          = 80;
+    s->server_software      = NULL;
+    s->content_length       = 0;
+    s->is_chunked           = 0;
+    s->no_more_chunks       = 0;
+    s->content_read         = 0;
+    s->is_ssl               = JK_FALSE;
+    s->ssl_cert             = NULL;
+    s->ssl_cert_len         = 0;
+    s->ssl_cipher           = NULL;
+    s->ssl_session          = NULL;
+    s->headers_names        = NULL;
+    s->headers_values       = NULL;
+    s->num_headers          = 0;
+    s->attributes_names     = NULL;
+    s->attributes_values    = NULL;
+    s->num_attributes       = 0;
+    s->jvm_route            = NULL;
+}
diff --git a/connectors/jk/native/common/jk_util.h b/connectors/jk/native/common/jk_util.h
new file mode 100644
index 0000000..752d4f2
--- /dev/null
+++ b/connectors/jk/native/common/jk_util.h
@@ -0,0 +1,205 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Various utility functions                                  *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+#ifndef _JK_UTIL_H
+#define _JK_UTIL_H
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_logger.h"
+#include "jk_service.h"
+
+int jk_parse_log_level(const char *level);
+
+int jk_open_file_logger(jk_logger_t **l,
+                        const char *file,
+                        int level);
+
+int jk_close_file_logger(jk_logger_t **l);
+
+int jk_log(jk_logger_t *l,
+           const char *file,
+           int line,
+           int level,
+           const char *fmt, ...);
+
+/* [V] Two general purpose functions. Should ease the function bloat. */
+int jk_get_worker_str_prop(jk_map_t *m,
+                           const char *wname,
+                           const char *pname,
+                           char **prop);
+
+int jk_get_worker_int_prop(jk_map_t *m,
+                           const char *wname,
+                           const char *pname,
+                           int *prop);
+
+char *jk_get_worker_host(jk_map_t *m,
+                         const char *wname,
+                         const char *def);
+
+char *jk_get_worker_type(jk_map_t *m,
+                         const char *wname);
+
+int jk_get_worker_port(jk_map_t *m,
+                       const char *wname,
+                       int def);
+
+int jk_get_worker_cache_size(jk_map_t *m, 
+                             const char *wname,
+                             int def);
+
+int jk_get_worker_socket_timeout(jk_map_t *m,
+                             const char *wname,
+                             int def);
+
+int jk_get_worker_socket_keepalive(jk_map_t *m,
+                             const char *wname,
+                             int def);
+
+int jk_get_worker_cache_timeout(jk_map_t *m,
+                             const char *wname,
+                             int def);
+
+int jk_get_worker_recovery_opts(jk_map_t *m,
+                             const char *wname,
+                             int def);
+
+int jk_get_worker_connect_timeout(jk_map_t *m,
+                                  const char *wname,
+                                  int def);
+
+int jk_get_worker_reply_timeout(jk_map_t *m,
+                                const char *wname,
+                                 int def);
+
+int jk_get_worker_prepost_timeout(jk_map_t *m,
+                           const char *wname,
+                           int def);
+
+char * jk_get_worker_secret_key(jk_map_t *m,
+                                const char *wname);
+
+void jk_set_log_format(const char *logformat);
+
+int jk_get_worker_list(jk_map_t *m,
+                       char ***list,
+                       unsigned *num_of_wokers);
+
+double jk_get_lb_factor(jk_map_t *m, 
+                        const char *wname);
+
+int jk_get_is_sticky_session(jk_map_t *m,
+                           const char *wname);
+
+int jk_get_is_local_worker(jk_map_t *m,
+                           const char *wname);
+
+int jk_get_local_worker_only_flag(jk_map_t *m,
+		                  const char *lb_wname);
+
+int jk_get_lb_worker_list(jk_map_t *m, 
+                          const char *lb_wname,
+                          char ***list, 
+                          unsigned *num_of_wokers);
+
+char *jk_get_worker_secret(jk_map_t *m,
+                           const char *wname);
+
+int jk_get_worker_mx(jk_map_t *m, 
+                     const char *wname,
+                     unsigned *mx);
+
+int jk_get_worker_ms(jk_map_t *m, 
+                     const char *wname,
+                     unsigned *ms);
+
+int jk_get_worker_classpath(jk_map_t *m, 
+                            const char *wname, 
+                            char **cp);
+
+
+int jk_get_worker_bridge_type(jk_map_t *m, 
+                              const char *wname,
+                              unsigned *bt);
+                              
+int jk_get_worker_jvm_path(jk_map_t *m, 
+                           const char *wname, 
+                           char **vm_path);
+
+int jk_get_worker_callback_dll(jk_map_t *m, 
+                               const char *wname, 
+                               char **cb_path);
+
+int jk_get_worker_cmd_line(jk_map_t *m, 
+                           const char *wname, 
+                           char **cmd_line);
+
+int jk_file_exists(const char *f);
+
+int jk_is_path_poperty(const char *prp_name);
+
+int jk_is_cmd_line_poperty(const char *prp_name);
+
+int jk_get_worker_stdout(jk_map_t *m, 
+                         const char *wname, 
+                         char **stdout_name);
+
+int jk_get_worker_stderr(jk_map_t *m, 
+                         const char *wname, 
+                         char **stderr_name);
+
+int jk_get_worker_sysprops(jk_map_t *m, 
+                           const char *wname, 
+                           char **sysprops);
+
+int jk_get_worker_libpath(jk_map_t *m, 
+                          const char *wname, 
+                          char **libpath);
+
+char **jk_parse_sysprops(jk_pool_t *p, 
+                         const char *sysprops);
+        
+
+void jk_append_libpath(jk_pool_t *p, 
+                       const char *libpath);
+
+void jk_init_ws_service(jk_ws_service_t *s);
+
+
+#define	TC32_BRIDGE_TYPE	32
+#define	TC33_BRIDGE_TYPE	33
+#define	TC40_BRIDGE_TYPE	40
+#define	TC41_BRIDGE_TYPE	41
+#define	TC50_BRIDGE_TYPE	50
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _JK_UTIL_H */
diff --git a/connectors/jk/native/common/jk_version.h b/connectors/jk/native/common/jk_version.h
new file mode 100644
index 0000000..43ed188
--- /dev/null
+++ b/connectors/jk/native/common/jk_version.h
@@ -0,0 +1,63 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: JK version header file                                     *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef __JK_VERSION_H
+#define __JK_VERSION_H
+
+/************** START OF AREA TO MODIFY BEFORE RELEASING *************/
+#define JK_VERMAJOR     1
+#define JK_VERMINOR     2
+#define JK_VERFIX       7 
+#define JK_VERSTRING    "1.2.7"
+
+/* Beta number */
+#define JK_VERBETA      0
+#define JK_BETASTRING   "1"
+/* set JK_VERISRELEASE to 1 when release (do not forget to commit!) */
+#define JK_VERISRELEASE 0
+/************** END OF AREA TO MODIFY BEFORE RELEASING *************/
+
+#if !defined(PACKAGE)
+#define PACKAGE "mod_jk"
+#endif
+
+/* Build JK_EXPOSED_VERSION and JK_VERSION */
+#define JK_EXPOSED_VERSION_INT PACKAGE "/" JK_VERSTRING
+
+#if ( JK_VERISRELEASE == 1 )
+  #define JK_RELEASE_STR  JK_EXPOSED_VERSION_INT
+#else
+  #define JK_RELEASE_STR  JK_EXPOSED_VERSION_INT "-dev"
+#endif
+
+#if ( JK_VERBETA == 0 )
+    #define JK_EXPOSED_VERSION JK_RELEASE_STR
+    #undef JK_VERBETA
+    #define JK_VERBETA 255
+#else
+    #define JK_EXPOSED_VERSION JK_RELEASE_STR "-beta-" JK_BETASTRING
+#endif
+
+#define JK_MAKEVERSION(major, minor, fix, beta) (((major) << 24) + ((minor) << 16) + ((fix) << 8) + (beta))
+
+#define JK_VERSION JK_MAKEVERSION(JK_VERMAJOR, JK_VERMINOR, JK_VERFIX, JK_VERBETA)
+
+#endif /* __JK_VERSION_H */
diff --git a/connectors/jk/native/common/jk_worker.c b/connectors/jk/native/common/jk_worker.c
new file mode 100644
index 0000000..5693335
--- /dev/null
+++ b/connectors/jk/native/common/jk_worker.c
@@ -0,0 +1,224 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Workers controller                                         *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+#define _PLACE_WORKER_LIST_HERE
+#include "jk_worker_list.h"
+#include "jk_worker.h"
+#include "jk_util.h"
+
+static void close_workers(jk_logger_t *l);
+
+static worker_factory get_factory_for(char *type);
+
+static int build_worker_map(jk_map_t *init_data, 
+                            char **worker_list, 
+                            unsigned num_of_workers,
+                            jk_worker_env_t *we,
+                            jk_logger_t *l);
+
+int wc_open(jk_map_t *init_data,
+		    jk_worker_env_t *we,
+            jk_logger_t *l)
+{
+    char **worker_list  = NULL;
+    unsigned num_of_workers = 0;
+
+    jk_log(l, JK_LOG_DEBUG, "Into wc_open\n"); 
+
+    if(!map_alloc(&worker_map)) {
+        return JK_FALSE;
+    }
+    
+    if(!jk_get_worker_list(init_data, 
+                           &worker_list, 
+                           &num_of_workers)) {
+        return JK_FALSE;
+    }
+    
+    if(!build_worker_map(init_data, 
+                         worker_list, 
+                         num_of_workers,
+                         we,
+                         l)) {
+        close_workers(l);
+        return JK_FALSE;
+    }
+
+    we->num_of_workers=num_of_workers;
+    we->first_worker=worker_list[0];
+    jk_log(l, JK_LOG_DEBUG, "wc_open, done %d\n", num_of_workers); 
+    return JK_TRUE;
+}
+
+
+void wc_close(jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, "Into wc_close\n"); 
+    close_workers(l);
+    jk_log(l, JK_LOG_DEBUG, "wc_close, done\n"); 
+}
+
+jk_worker_t *wc_get_worker_for_name(const char *name, 
+                                    jk_logger_t *l)
+{
+    jk_worker_t * rc;
+
+    if(!name) {
+        jk_log(l, JK_LOG_ERROR, "wc_get_worker_for_name NULL name\n"); 
+    }
+
+    jk_log(l, JK_LOG_DEBUG, "Into wc_get_worker_for_name %s\n", name); 
+
+    rc = map_get(worker_map, name, NULL);
+
+    jk_log(l, JK_LOG_DEBUG, "wc_get_worker_for_name, done %s a worker\n", 
+        rc ? "found" : "did not find"); 
+    return rc;
+}
+
+
+int wc_create_worker(const char *name, 
+                     jk_map_t *init_data,
+                     jk_worker_t **rc,
+                     jk_worker_env_t *we,
+                     jk_logger_t *l)
+{
+    jk_log(l, JK_LOG_DEBUG, "Into wc_create_worker\n"); 
+
+    if(rc) {
+        char *type = jk_get_worker_type(init_data, name);
+        worker_factory fac = get_factory_for(type);
+        jk_worker_t *w = NULL;
+
+        *rc = NULL;
+
+        if(!fac) {
+            jk_log(l, JK_LOG_ERROR, "wc_create_worker NULL factory for %s\n", type); 
+            return JK_FALSE;
+        }
+
+        jk_log(l, JK_LOG_DEBUG, "wc_create_worker, about to create instance %s of %s\n", 
+               name, type);         
+
+        if(!fac(&w, name, l) || !w) {
+            jk_log(l, JK_LOG_ERROR, "wc_create_worker factory for %s failed for %s\n", 
+                   type, name); 
+            return JK_FALSE;
+        }
+        
+        jk_log(l, JK_LOG_DEBUG, "wc_create_worker, about to validate and init %s\n", name);         
+        if(!w->validate(w, init_data, we, l)) {
+            w->destroy(&w, l);
+            jk_log(l, JK_LOG_ERROR, "wc_create_worker validate failed for %s\n", 
+                   name); 
+            return JK_FALSE;
+        }
+    
+        if(!w->init(w, init_data, we, l)) {
+            w->destroy(&w, l);
+            jk_log(l, JK_LOG_ERROR, "wc_create_worker init failed for %s\n", 
+                   name); 
+            return JK_FALSE;
+        }
+
+        *rc = w;
+        jk_log(l, JK_LOG_DEBUG, "wc_create_worker, done\n"); 
+        return JK_TRUE;
+    }
+
+    jk_log(l, JK_LOG_ERROR, "wc_create_worker, NUll input\n"); 
+    return JK_FALSE;
+}
+
+static void close_workers(jk_logger_t *l)
+{
+    int sz = map_size(worker_map);
+
+	jk_log(l, JK_LOG_DEBUG, "close_workers got %d workers to destroy\n", sz);
+
+    if(sz > 0) {
+        int i;
+        for(i = 0 ; i < sz ; i++) {
+            jk_worker_t *w = map_value_at(worker_map, i);
+            if(w) {
+				jk_log(l, JK_LOG_DEBUG, "close_workers will destroy worker %s\n", map_name_at(worker_map, i));
+                w->destroy(&w, l);
+            }
+        }
+    }
+    map_free(&worker_map);
+}
+
+static int build_worker_map(jk_map_t *init_data, 
+                            char **worker_list, 
+                            unsigned num_of_workers,
+                            jk_worker_env_t *we,
+                            jk_logger_t *l)
+{
+    unsigned i;
+
+    jk_log(l, JK_LOG_DEBUG, 
+           "Into build_worker_map, creating %d workers\n", num_of_workers); 
+
+    for(i = 0 ; i < num_of_workers ; i++) {
+        jk_worker_t *w = NULL;
+
+        jk_log(l, JK_LOG_DEBUG, 
+               "build_worker_map, creating worker %s\n", worker_list[i]); 
+
+        if(wc_create_worker(worker_list[i], init_data, &w, we, l)) {
+            jk_worker_t *oldw = NULL;
+            if(!map_put(worker_map, worker_list[i], w, (void *)&oldw)) {
+                w->destroy(&w, l);
+                return JK_FALSE;
+            }
+
+            jk_log(l, JK_LOG_DEBUG, 
+                   "build_worker_map, removing old %s worker \n", worker_list[i]); 
+            if(oldw) {
+                oldw->destroy(&oldw, l);
+            }
+        } else {        
+            jk_log(l, JK_LOG_ERROR, "build_worker_map failed to create worker%s\n", 
+                   worker_list[i]); 
+            return JK_FALSE;
+        }
+    }
+
+    jk_log(l, JK_LOG_DEBUG, "build_worker_map, done\n"); 
+    return JK_TRUE;
+}
+
+static worker_factory get_factory_for(char *type)
+{
+    worker_factory_record_t *factory = &worker_factories[0];
+    while(factory->name) {
+        if(0 == strcmp(factory->name, type)) {
+            return factory->fac;
+        }
+
+        factory ++;
+    }
+
+    return NULL;
+}
diff --git a/connectors/jk/native/common/jk_worker.h b/connectors/jk/native/common/jk_worker.h
new file mode 100644
index 0000000..e9406f1
--- /dev/null
+++ b/connectors/jk/native/common/jk_worker.h
@@ -0,0 +1,55 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Workers controller header file                             *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           * 
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef JK_WORKER_H
+#define JK_WORKER_H
+
+#include "jk_logger.h"
+#include "jk_service.h"
+#include "jk_map.h"
+#include "jk_uri_worker_map.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+int wc_open(jk_map_t *init_data,
+			jk_worker_env_t *we,
+            jk_logger_t *l);
+
+void wc_close(jk_logger_t *l);
+
+jk_worker_t *wc_get_worker_for_name(const char *name, 
+                                    jk_logger_t *l);
+
+int wc_create_worker(const char *name, 
+                     jk_map_t *init_data,
+                     jk_worker_t **rc,
+                     jk_worker_env_t *we,
+                     jk_logger_t *l);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* JK_WORKER_H */
diff --git a/connectors/jk/native/common/jk_worker_list.h b/connectors/jk/native/common/jk_worker_list.h
new file mode 100644
index 0000000..6a497f8
--- /dev/null
+++ b/connectors/jk/native/common/jk_worker_list.h
@@ -0,0 +1,91 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Worker list                                                *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+/*
+ * This file includes a list of all the possible workers in the jk library
+ * plus their factories. 
+ *
+ * If you want to add a worker just place it in the worker_factories array
+ * with its unique name and factory.
+ *
+ * If you want to remove a worker, hjust comment out its line in the 
+ * worker_factories array as well as its header file. For example, look
+ * at what we have done to the ajp23 worker.
+ *
+ * Note: This file should be included only in the jk_worker controller.
+ * Currently the jk_worker controller is located in jk_worker.c
+ */
+#ifdef _PLACE_WORKER_LIST_HERE
+    #ifndef _JK_WORKER_LIST_H
+    #define _JK_WORKER_LIST_H
+
+        #include "jk_ajp12_worker.h"
+        #include "jk_ajp13_worker.h"
+        #include "jk_ajp14_worker.h"
+        #ifdef HAVE_JNI
+            #include "jk_jni_worker.h"
+        #endif
+        #include "jk_lb_worker.h"
+
+        struct worker_factory_record {
+            const char *name;
+            worker_factory fac;
+        };
+        typedef struct worker_factory_record worker_factory_record_t;
+
+        static jk_map_t *worker_map;
+
+        static worker_factory_record_t worker_factories[] = {
+            /*
+             * AJPv12 worker, this is the stable worker.
+             */
+            { JK_AJP12_WORKER_NAME, ajp12_worker_factory},
+            /*
+             * AJPv13 worker, fast bi-directional worker.
+             */
+            { JK_AJP13_WORKER_NAME, ajp13_worker_factory},
+            /*
+             * AJPv14 worker, next generation fast bi-directional worker.
+             */
+            { JK_AJP14_WORKER_NAME, ajp14_worker_factory},
+            /*
+             * In process JNI based worker. Requires the server to be 
+             * multithreaded and to use native threads.
+             */
+            #ifdef HAVE_JNI
+                { JK_JNI_WORKER_NAME, jni_worker_factory},
+            #endif
+            /*
+             * Load balancing worker. Performs round robin with sticky 
+             * session load balancing.
+             */
+            { JK_LB_WORKER_NAME, lb_worker_factory},
+
+            /*
+             * Marks the end of the worker factory list.
+             */
+            { NULL, NULL}
+    };
+    #endif /* _JK_WORKER_LIST_H */
+#endif /* _PLACE_WORKER_LIST_HERE */
+
diff --git a/connectors/jk/native/common/list.mk.in b/connectors/jk/native/common/list.mk.in
new file mode 100644
index 0000000..8309624
--- /dev/null
+++ b/connectors/jk/native/common/list.mk.in
@@ -0,0 +1,10 @@
+## Object needed for mod_jk for Apache-1.3
+APACHE_OBJECTS= ${JK}jk_ajp12_worker${OEXT} ${JK}jk_connect${OEXT} \
+                ${JK}jk_msg_buff${OEXT} ${JK}jk_util${OEXT} \
+                ${JK}jk_ajp13${OEXT} ${JK}jk_pool${OEXT} \
+                ${JK}jk_worker${OEXT} ${JK}jk_ajp13_worker${OEXT} \
+                ${JK}jk_lb_worker${OEXT} ${JK}jk_sockbuf${OEXT} \
+                ${JK}jk_map${OEXT} ${JK}jk_uri_worker_map${OEXT} \
+                ${JK}jk_ajp14${OEXT} ${JK}jk_ajp14_worker${OEXT} \
+                ${JK}jk_md5${OEXT} @JK_JNI_WORKER@ \
+                ${JK}jk_ajp_common${OEXT} ${JK}jk_context${OEXT}
diff --git a/connectors/jk/native/common/portable.h b/connectors/jk/native/common/portable.h
new file mode 100644
index 0000000..9100e1a
--- /dev/null
+++ b/connectors/jk/native/common/portable.h
@@ -0,0 +1 @@
+/* On most platform this file is overwritten when doing configure */
diff --git a/connectors/jk/native/configure.in b/connectors/jk/native/configure.in
new file mode 100755
index 0000000..560e1d5
--- /dev/null
+++ b/connectors/jk/native/configure.in
@@ -0,0 +1,465 @@
+dnl
+dnl Process this file with autoconf to produce a configure script
+dnl
+AC_REVISION($Id$)dnl
+
+AC_PREREQ(2.13)
+AC_INIT(common/jk_worker.h)
+AC_CONFIG_HEADER(common/portable.h)
+AC_CONFIG_AUX_DIR(scripts/build/unix)
+AC_CANONICAL_SYSTEM
+
+dnl package and version. (synchronization with common/jk_version.h ?)
+PACKAGE=mod_jk
+VERSION=1.2.7
+
+AM_INIT_AUTOMAKE(${PACKAGE}, ${VERSION})
+
+AC_PROG_CC
+AC_PROG_LD
+
+dnl Not sure what it does, but the libtool manual seems to require this
+dnl It should use the native platform dlopen ( if available )
+AC_LIBTOOL_DLOPEN
+
+dnl AM_PROG_LIBTOOL often causes problems.
+dnl I have solved them once using aclocal --acdir=/usr/local/share/aclocal/
+AM_PROG_LIBTOOL
+
+AC_PATH_PROG(TEST,test,$PATH)dnl
+AC_SUBST(TEST)
+
+AC_PATH_PROG(RM,rm,$PATH)dnl
+AC_SUBST(RM)
+
+AC_PATH_PROG(GREP,grep,$PATH)dnl
+AC_SUBST(GREP)
+
+AC_PATH_PROG(ECHO,echo,echo,$PATH)dnl
+AC_SUBST(ECHO)
+
+AC_PATH_PROG(SED,sed,$PATH)dnl
+AC_SUBST(SED)
+
+AC_PATH_PROG(CP,cp,$PATH)dnl
+AC_SUBST(CP)
+
+AC_PATH_PROG(MKDIR,mkdir,$PATH)dnl
+AC_SUBST(MKDIR)
+
+dnl AC_PATH_PROG(LIBTOOL,libtool,$PATH)dnl
+AC_SUBST(LIBTOOL)
+
+dnl check for snprintf and vsnprintf.
+AC_CHECK_FUNC(snprintf, AC_DEFINE(HAVE_SNPRINTF,1,[Have snprintf()]))
+
+AC_CHECK_FUNC(vsnprintf, AC_DEFINE(HAVE_VSNPRINTF,1,[Have vsnprintf()]))
+
+APACHE_CONFIG_VARS=`pwd`/scripts/build/config_vars.mk
+WEBSERVER=""
+apache_dir=""
+apache_include=""
+APXS="apxs"
+AC_ARG_WITH(apxs,
+[  --with-apxs[=FILE]      Build shared Apache module. FILE is the optional
+                        pathname to the apxs tool; defaults to finding
+			apxs in your PATH.],
+[
+    case "${withval}" in 
+        y | yes | true) find_apxs=true ;;
+        n | no | false) find_apxs=false ;;
+        *) find_apxs=false ;;
+    esac
+
+    if ${TEST} ${find_apxs} ; then    
+        AC_MSG_RESULT([need to check for Perl first, apxs depends on it...])
+        AC_PATH_PROG(PERL,perl,$PATH)dnl
+    
+        if ${TEST} ${find_apxs} ; then
+            APXS=${withval}
+        else
+            AC_PATH_PROG(APXS,apxs,$PATH)dnl
+        fi
+    
+        if ${TEST} -n "${APXS}" ; then
+            dnl Seems that we have it, but have to check if it is OK first        
+            if ${TEST} ! -x "${APXS}" ; then
+                AC_MSG_ERROR(Invalid location for apxs: '${APXS}')
+            fi
+            
+            ${APXS} -q PREFIX >/dev/null 2>/dev/null || apxs_support=false
+    
+            if ${TEST} "${apxs_support}" = "false" ; then
+                AC_MSG_RESULT(could not find ${APXS})
+                AC_MSG_ERROR(You must specify a valid --with-apxs path)
+            fi
+
+            dnl apache_dir and apache_include are also needed.
+            apache_dir=`$APXS -q PREFIX`
+            apache_include="-I`$APXS -q INCLUDEDIR`"
+
+            dnl test apache version
+            APA=`${GREP} STANDARD20 ${APXS}`
+            if ${TEST} -z "$APA" ; then
+                WEBSERVER="apache-1.3"
+		APXSCFLAGS=`$APXS -q CFLAGS`
+		APXS_CPPFLAGS=""
+            else
+                WEBSERVER="apache-2.0"
+              	APRINCLUDEDIR="-I`$APXS -q APR_INCLUDEDIR`"
+              	APXSCFLAGS="`${APXS} -q CFLAGS` `${APXS} -q EXTRA_CFLAGS` -DHAVE_APR ${APRINCLUDEDIR}"
+              	APXSCPPFLAGS="`${APXS} -q EXTRA_CPPFLAGS`"
+                APACHE_CONFIG_VARS="`${APXS} -q exp_installbuilddir`/config_vars.mk"
+                LIBTOOL=`$APXS -q LIBTOOL`
+            fi
+            AC_MSG_RESULT([building connector for \"$WEBSERVER\"])
+    
+            AC_SUBST(APXS)
+        fi
+    fi
+],
+[
+	AC_MSG_RESULT(no apxs given)
+])
+AC_SUBST(APACHE_CONFIG_VARS)
+
+dnl Apache-2.0 needs the os subdirectory to include os.h
+dnl this include is copy from os/config.m4
+sinclude(../../common/build/os_apache.m4)
+
+dnl it is copied from the configure of JServ ;=)
+dnl and adapted. 
+
+apache_dir_is_src="false"
+AC_ARG_WITH(apache,
+[  --with-apache=DIR      Build static Apache module. DIR is the pathname 
+                        to the Apache source directory.],
+[
+    if ${TEST} ! -z "$WEBSERVER" ; then
+        AC_MSG_ERROR([Sorry cannot use --with-apxs=${APXS} and --with-apache=${withval} togother, please choose one of both])
+    fi
+
+    AC_MSG_CHECKING([for Apache source directory (assume static build)])
+
+    if ${TEST} -n "${withval}" && ${TEST} -d "${withval}" ; then
+        if ${TEST} -d "${withval}/src" ; then
+           # handle the case where people use relative paths to 
+           # the apache source directory by pre-pending the current
+           # build directory to the path. there are probably 
+           # errors with this if configure is run while in a 
+           # different directory than what you are in at the time
+           if ${TEST} -n "`${ECHO} ${withval}|${GREP} \"^\.\.\"`" ; then
+               withval=`pwd`/${withval}
+           fi
+
+           apache_dir=${withval}
+           apache_dir_is_src="true"
+           AC_MSG_RESULT(${apache_dir})
+        
+           AC_MSG_CHECKING(for Apache include directory)
+
+           if ${TEST} -d "${withval}/src/include" ; then
+               # read osdir from the existing apache.
+               osdir=`${GREP} '^OSDIR=' ${withval}/src/Makefile.config | ${SED} -e 's:^OSDIR=.*/os:os:'`
+               if ${TEST} -z "${osdir}" ; then
+                   osdir=os/unix
+               fi
+               apache_include="-I${withval}/src/include \
+                   -I${withval}/src/${osdir}"
+               WEBSERVER="apache-1.3"
+               LIB_JK_TYPE=mod_jk.a
+               AC_MSG_RESULT([${apache_include}, version 1.3])
+           else
+               AC_MSG_ERROR([Sorry Apache 1.2.x is no longer supported.])
+           fi
+        else
+           if ${TEST} -d "${withval}/include" ; then
+              # osdir for Apache20.
+              WEBSERVER="apache-2.0"
+              apache_dir=${withval}
+              apache_dir_is_src="true"
+              LIB_JK_TYPE=lib_jk.la
+              apache_include="-I${withval}/include -I${withval}/srclib/apr/include -I${withval}/os/${OS_APACHE_DIR} -I${withval}/srclib/apr-util/include"
+              AC_MSG_RESULT(${apache_dir})
+           fi
+        fi
+    fi
+
+    dnl Make sure we have a result.
+    if ${TEST} -z "$WEBSERVER" ; then
+        AC_MSG_ERROR([Directory $apache_dir is not a valid Apache source distribution])
+    fi
+
+# VT: Now, which one I'm supposed to use? Let's figure it out later
+
+    configure_apache=true
+    configure_src=true
+    
+    AC_MSG_RESULT([building connector for \"$WEBSERVER\"])
+],
+[
+	AC_MSG_RESULT(no apache given)
+])
+AC_SUBST(apache_include)
+APACHE_DIR=${apache_dir}
+AC_SUBST(APACHE_DIR)
+
+dnl Check for enable-jni
+JK_JNI_WORKER=""
+AC_ARG_ENABLE(jni,
+[  --enable-jni     Build jni_connect.so and enable jni_worker.],
+[
+    AC_MSG_RESULT(jni enable (need JDK))
+    CFLAGS="${CFLAGS} -DHAVE_JNI"
+    JK_JNI_WORKER="\${JK}jk_jni_worker\${OEXT}"
+])dnl
+AC_SUBST(JK_JNI_WORKER)
+
+dnl CFLAGS for EAPI mod_ssl (Apache 1.3)
+dnl it also allows the CFLAGS environment variable.
+CFLAGS="${CFLAGS}"
+AC_ARG_ENABLE(
+EAPI,
+[  --enable-EAPI           Enable EAPI support (mod_ssl, Apache 1.3)],
+[
+case "${enableval}" in
+    y | Y | YES | yes | TRUE | true )
+        CFLAGS="${CFLAGS} -DEAPI"
+        AC_MSG_RESULT([...Enabling EAPI Support...])
+        ;;
+esac
+])
+AC_SUBST(CFLAGS)
+
+dnl the APXSCFLAGS is given by apxs to the C compiler
+dnl the APXSLDFLAGS is given to the linker (for APRVARS).
+dnl APXSLDFLAGS=""
+dnl APXSCFLAGS=""
+if ${TEST} -n "${CFLAGS}" ; then
+	APXSCFLAGS="${APXSCFLAGS} ${CFLAGS}"
+fi
+dnl the APXSLDFLAGS is normaly empty but APXSCFLAGS is not.
+if ${TEST} -n "${LDFLAGS}" ; then
+	APXSLDFLAGS="-Wl,${LDFLAGS}"
+fi
+AC_SUBST(APXSCFLAGS)
+AC_SUBST(APXSCPPFLAGS)
+AC_SUBST(APXSLDFLAGS)
+
+if ${TEST} -n "${JK_JNI_WORKER}" ; then
+                       
+WEBSERVER="jni ${WEBSERVER}"
+ 
+dnl Find the JDK
+dnl Results go in JAVA_HOME
+dnl Also sets JAVA_PLATFORM to 1 for 1.1 and to 2 for 1.2
+
+AC_MSG_CHECKING([for JDK location (please wait)])
+
+dnl The order is: --with-java-home first, environment second, guessed value third.
+    
+dnl This is a safe default. Could screw up on the security features, but
+dnl oh well, this is what --with-java2 is for.
+    
+if ${TEST} -n "${JAVA_HOME}" ; then
+	JAVA_HOME_ENV="${JAVA_HOME}"
+else
+	JAVA_HOME_ENV=""
+fi
+JAVA_HOME=""
+JAVA_PLATFORM="1"
+
+AC_ARG_WITH(java-home,
+[  --with-java-home=DIR     Where is your JDK root directory.],
+[
+    
+    # This stuff works if the command line parameter --with-java-home was
+    # specified, so it takes priority rightfully.
+    
+    JAVA_HOME=${withval}
+	
+    if ${TEST} ! -d "${JAVA_HOME}" ; then
+        AC_MSG_ERROR(Not a directory: ${JAVA_HOME})
+    fi
+	
+    AC_MSG_RESULT(${JAVA_HOME})
+	
+],
+[
+    # This works if the parameter was NOT specified, so it's a good time
+    # to see what the enviroment says.
+        
+    # Since Sun uses JAVA_HOME a lot, we check it first and ignore the
+    # JAVA_HOME, otherwise just use whatever JAVA_HOME was specified.
+        
+    if ${TEST} -n "${JAVA_HOME_ENV}" ; then
+            
+        JAVA_HOME=${JAVA_HOME_ENV}
+        AC_MSG_RESULT(${JAVA_HOME_ENV} from environment)
+    fi
+])
+
+if ${TEST} -z "${JAVA_HOME}" ; then
+    
+    # Oh well, nobody set neither JAVA_HOME nor JAVA_HOME, have to guess
+        
+    # The following code is based on the code submitted by Henner Zeller
+    # for ${srcdir}/src/scripts/package/rpm/ApacheJServ.spec
+        
+    # Two variables will be set as a result:
+    #
+    # JAVA_HOME
+    # JAVA_PLATFORM
+    AC_MSG_CHECKING([Try to guess JDK location])
+        
+        
+    for JAVA_PREFIX in \
+	    /usr/local \
+	    /usr/local/lib \
+    	    /usr \
+    	    /usr/lib \
+            /opt  \
+    	    / 
+    do
+        for JAVA_PLATFORM in 3 2 1 ;
+        do
+            
+            for subversion in .9 .8 .7 .6 .5 .4 .3 .2 .1 "" ;
+                do
+                    for VARIANT in IBMJava2- java java- jdk jdk-;
+                    do
+                        GUESS="${JAVA_PREFIX}/${VARIANT}1.${JAVA_PLATFORM}${subversion}"
+dnl                        AC_MSG_CHECKING([${GUESS}])
+                        if ${TEST} -d "${GUESS}/bin" \
+                        && ${TEST} -d "${GUESS}/include" ; then
+                        
+                            JAVA_HOME="${GUESS}"
+                            AC_MSG_RESULT([${GUESS}])
+                            break
+                        fi
+
+                    done
+
+                    if ${TEST} -n "${JAVA_HOME}" ; then
+                        break;
+                    fi
+
+                done
+
+                if ${TEST} -n "${JAVA_HOME}" ; then
+                    break;
+                fi
+
+            done
+
+            if ${TEST} -n "${JAVA_HOME}" ; then
+                break;
+            fi
+
+        done
+
+        if ${TEST} -n "${JAVA_HOME}" ; then
+
+            dnl Just to have the messages looking uniformly
+
+            AC_MSG_CHECKING(Java platform)
+            AC_MSG_RESULT([guess ${JAVA_PLATFORM}])
+        fi                                                                      
+
+        
+else
+    
+        AC_MSG_CHECKING(Java platform)
+
+        AC_ARG_WITH(java-platform,
+        [  --with-java-platform=VAL Force the Java platorm
+                          (value is 1 for 1.1.x or 2 for 1.2.x or greater)],
+        [
+            dnl This requires a bit of tweaking to be handled properly, but
+            dnl the default is good enough
+            
+            JAVA_PLATFORM="2"
+        ])
+
+        AC_MSG_RESULT(forced Java ${JAVA_PLATFORM})
+
+fi
+dnl end of JServ ;=)
+
+dnl test if --enable-jni give but not valid JAVA_HOME
+if ${TEST} -z "${JAVA_HOME}" ; then
+    AC_MSG_ERROR([JDK home not found, please specify one with --with-java-home option (run ./configure --help for more options)])
+fi
+
+dnl guess OS = OS_TYPE for jni_md.h
+OS=""
+AC_ARG_WITH(os-type,
+[  --with-os-type[=SUBDIR]     Where is your JDK os-type subdirectory.],
+[
+    OS=${withval}
+	
+    if ${TEST} ! -d "${JAVA_HOME}/${OS}" ; then
+        AC_MSG_ERROR(Not a directory: ${JAVA_HOME}/${OS})
+    fi
+],
+[
+    AC_MSG_CHECKING(os_type directory)
+    if ${TEST} -f ${JAVA_HOME}/include/jni_md.h; then
+    	OS=""
+    else
+        for f in ${JAVA_HOME}/include/*/jni_md.h; do
+            if ${TEST} -f $f; then
+                OS=`dirname ${f}`
+                OS=`basename ${OS}`
+                echo " ${OS}"
+            fi
+        done
+        if ${TEST} -z "${OS}"; then
+            AC_MSG_RESULT(Cannot find jni_md.h in ${JAVA_HOME}/${OS})
+            AC_MSG_ERROR(You should retry --with-os-type=SUBDIR)
+        fi
+    fi
+])
+fi
+AC_SUBST(JAVA_HOME)
+AC_SUBST(OS)
+
+
+dnl Check that  a WEBSERVER has been given
+if ${TEST} -z "$WEBSERVER" ; then
+	AC_MSG_ERROR(Cannot find the WebServer)
+fi
+
+dnl Add common to subdir list
+WEBSERVER="common ${WEBSERVER}"
+
+AC_SUBST(WEBSERVER)
+
+AM_CONDITIONAL(MAKE_DYNAMIC_APACHE, ${TEST} "${apache_dir_is_src}" = "false")
+
+if ${TEST} "${apache_dir_is_src}" = "false" ; then
+dnl normal apxs handling
+	APACHE20_OEXT=.c
+	LIB_JK_TYPE=mod_jk.so
+	INSTALL_TYPE=install_dynamic
+else
+dnl install static library in apache sources.
+	APACHE20_OEXT=.lo
+	INSTALL_TYPE=install_static
+fi
+AC_SUBST(APACHE20_OEXT)
+AC_SUBST(LIB_JK_TYPE)
+AC_SUBST(INSTALL_TYPE)
+
+dnl automake needs the path it does not work with $WEBSERVER
+dnl that why useless Makefiles are build.
+AC_OUTPUT([
+	Makefile
+	apache-1.3/Makefile
+	apache-1.3/Makefile.apxs
+	apache-2.0/Makefile
+	apache-2.0/Makefile.apxs
+	common/Makefile
+	common/list.mk
+	jni/Makefile
+	])
diff --git a/connectors/jk/native/docs/api/README.txt b/connectors/jk/native/docs/api/README.txt
new file mode 100644
index 0000000..e7a747c
--- /dev/null
+++ b/connectors/jk/native/docs/api/README.txt
@@ -0,0 +1,3 @@
+To generate the API documentation for the mod_jk library, simply invoke
+  make apidocs
+from the mod_jk Library root source path. (jakarta-tomcat-connectors/jk/native)
diff --git a/connectors/jk/native/domino/HISTORY b/connectors/jk/native/domino/HISTORY
new file mode 100644
index 0000000..3a788ba
--- /dev/null
+++ b/connectors/jk/native/domino/HISTORY
@@ -0,0 +1,22 @@
+$Log$
+Revision 1.2  2001/06/14 14:36:08  andya
+Better SSL support, better ajp14 support
+
+
+--- pre cvs log ---
+
+20010608 1.0.2 AA
+
+Banished malloc()/free() in favour of jk_pool based memory allocation for configuration data. Added syntax checking and test harness to inifile code.
+
+20010607 1.0.2 AA
+
+Fixed broken SSL detection and started passing any available client certificate through to Tomcat.
+			
+20010603 1.0.1 AA
+
+Made it work under Linux, which principally involved adding support for reading the configuration from an ini file rather than the registry
+
+20010603 1.0.1 AA
+
+Tidied up configuration file handling, so the code that gets the config items is mostly common for Registry based config and inifile based config
diff --git a/connectors/jk/native/domino/Makefile b/connectors/jk/native/domino/Makefile
new file mode 100644
index 0000000..dccc090
--- /dev/null
+++ b/connectors/jk/native/domino/Makefile
@@ -0,0 +1,83 @@
+# Makefile for Tomcat Domino connector
+
+CC=gcc
+LD=gcc -shared
+CCFLAGS=-fPIC -O3
+LDFLAGS=-shared
+
+#################################################################################
+#										#
+# These will almost certainly need to be changed to match the specifics of your	#
+# Domino and Tomcat installations.						#
+#										#
+#################################################################################
+
+# The root of your Domino installation. Mine's in /usr/local/lotus, but your's
+# may well be /opt/lotus
+NOTESROOT=/usr/local/lotus
+
+# The place where the Notes API is installed
+NOTESAPI=$(NOTESROOT)/notesapi
+
+# The Domino program directory.
+NOTESHOME=$(NOTESROOT)/notes/5061/linux
+
+# The Domino data directory (the directory containing names.nsf)
+NOTESDATA=$(NOTESROOT)/notes/data
+
+# The include path for the Notes C API headers
+NOTESINC=$(NOTESAPI)/include
+
+# Where tomcat is installed. This is where conf, lib, webapps et al normally are
+TOMCATHOME=/usr/local/apache/tomcat
+
+# Your JDK's include directory
+JAVAINC=$(JAVA_HOME)/include
+
+#################################################################################
+#										#
+# You probably won't need to change anything below here.			#
+#										#
+#################################################################################
+
+JK=../common
+LIBS=-lc -ldl -lnotes -L$(NOTESHOME)
+
+INC=-I$(NOTESINC) -I$(JAVAINC) -I$(JK)
+DEFS=-DLINUX -DUNIX
+
+JKOBJ = $(JK)/jk_ajp12_worker.o $(JK)/jk_ajp13.o $(JK)/jk_ajp13_worker.o \
+	$(JK)/jk_connect.o $(JK)/jk_jni_worker.o $(JK)/jk_lb_worker.o \
+	$(JK)/jk_map.o $(JK)/jk_msg_buff.o $(JK)/jk_nwmain.o \
+	$(JK)/jk_pool.o $(JK)/jk_sockbuf.o $(JK)/jk_uri_worker_map.o \
+	$(JK)/jk_util.o $(JK)/jk_worker.o $(JK)/jk_ajp14.o \
+	$(JK)/jk_ajp14_worker.o $(JK)/jk_context.o $(JK)/jk_md5.o \
+	$(JK)/jk_ajp_common.o
+
+LOBJ  =	jk_dsapi_plugin.o inifile.o
+OBJ   = $(JKOBJ) $(LOBJ)
+
+TARG  = libtomcat.so
+INI   = libtomcat.ini
+
+all: $(TARG) $(INI) 
+
+$(TARG): $(OBJ)
+	$(LD) $(LDFLAGS) $(LIBS) $(OBJ) -o $(TARG)
+
+$(INI): mkini.sh Makefile
+	./mkini.sh $(TOMCATHOME) > $(INI)
+
+.c.o:
+	$(CC) $(CCFLAGS) $(DEFS) $(INC) -o $@ -c $< 
+
+clean:
+	rm -f $(OBJ)
+
+install: $(TARG) $(INI)
+	cp $(TARG) $(NOTESHOME)
+	cp $(INI) $(NOTESDATA)
+
+# Various dependencies
+jk_dsapi_plugin.o: jk_dsapi_plugin.c config.h inifile.h
+inifile.o: inifile.c config.h inifile.h
diff --git a/connectors/jk/native/domino/config.h b/connectors/jk/native/domino/config.h
new file mode 100755
index 0000000..19dad42
--- /dev/null
+++ b/connectors/jk/native/domino/config.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: DSAPI plugin for Lotus Domino                              *
+ * Author:      Andy Armstrong <andy@tagish.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef __config_h
+#define __config_h
+
+#define MAKEVERSION(a, b, c, d) \
+	(((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
+
+/* the _memicmp() function is available */
+#if defined(WIN32)
+
+#define HAVE_MEMICMP
+#undef USE_INIFILE
+
+#elif defined(LINUX)
+
+#undef HAVE_MEMICMP
+#define USE_INIFILE
+
+#elif defined(SOLARIS)
+
+#undef HAVE_MEMICMP
+#define USE_INIFILE
+
+#else
+#error Please define one of WIN32, LINUX or SOLARIS
+#endif
+
+/* define if you don't have the Notes C API which is available from
+ *
+ *    http://www.lotus.com/rw/dlcapi.nsf
+ */
+/* #undef NO_CAPI */
+
+#define DEBUG(args) \
+	do { /*printf args ;*/ } while (0)
+
+#endif /* __config_h */
diff --git a/connectors/jk/native/domino/dsapi.dsp b/connectors/jk/native/domino/dsapi.dsp
new file mode 100755
index 0000000..a7eba9d
--- /dev/null
+++ b/connectors/jk/native/domino/dsapi.dsp
@@ -0,0 +1,305 @@
+# Microsoft Developer Studio Project File - Name="dsapi" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=dsapi - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "dsapi.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "dsapi.mak" CFG="dsapi - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "dsapi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "dsapi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "dsapi - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DSAPI_EXPORTS" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "..\common" /I "C:\notesapi\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DSAPI_EXPORTS" /D "NT" /FR /FD /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib notes.lib /nologo /dll /machine:I386 /out:"Release/tomcat_redirector.dll" /libpath:"C:\notesapi\lib\mswin32"
+
+!ELSEIF  "$(CFG)" == "dsapi - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DSAPI_EXPORTS" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "..\common" /I "C:\notesapi\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DSAPI_EXPORTS" /D "NT" /FR /FD /GZ /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib notes.lib /nologo /dll /debug /machine:I386 /out:"Debug/tomcat_redirector.dll" /pdbtype:sept /libpath:"C:\notesapi\lib\mswin32"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "dsapi - Win32 Release"
+# Name "dsapi - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\inifile.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp12_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp_common.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_connect.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_context.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_dsapi_plugin.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_jni_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_lb_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_msg_buff.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_nwmain.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_sockbuf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_uri_worker_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_util.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_registry.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_env.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\config.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dsapifilter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\inifile.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp12_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp_common.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_connect.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_context.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_global.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_jni_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_lb_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_msg_buff.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_mt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_service.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_sockbuf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_uri_worker_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_util.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_env.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# Begin Source File
+
+SOURCE=.\ReadMe.txt
+# End Source File
+# End Target
+# End Project
diff --git a/connectors/jk/native/domino/dsapifilter.h b/connectors/jk/native/domino/dsapifilter.h
new file mode 100755
index 0000000..4df4344
--- /dev/null
+++ b/connectors/jk/native/domino/dsapifilter.h
@@ -0,0 +1,329 @@
+/*--------------------------------------------------------------------
+ *
+ *      File:      dsapifilter.h
+ *
+ *      Copyright (c)1999 Iris Associates
+ *
+ *-------------------------------------------------------------------*/
+
+#if !defined(DSAPIFILTER_H)
+#define DSAPIFILTER_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/*---
+ *      Types and Defines
+ */
+
+#define kInterfaceVersion   2
+#define kMaxFilterDesc   255
+
+	typedef unsigned char LMBCS;
+
+	typedef enum
+	{
+		kFilterNotHandled = 0,
+		kFilterHandledRequest = 1,
+		kFilterHandledEvent = 2,
+		kFilterError = 3
+	}
+	FilterReturnCode;
+
+/*--
+ *      Filter interface
+ */
+
+/*---
+*      events to register for
+*/
+	typedef enum
+	{
+		kFilterRawRequest = 0x01,
+		kFilterParsedRequest = 0x02,
+		kFilterAuthUser = 0x04,
+		kFilterUserNameList = 0x08,
+		kFilterMapURL = 0x10,
+		kFilterResponse = 0x20,
+		kFilterRawWrite = 0x40,
+		kFilterEndRequest = 0x80,
+		kFilterAny = 0xFF
+	}
+	EventFlags;
+
+/*---
+ *      filter initialization data
+ */
+	typedef struct
+	{
+		unsigned int serverFilterVersion;
+		unsigned int appFilterVersion;
+		unsigned int eventFlags;
+		unsigned int initFlags;
+		char filterDesc[kMaxFilterDesc + 1];
+	}
+	FilterInitData;
+
+/*---
+ *      request line structure
+ */
+	typedef struct
+	{
+		unsigned int method;
+		char *URL;
+		char *version;
+		char *userName;
+		char *password;
+		unsigned char *clientCert;
+		unsigned int clientCertLen;
+		char *contentRead;
+		unsigned int contentReadLen;
+	}
+	FilterRequest;
+
+/*---
+ *      filter context data included in every call to filter
+ */
+	typedef struct _FilterContext
+	{
+		unsigned int contextSize;
+		unsigned int revision;
+		void *serverContext;
+		unsigned int serverReserved;
+		unsigned int securePort;
+		void *privateContext;
+
+		int (*GetRequest) (struct _FilterContext *context, FilterRequest * request,
+						   unsigned int *errID);
+
+		int (*GetRequestContents) (struct _FilterContext *context, char **contents,
+								   unsigned int *errID);
+
+		int (*GetServerVariable) (struct _FilterContext * context, char *name, void *buffer,
+								  unsigned int bufferSize, unsigned int *errID);
+
+		int (*WriteClient) (struct _FilterContext * context, char *buffer, unsigned int bufferLen,
+							unsigned int reserved, unsigned int *errID);
+
+		void *(*AllocMem) (struct _FilterContext * context, unsigned int size,
+						   unsigned int reserved, unsigned int *errID);
+
+		int (*ServerSupport) (struct _FilterContext * context, unsigned int funcType, void *data1,
+							  void *data2, unsigned int other, unsigned int *errID);
+	}
+	FilterContext;
+
+	typedef unsigned int (*FilterInitFuncType) (FilterInitData * initData);
+	typedef unsigned int (*FilterEventFuncType) (FilterContext * context, unsigned int eventType,
+												 void *eventData, unsigned int *errID);
+	typedef unsigned int (*FilterTermFuncType) (unsigned int);
+
+/*---
+ *      request methods
+ */
+	typedef enum
+	{
+		kRequestNone = 0,
+		kRequestHEAD = 1,
+		kRequestGET = 2,
+		kRequestPOST = 3,
+		kRequestPUT = 4,
+		kRequestDELETE = 5
+	}
+	RequestMethod;
+
+/*---
+ *      server support function types
+ */
+	typedef enum
+	{
+		kWriteResponseHeaders = 1
+	}
+	ServerSupportTypes;
+
+/*---
+ *      'data1' for server support function 'kWriteResponseHeaders'
+ */
+	typedef struct
+	{
+		unsigned int responseCode;
+		char *reasonText;
+		char *headerText;
+	}
+	FilterResponseHeaders;
+
+/*---
+ *      raw request (headers not processed yet)
+ */
+	typedef struct
+	{
+		unsigned int requestMethod;
+
+		int (*GetAllHeaders) (FilterContext * context, char **headers, unsigned int *errID);
+
+		int (*GetHeader) (FilterContext * context, char *name, char *buffer,
+						  unsigned int bufferSize, unsigned int *errID);
+
+		int (*SetHeader) (FilterContext * context, char *name, char *value, unsigned int *errID);
+
+		int (*AddHeader) (FilterContext * context, char *header, unsigned int *errID);
+
+		unsigned int reserved;
+	}
+	FilterRawRequest;
+
+/*---
+ *      parsed request
+ */
+	typedef struct
+	{
+		unsigned int requestMethod;
+
+		int (*GetAllHeaders) (FilterContext * context, char **headers, unsigned int *errID);
+
+		int (*GetHeader) (FilterContext * context, char *name, char *buffer,
+						  unsigned int bufferSize, unsigned int *errID);
+
+		unsigned int reserved;
+	}
+	FilterParsedRequest;
+
+/*---
+ *      URL map
+ */
+	typedef struct
+	{
+		const char *url;
+		char *pathBuffer;
+		unsigned int bufferSize;
+		unsigned int mapType;
+	}
+	FilterMapURL;
+
+/*---
+ *      URL map types
+ */
+	typedef enum
+	{
+		kURLMapUnknown = 0,
+		kURLMapPass = 1,
+		kURLMapExec = 2,
+		kURLMapRedirect = 3,
+		kURLMapService = 4,
+		kURLMapDomino = 5
+	}
+	FilterULMapTypes;
+
+/*---
+ *      user authentication
+ */
+	typedef struct
+	{
+		LMBCS *userName;
+		LMBCS *password;
+		unsigned char *clientCert;
+		unsigned int clientCertLen;
+		unsigned int authFlags;
+		unsigned int preAuthenticated;
+		unsigned int foundInCache;
+		unsigned int authNameSize;
+		LMBCS *authName;
+		unsigned int authType;
+
+		int (*GetUserNameList) (FilterContext * context, LMBCS * buffer, unsigned int bufferSize,
+								unsigned int *numNames, unsigned int reserved, unsigned int *errID);
+
+		int (*GetHeader) (FilterContext * context, char *name, char *buffer,
+						  unsigned int bufferSize, unsigned int *errID);
+	}
+	FilterAuthenticate;
+
+/*---
+ *      user authentication types
+ */
+	typedef enum
+	{
+		kNotAuthentic = 0,
+		kAuthenticBasic = 1,
+		kAuthenticClientCert = 2
+	}
+	FilterAuthenticationTypes;
+
+/*---
+ *      authentication configuration flags
+ */
+	typedef enum
+	{
+		kAuthAllowBasic = 1,
+		kAuthAllowAnonymous = 2,
+		kAuthAllowSSLCert = 4,
+		kAuthAllowSSLBasic = 8,
+		kAuthAllowSSLAnonymous = 16,
+		kAuthRedirectToSSL = 32
+	}
+	FilterAuthConfigFlags;
+
+/*---
+ *      user name list
+ */
+	typedef struct
+	{
+		const LMBCS *userName;
+
+		int (*GetUserNameList) (FilterContext * context, LMBCS * buffer, unsigned int bufferSize,
+								unsigned int *numNames, unsigned int reserved, unsigned int *errID);
+
+		int (*PopulateUserNameList) (FilterContext * context, LMBCS * buffer,
+									 unsigned int bufferSize, unsigned int *numNames,
+									 unsigned int reserved, unsigned int *errID);
+
+		int (*AddGroupsToList) (FilterContext * context, LMBCS * groupNames,
+								unsigned int numGroupNames, unsigned int reserved,
+								unsigned int *errID);
+
+		int (*RemoveGroupsFromList) (FilterContext * context, unsigned int reserved,
+									 unsigned int *errID);
+
+		unsigned int reserved;
+	}
+	FilterUserNameList;
+
+/*---
+ *      request response
+ */
+	typedef struct
+	{
+		unsigned int responseCode;
+		char *reasonText;
+
+		int (*GetAllHeaders) (FilterContext * context, char **headers, unsigned int *errID);
+
+		int (*GetHeader) (FilterContext * context, char *name, char *buffer,
+						  unsigned int bufferSize, unsigned int *errID);
+
+		int (*SetHeader) (FilterContext * context, char *name, char *value, unsigned int *errID);
+
+		int (*AddHeader) (FilterContext * context, char *header, unsigned int *errID);
+
+		unsigned int reserved;
+	}
+	FilterResponse;
+
+/*---
+ *      write content
+ */
+	typedef struct
+	{
+		char *content;
+		unsigned int contentLen;
+		unsigned int reserved;
+	}
+	FilterRawWrite;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* DSAPIFILTER_H */
diff --git a/connectors/jk/native/domino/inifile.c b/connectors/jk/native/domino/inifile.c
new file mode 100644
index 0000000..102231f
--- /dev/null
+++ b/connectors/jk/native/domino/inifile.c
@@ -0,0 +1,279 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: DSAPI plugin for Lotus Domino                              *
+ * Author:      Andy Armstrong <andy@tagish.com>                           *
+ * Date:        20010603                                                   *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#include "config.h"
+#include "inifile.h"
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* We have one of these for each ini file line. Once we've read the
+ * file and parsed it we'll have an array in key order containing one
+ * of these for each configuration item in file.
+ */
+typedef struct
+{
+	const char *key;
+	const char *value;
+
+} inifile_key;
+
+static char *file;			/* the text of the ini file				*/
+static inifile_key *keys;	/* an array of keys, one per item		*/
+static size_t klen;			/* length of the key array				*/
+
+/* Text that will prefix all of our error messages */
+#define ERRPFX "INIFILE: "
+/* Various error messages that we can return */
+ERRTYPE inifile_outofmemory		= ERRPFX "Out of memory";
+ERRTYPE inifile_filenotfound	= ERRPFX "File not found";
+ERRTYPE inifile_readerror		= ERRPFX "Error reading file";
+#define SYNFMT ERRPFX "File %s, line %d: %s"
+
+/* Case insensitive string comparison, works like strcmp() */
+static int inifile__stricmp(const char *s1, const char *s2)
+{
+	while (*s1 && tolower(*s1) == tolower(*s2))
+		s1++, s2++;
+	return tolower(*s1) - tolower(*s2);
+}
+
+/* Compare keys, suitable for passing to qsort() */
+static int inifile__cmp(const void *k1, const void *k2)
+{
+	const inifile_key *kk1 = (const inifile_key *) k1;
+	const inifile_key *kk2 = (const inifile_key *) k2;
+	return inifile__stricmp(kk1->key, kk2->key);
+}
+
+/* Return a new syntax error message. */
+static ERRTYPE inifile__syntax(jk_pool_t *p, const char *file, int line, const char *msg)
+{
+	static const char synfmt[] = SYNFMT;
+	size_t len = sizeof(synfmt) + strlen(msg) + strlen(file) + 10 /* fudge for line number */;
+	char *buf = jk_pool_alloc(p, len);
+	sprintf(buf, synfmt, file, line, msg);
+	return buf;
+}
+
+/* Various macros to tidy up the parsing code */
+
+/* Characters that are OK in the keyname */
+#define KEYCHR(c) \
+	(isalnum(c) || (c) == '.' || (c) == '_')
+
+/* Skip whitespace */
+#define SKIPSPC() \
+	while (*fp == '\t' || *fp == ' ') fp++
+
+/* Skip to the end of the current line */
+#define SKIPLN() \
+	while (*fp != '\0' && *fp != '\r' && *fp != '\n') fp++
+
+/* Move from the end of the current line to the start of the next, learning what the
+ * newline character is and counting lines
+ */
+#define NEXTLN() \
+	do { while (*fp == '\r' || *fp == '\n') { if (nlc == -1) nlc = *fp; if (*fp == nlc) ln++; fp++; } } while (0)
+
+/* Build the index. Called when the inifile is loaded by inifile_load()
+ */
+static ERRTYPE inifile__index(jk_pool_t *p, const char *name)
+{
+	int pass;
+	int ln = 1;
+	int nlc = -1;
+
+	/* Make two passes over the data. First time we're just counting
+	 * the lines that contain values so we can allocate the index, second
+	 * time we build the index.
+	 */
+	for (pass = 0; pass < 2; pass++)
+	{
+		char *fp = file;
+		char *ks = NULL, *ke;	/* key start, end */
+		char *vs = NULL, *ve;	/* value start, end */
+		klen = 0;
+
+		while (*fp != '\0')
+		{
+			SKIPSPC();
+
+			/* turn a comment into an empty line by moving to the next \r|\n */
+			if (*fp == '#' || *fp == ';')
+				SKIPLN();
+
+			if (*fp != '\0' && *fp != '\r' && *fp != '\n')
+			{
+				ks = fp;		/* start of key */
+				while (KEYCHR(*fp)) fp++;
+				ke = fp;		/* end of key */
+				SKIPSPC();
+
+				if (*fp != '=')
+					return inifile__syntax(p, name, ln, "Missing '=' or illegal character in key");
+
+				fp++; /* past the = */
+				SKIPSPC();
+				vs = fp;
+				SKIPLN();
+				ve = fp;
+				/* back up over any trailing space */
+				while (ve > vs && (ve[-1] == ' ' || ve[-1] == '\t')) ve--;
+				NEXTLN(); /* move forwards *before* we trash the eol characters */
+
+				if (NULL != keys) /* second pass? if so stash a pointer */
+				{
+					*ke = '\0';
+					*ve = '\0';
+					keys[klen].key = ks;
+					keys[klen].value = vs;
+				}
+
+				klen++;
+			}
+			else
+			{
+				NEXTLN();
+			}
+		}
+
+		if (NULL == keys && (keys = jk_pool_alloc(p, sizeof(inifile_key) * klen), NULL == keys))
+			return inifile_outofmemory;
+	}
+
+	/* got the index now, sort it so we can search it quickly */
+	qsort(keys, klen, sizeof(inifile_key), inifile__cmp);
+
+	return ERRNONE;
+}
+
+/* Read an INI file from disk
+ */
+ERRTYPE inifile_read(jk_pool_t *p, const char *name)
+{
+	FILE *fl;
+	size_t flen;
+	int ok;
+
+	if (fl = fopen(name, "rb"), NULL == fl)
+		return inifile_filenotfound;
+
+	fseek(fl, 0L, SEEK_END);
+	flen = (size_t) ftell(fl);
+	fseek(fl, 0L, SEEK_SET);
+
+	/* allocate one extra byte for trailing \0
+	 */
+	if (file = jk_pool_alloc(p, flen+1), NULL == file)
+	{
+		fclose(fl);
+		return inifile_outofmemory;
+	}
+
+	ok = (fread(file, flen, 1, fl) == 1);
+	fclose(fl);
+	if (!ok) return inifile_readerror;
+
+	file[flen] = '\0';	/* terminate it to simplify parsing */
+
+	return inifile__index(p, name);
+}
+
+/* Find the value associated with the given key returning it or NULL
+ * if no match is found. Key name matching is case insensitive.
+ */
+const char *inifile_lookup(const char *key)
+{
+	int lo, mid, hi, cmp;
+
+	if (NULL == keys)
+		return NULL;
+
+	for (lo = 0, hi = klen-1; lo <= hi; )
+	{
+		mid = (lo + hi) / 2;
+		cmp = inifile__stricmp(key, keys[mid].key);
+		if (cmp < 0)	/* key in array is greater */
+			hi = mid-1;
+		else if (cmp > 0)
+			lo = mid+1;
+		else
+			return keys[mid].value;
+	}
+
+	return NULL;
+}
+
+#ifdef TEST
+
+static jk_pool_t pool;
+extern void jk_dump_pool(jk_pool_t *p, FILE *f); /* not declared in header */
+
+int main(void)
+{
+	ERRTYPE e;
+	unsigned k;
+	int rc = 0;
+
+	jk_open_pool(&pool, NULL, 0);
+
+	e = inifile_read(&pool, "ok.ini");
+	if (e == ERRNONE)
+	{
+		printf("%u keys in ok.ini\n", klen);
+		for (k = 0; k < klen; k++)
+		{
+			const char *val = inifile_lookup(keys[k].key);
+			printf("Key: \"%s\", value: \"%s\"\n", keys[k].key, val);
+		}
+	}
+	else
+	{
+		printf("Error reading ok.ini: %s\n", e);
+		rc = 1;
+	}
+
+	e = inifile_read(&pool, "bad.ini");
+	if (e == ERRNONE)
+	{
+		printf("%u keys in bad.ini\n", klen);
+		for (k = 0; k < klen; k++)
+		{
+			const char *val = inifile_lookup(keys[k].key);
+			printf("Key: \"%s\", value: \"%s\"\n", keys[k].key, val);
+		}
+		rc = 1;		/* should be a syntax error */
+	}
+	else
+	{
+		printf("Error reading bad.ini: %s (which is OK: that's what we expected)\n", e);
+	}
+
+	jk_dump_pool(&pool, stdout);
+	jk_close_pool(&pool);
+
+	return rc;
+}
+#endif
diff --git a/connectors/jk/native/domino/inifile.h b/connectors/jk/native/domino/inifile.h
new file mode 100644
index 0000000..b793fb0
--- /dev/null
+++ b/connectors/jk/native/domino/inifile.h
@@ -0,0 +1,45 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: DSAPI plugin for Lotus Domino                              *
+ * Author:      Andy Armstrong <andy@tagish.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef __inifile_h
+#define __inifile_h
+
+#include "jk_pool.h"
+
+#define ERRTYPE const char *
+#define ERRFMT  "%s"			/* natural printf format for errors */
+#define ERRTXT(e) (e)			/* macro to return text for an error */
+#define ERRNONE NULL
+extern ERRTYPE inifile_outofmemory;
+extern ERRTYPE inifile_filenotfound;
+extern ERRTYPE inifile_readerror;
+
+/* Read an INI file from disk
+ */
+ERRTYPE inifile_read(jk_pool_t *pool, const char *name);
+
+/* Find the value associated with the given key returning it or NULL
+ * if no match is found. Key name matching is case insensitive.
+ */
+const char *inifile_lookup(const char *key);
+
+#endif /* __inifile_h */
diff --git a/connectors/jk/native/domino/jk_dsapi_plugin.c b/connectors/jk/native/domino/jk_dsapi_plugin.c
new file mode 100755
index 0000000..b7abbbb
--- /dev/null
+++ b/connectors/jk/native/domino/jk_dsapi_plugin.c
@@ -0,0 +1,1075 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: DSAPI plugin for Lotus Domino                              *
+ * Author:      Andy Armstrong <andy@tagish.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+/* Based on the IIS redirector by Gal Shachor <shachor@il.ibm.com> */
+
+#include "config.h"
+#include "inifile.h"
+
+/* JK stuff */
+#include "jk_global.h"
+#include "jk_util.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+#include "jk_ajp12_worker.h"
+#include "jk_uri_worker_map.h"
+
+#ifndef NO_CAPI
+/* Domino stuff */
+#include <global.h>
+#include <addin.h>
+#else
+#include <stdarg.h>
+#define NOERROR 0
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "dsapifilter.h"
+#if !defined(DLLEXPORT)
+#ifdef WIN32
+#define DLLEXPORT __declspec(dllexport)
+#else
+#define DLLEXPORT
+#endif
+#endif
+
+#define VERSION				"1.0.3"
+#define VERSION_STRING		"Jakarta/DSAPI/" VERSION
+/* What we call ourselves */
+#define FILTERDESC			"Apache Tomcat Interceptor (" VERSION_STRING ")"
+
+#define SERVERDFLT			"Lotus Domino"
+/* Registry location of configuration data */
+#define REGISTRY_LOCATION	"Software\\Apache Software Foundation\\Jakarta Dsapi Redirector\\1.0"
+/* Name of INI file relative to whatever the 'current' directory is when the filter is
+ * loaded. Certainly on Linux this is the Domino data directory -- it seems likely that
+ * it's the same on other platforms
+ */
+#define ININAME				"libtomcat.ini"
+
+/* Names of registry keys/ini items that contain commands to start, stop Tomcat */
+#define TOMCAT_START		"tomcat_start"
+#define TOMCAT_STOP			"tomcat_stop"
+#define TOMCAT_STARTSTOP_TO	30000				/* 30 seconds */
+
+static int					initDone	= JK_FALSE;
+static jk_uri_worker_map_t	*uw_map		= NULL;
+static jk_logger_t			*logger		= NULL;
+
+static int					logLevel	= JK_LOG_EMERG_LEVEL;
+static jk_pool_t			cfgPool;
+
+static const char *logFile;
+static const char *workerFile;
+static const char *workerMountFile;
+static const char *tomcatStart;
+static const char *tomcatStop;
+
+#if defined(JK_VERSION) && JK_VERSION >= MAKEVERSION(1, 2, 0, 1)
+static jk_worker_env_t   worker_env;
+#endif
+
+static char					*crlf		= "\r\n";
+
+typedef struct private_ws
+{
+	jk_pool_t			p;
+
+	/* These get passed in by Domino and are used to access various
+	 * Domino methods and data.
+	 */
+	FilterContext		*context;
+	FilterParsedRequest	*reqData;
+
+	/* True iff the response headers have been sent
+	 */
+	int					responseStarted;
+
+	/* Current pointer into and remaining size
+	 * of request body data
+	 */
+	char				*reqBuffer;
+	unsigned int		reqSize;
+
+} private_ws_t;
+
+/* These three functions are called back (indirectly) by
+ * Tomcat during request processing. StartResponse() sends
+ * the headers associated with the response.
+ */
+static int JK_METHOD StartResponse(jk_ws_service_t * s, int status, const char *reason,
+									const char *const *hdrNames,
+									const char *const *hdrValues, unsigned hdrCount);
+/* Read() is called by Tomcat to read from the request body (if any).
+ */
+static int JK_METHOD Read(jk_ws_service_t * s, void *b, unsigned l, unsigned *a);
+
+/* Write() is called by Tomcat to send data back to the client.
+ */
+static int JK_METHOD Write(jk_ws_service_t * s, const void *b, unsigned l);
+
+static int ReadInitData(void);
+
+#ifndef USE_INIFILE
+static const char *GetRegString(HKEY hkey, const char *key);
+#endif
+
+static unsigned int ParsedRequest(FilterContext *context, FilterParsedRequest *reqData);
+
+/* Case insentive memcmp() clone
+ */
+#ifdef HAVE_MEMICMP
+#define NoCaseMemCmp(ci, cj, l) _memicmp((void *) (ci), (void *) (cj), (l))
+#else
+static int NoCaseMemCmp(const char *ci, const char *cj, int len)
+{
+	if (0 == memcmp(ci, cj, len))
+		return 0;
+	while (len > 0)
+	{
+		int cmp = tolower(*ci) - tolower(*cj);
+		if (cmp != 0) return cmp;
+		ci++;
+		cj++;
+		len--;
+	}
+	return 0;
+}
+#endif
+
+/* Case insentive strcmp() clone
+ */
+#ifdef HAVE_STRICMP
+#define NoCaseStrCmp(si, sj) _stricmp((void *) (si), (void *) (sj))
+#else
+static int NoCaseStrCmp(const char *si, const char *sj)
+{
+	if (0 == strcmp(si, sj))
+		return 0;
+
+	while (*si && tolower(*si) == tolower(*sj))
+		si++, sj++;
+
+	return tolower(*si) - tolower(*sj);
+}
+#endif
+
+/* Case insensitive substring search.
+ * str		string to search
+ * slen		length of string to search
+ * ptn		pattern to search for
+ * plen		length of pattern
+ * returns	1 if there's a match otherwise 0
+ */
+static int FindPathElem(const char *str, int slen, const char *ptn, int plen)
+{
+	const char *sp = str;
+
+	while (slen >= plen)
+	{
+		/* We're looking for a match for the specified string bounded by
+		 * the start of the string, \ or / at the left and the end of the
+		 * string, \ or / at the right. We look for \ as well as / on the
+		 * suspicion that a Windows hosted server might accept URIs
+		 * containing \.
+		 */
+		if (NoCaseMemCmp(sp, ptn, plen) == 0 &&
+			(sp == str || *sp == '\\' || *sp == '/') &&
+			(*sp == '\0' || *sp == '\\' || *sp == '/'))
+			return 1;
+		slen--;
+		sp++;
+	}
+	return 0;
+}
+
+#ifdef NO_CAPI
+/* Alternative to the Domino function */
+static void AddInLogMessageText(char *msg, unsigned short code, ...)
+{
+	va_list ap;
+
+	if (code != NOERROR)
+		printf("Error %d: ", code);
+
+	va_start(ap, code);
+	vprintf(msg, ap);
+	va_end(ap);
+	printf("\n");
+}
+#endif
+
+/* Get the value of a server (CGI) variable as a string
+ */
+static int GetVariable(private_ws_t *ws, char *hdrName,
+					 char *buf, DWORD bufsz, char **dest, const char *dflt)
+{
+	int errID;
+
+	if (ws->context->GetServerVariable(ws->context, hdrName, buf, bufsz, &errID))
+		*dest = jk_pool_strdup(&ws->p, buf);
+	else
+		*dest = jk_pool_strdup(&ws->p, dflt);
+
+	DEBUG(("%s = %s\n", hdrName, *dest));
+
+	return JK_TRUE;
+}
+
+/* Get the value of a server (CGI) variable as an integer
+ */
+static int GetVariableInt(private_ws_t *ws, char *hdrName,
+						char *buf, DWORD bufsz, int *dest, int dflt)
+{
+	int errID;
+
+	if (ws->context->GetServerVariable(ws->context, hdrName, buf, bufsz, &errID))
+		*dest = atoi(buf);
+	else
+		*dest = dflt;
+
+	DEBUG(("%s = %d\n", hdrName, *dest));
+
+	return JK_TRUE;
+}
+/* Get the value of a server (CGI) variable as an integer
+ */
+static int GetVariableBool(private_ws_t *ws, char *hdrName,
+						char *buf, DWORD bufsz, int *dest, int dflt)
+{
+	int errID;
+
+	if (ws->context->GetServerVariable(ws->context, hdrName, buf, bufsz, &errID))
+	{
+		if (isdigit(buf[0]))
+			*dest = atoi(buf) != 0;
+		else if (NoCaseStrCmp(buf, "yes") == 0 || NoCaseStrCmp(buf, "on") == 0)
+			*dest = 1;
+		else
+			*dest = 0;
+	}
+	else
+	{
+		*dest = dflt;
+	}
+
+	DEBUG(("%s = %d\n", hdrName, *dest));
+
+	return JK_TRUE;
+}
+
+/* A couple of utility macros to supply standard arguments to GetVariable() and
+ * GetVariableInt().
+ */
+#define GETVARIABLE(name, dest, dflt)		GetVariable(ws, (name), workBuf, sizeof(workBuf), (dest), (dflt))
+#define GETVARIABLEINT(name, dest, dflt)	GetVariableInt(ws, (name), workBuf, sizeof(workBuf), (dest), (dflt))
+#define GETVARIABLEBOOL(name, dest, dflt)	GetVariableBool(ws, (name), workBuf, sizeof(workBuf), (dest), (dflt))
+
+/* Return 1 iff the supplied string contains "web-inf" (in any case
+ * variation. We don't allow URIs containing web-inf, although
+ * FindPathElem() actually looks for the string bounded by path punctuation
+ * or the ends of the string, so web-inf must appear as a single element
+ * of the supplied URI
+ */
+static int BadURI(const char *uri)
+{
+	static char *wi = "web-inf";
+	return FindPathElem(uri, strlen(uri), wi, strlen(wi));
+}
+
+/* Replacement for strcat() that updates a buffer pointer. It's
+ * probably marginal, but this should be more efficient that strcat()
+ * in cases where the string being concatenated to gets long because
+ * strcat() has to count from start of the string each time.
+ */
+static void Append(char **buf, const char *str)
+{
+	int l = strlen(str);
+	memcpy(*buf, str, l);
+	(*buf)[l] = '\0';
+	*buf += l;
+}
+
+/* Start the response by sending any headers. Invoked by Tomcat. I don't
+ * particularly like the fact that this always allocates memory, but
+ * perhaps jk_pool_alloc() is efficient.
+ */
+static int JK_METHOD StartResponse(jk_ws_service_t *s, int status, const char *reason,
+									const char *const *hdrNames,
+									const char *const *hdrValues, unsigned hdrCount)
+{
+	DEBUG(("StartResponse()\n"));
+	jk_log(logger, JK_LOG_DEBUG, "Into jk_ws_service_t::StartResponse\n");
+
+	if (status < 100 || status > 1000)
+	{
+		jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::StartResponse, invalid status %d\n", status);
+		return JK_FALSE;
+	}
+
+	if (s && s->ws_private)
+	{
+		private_ws_t *p = s->ws_private;
+
+		if (!p->responseStarted)
+		{
+			char *hdrBuf;
+			FilterResponseHeaders frh;
+			int rc, errID;
+
+			p->responseStarted = JK_TRUE;
+
+			if (NULL == reason)
+				reason = "";
+
+			/* Build a single string containing all the headers
+			 * because that's what Domino needs.
+			 */
+			if (hdrCount > 0)
+			{
+				unsigned i;
+				unsigned hdrLen;
+				char *bufp;
+
+				for (i = 0, hdrLen = 3; i < hdrCount; i++)
+					hdrLen += strlen(hdrNames[i]) + strlen(hdrValues[i]) + 4;
+
+				hdrBuf = jk_pool_alloc(&p->p, hdrLen);
+				bufp = hdrBuf;
+
+				for (i = 0; i < hdrCount; i++)
+				{
+					Append(&bufp, hdrNames[i]);
+					Append(&bufp, ": ");
+					Append(&bufp, hdrValues[i]);
+					Append(&bufp, crlf);
+				}
+
+				Append(&bufp, crlf);
+			}
+			else
+			{
+				hdrBuf = crlf;
+			}
+
+			frh.responseCode = status;
+			frh.reasonText = (char *) reason;
+			frh.headerText = hdrBuf;
+
+			DEBUG(("%d %s\n%s", status, reason, hdrBuf));
+
+			/* Send the headers */
+			rc = p->context->ServerSupport(p->context, kWriteResponseHeaders, &frh, NULL, 0, &errID);
+
+			/*
+			if (rc)
+			{
+				jk_log(logger, JK_LOG_ERROR,
+					   "jk_ws_service_t::StartResponse, ServerSupportFunction failed\n");
+				return JK_FALSE;
+			}
+			*/
+
+		}
+		return JK_TRUE;
+	}
+
+	jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::StartResponse, NULL parameters\n");
+
+	return JK_FALSE;
+}
+
+static int JK_METHOD Read(jk_ws_service_t * s, void *bytes, unsigned len, unsigned *countp)
+{
+	DEBUG(("Read(%p, %p, %u, %p)\n", s, bytes, len, countp));
+	jk_log(logger, JK_LOG_DEBUG, "Into jk_ws_service_t::Read\n");
+
+	if (s && s->ws_private && bytes && countp)
+	{
+		private_ws_t *p = s->ws_private;
+
+		/* Copy data from Domino's buffer. Although it seems slightly
+		 * improbably we're believing that Domino always buffers the
+		 * entire request in memory. Not properly tested yet.
+		 */
+		if (len > p->reqSize) len = p->reqSize;
+		memcpy(bytes, p->reqBuffer, len);
+		p->reqBuffer += len;
+		p->reqSize -= len;
+		*countp = len;
+		return JK_TRUE;
+	}
+
+	jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::Read, NULL parameters\n");
+
+	return JK_FALSE;
+}
+
+static int JK_METHOD Write(jk_ws_service_t *s, const void *bytes, unsigned len)
+{
+	DEBUG(("Write(%p, %p, %u)\n", s, bytes, len));
+	jk_log(logger, JK_LOG_DEBUG, "Into jk_ws_service_t::Write\n");
+
+	if (s && s->ws_private && bytes)
+	{
+		private_ws_t *p = s->ws_private;
+		int errID, rc;
+
+		/* Make sure the response has really started. I'm almost certain
+		 * this isn't necessary, but it was in the ISAPI code, so it's in
+		 * here too.
+		 */
+		if (!p->responseStarted)
+			StartResponse(s, 200, NULL, NULL, NULL, 0);
+
+		DEBUG(("Writing %d bytes of content\n", len));
+
+		/* Send the data */
+		if (len > 0)
+			rc = p->context->WriteClient(p->context, (char *) bytes, len, 0, &errID);
+
+		return JK_TRUE;
+	}
+
+	jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::Write, NULL parameters\n");
+
+	return JK_FALSE;
+}
+
+static int RunProg(const char *cmd)
+{
+#ifdef WIN32
+    STARTUPINFO si;
+    PROCESS_INFORMATION pi;
+
+	ZeroMemory(&si, sizeof(si));
+    si.cb			= sizeof(si);    // Start the child process.
+	si.dwFlags		= STARTF_USESHOWWINDOW;
+	si.wShowWindow	= SW_SHOWMAXIMIZED;
+
+	if (!CreateProcess(NULL, (char *) cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
+	{
+		DWORD err = GetLastError();
+		AddInLogMessageText("Command \"%s\" (error %u)", NOERROR, cmd, err);
+		return FALSE;
+	}
+
+	if (WAIT_OBJECT_0 == WaitForSingleObject(pi.hProcess, TOMCAT_STARTSTOP_TO))
+		return TRUE;
+
+	AddInLogMessageText("Command \"%s\" didn't complete in time", NOERROR, cmd);
+	return FALSE;
+
+#else
+	int err = system(cmd);
+	if (0 == err) return 1;
+	AddInLogMessageText("Command \"%s\" failed (error %d)", NOERROR, cmd, err);
+	return 0;
+#endif
+}
+
+/* Called when the filter is unloaded. Free various resources and
+ * display a banner.
+ */
+DLLEXPORT unsigned int TerminateFilter(unsigned int reserved)
+{
+	if (initDone)
+	{
+		uri_worker_map_free(&uw_map, logger);
+		wc_close(logger);
+		if (logger)
+			jk_close_file_logger(&logger);
+
+
+		initDone = JK_FALSE;
+
+	}
+
+	if (NULL != tomcatStop && '\0' != *tomcatStop)
+	{
+		AddInLogMessageText("Attempting to stop Tomcat: %s", NOERROR, tomcatStop);
+		RunProg(tomcatStop);
+	}
+
+	AddInLogMessageText(FILTERDESC " unloaded", NOERROR);
+
+	jk_close_pool(&cfgPool);
+
+	return kFilterHandledEvent;
+}
+
+
+/* Called when Domino loads the filter. Reads a load of config data from
+ * the registry and elsewhere and displays a banner.
+ */
+DLLEXPORT unsigned int FilterInit(FilterInitData *filterInitData)
+{
+
+	jk_open_pool(&cfgPool, NULL, 0);		/* empty pool for config data */
+
+	if (!ReadInitData())
+		goto initFailed;
+
+	if (!jk_open_file_logger(&logger, logFile, logLevel))
+		logger = NULL;
+
+	if (NULL != tomcatStart && '\0' != *tomcatStart)
+	{
+		AddInLogMessageText("Attempting to start Tomcat: %s", NOERROR, tomcatStart);
+		RunProg(tomcatStart);
+	}
+
+	filterInitData->appFilterVersion = kInterfaceVersion;
+	filterInitData->eventFlags = kFilterParsedRequest;
+	strcpy(filterInitData->filterDesc, FILTERDESC);
+
+	// Banner
+	AddInLogMessageText("%s loaded", NOERROR, filterInitData->filterDesc);
+
+	return kFilterHandledEvent;
+
+initFailed:
+	AddInLogMessageText("Error loading %s", NOERROR, FILTERDESC);
+
+	return kFilterError;
+}
+
+/* Read parameters from the registry
+ */
+static int ReadInitData(void)
+{
+	int ok = JK_TRUE;
+	const char *v;
+
+#ifdef USE_INIFILE
+// Using an INIFILE
+
+#define GETV(key) inifile_lookup(key)
+
+	ERRTYPE e;
+
+	if (e = inifile_read(&cfgPool, ININAME), ERRNONE != e)
+	{
+		AddInLogMessageText("Error reading: %s, %s", NOERROR, ININAME, ERRTXT(e));
+		return JK_FALSE;
+	}
+
+#else
+// Using the registry
+#define GETV(key) GetRegString(hkey, key)
+
+	HKEY hkey;
+	long rc;
+
+	rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_LOCATION, (DWORD) 0, KEY_READ, &hkey);
+	if (ERROR_SUCCESS != rc) return JK_FALSE;
+
+#endif
+
+#define GETVNB(tag, var) \
+	var = GETV(tag); \
+	if (NULL == var) \
+	{ \
+		AddInLogMessageText("%s not defined in %s", NOERROR, tag, ININAME); \
+		ok = JK_FALSE; \
+	}
+
+	GETVNB(JK_LOG_FILE_TAG, logFile)
+	GETVNB(JK_LOG_LEVEL_TAG, v);
+	GETVNB(JK_WORKER_FILE_TAG, workerFile);
+	GETVNB(JK_MOUNT_FILE_TAG, workerMountFile);
+
+	logLevel = (NULL == v) ? 0 : jk_parse_log_level(v);
+
+	tomcatStart	= GETV(TOMCAT_START);
+	tomcatStop	= GETV(TOMCAT_STOP);
+
+#ifndef USE_INIFILE
+	RegCloseKey(hkey);
+#endif
+
+	return ok;
+}
+
+#ifndef USE_INIFILE
+static const char *GetRegString(HKEY hkey, const char *key)
+{
+	DWORD type = 0;
+	DWORD sz = 0;
+	LONG rc;
+	char *val;
+
+	rc = RegQueryValueEx(hkey, key, (LPDWORD) 0, &type, NULL, &sz);
+	if (rc != ERROR_SUCCESS || type != REG_SZ)
+		return NULL;
+
+	if (val = jk_pool_alloc(&cfgPool, sz), NULL == val)
+		return NULL;
+
+	rc = RegQueryValueEx(hkey, key, (LPDWORD) 0, &type, val, &sz);
+	if (rc == ERROR_SUCCESS)
+		return val;
+
+	return NULL;
+}
+#endif
+
+/* Main entry point for the filter. Called by Domino for every HTTP request.
+ */
+DLLEXPORT unsigned int HttpFilterProc(FilterContext *context, unsigned int eventType, void *eventData)
+{
+	switch (eventType)
+	{
+	case kFilterParsedRequest:
+		return ParsedRequest(context, (FilterParsedRequest *) eventData);
+	default:
+		break;
+	}
+	return kFilterNotHandled;
+}
+
+/* Send a simple response. Used when we don't want to bother Tomcat,
+ * which in practice means for various error conditions that we can
+ * detect internally.
+ */
+static void SimpleResponse(FilterContext *context, int status, char *reason, char *body)
+{
+	FilterResponseHeaders frh;
+	int rc, errID;
+	char hdrBuf[35];
+
+	sprintf(hdrBuf, "Content-type: text/html%s%s", crlf, crlf);
+
+	frh.responseCode = status;
+	frh.reasonText = reason;
+	frh.headerText = hdrBuf;
+
+	rc = context->ServerSupport(context, kWriteResponseHeaders, &frh, NULL, 0, &errID);
+	rc = context->WriteClient(context, body, strlen(body), 0, &errID);
+}
+
+/* Called to reject a URI that contains the string "web-inf". We block
+ * these because they may indicate an attempt to invoke arbitrary code.
+ */
+static unsigned int RejectBadURI(FilterContext *context)
+{
+	static char *msg = "<HTML><BODY><H1>Access is Forbidden</H1></BODY></HTML>";
+
+	SimpleResponse(context, 403, "Forbidden", msg);
+	return kFilterHandledRequest;
+}
+
+/* Allocate space for a string given a start pointer and an end pointer
+ * and return a pointer to the allocated, copied string.
+ */
+static char *MemDup(private_ws_t *ws, const char *start, const char *end)
+{
+	char *out = NULL;
+
+	if (start != NULL && end != NULL && end > start)
+	{
+		int len = end - start;
+		out = jk_pool_alloc(&ws->p, len + 1);
+		memcpy(out, start, len);
+		out[len] = '\0';
+	}
+
+	return out;
+}
+
+/* Given all the HTTP headers as a single string parse them into individual
+ * name, value pairs. Called twice: once to work out how many headers there
+ * are, then again to copy them.
+ */
+static int ParseHeaders(private_ws_t *ws, const char *hdrs, int hdrsz, jk_ws_service_t *s)
+{
+	int hdrCount = 0;
+	const char *limit = hdrs + hdrsz;
+	const char *name, *nameEnd;
+	const char *value, *valueEnd;
+
+	while (hdrs < limit)
+	{
+		/* Skip line *before* doing anything, cos we want to lose the first line which
+		 * contains the request.
+		 */
+		while (hdrs < limit && (*hdrs != '\n' && *hdrs != '\r'))
+			hdrs++;
+		while (hdrs < limit && (*hdrs == '\n' || *hdrs == '\r'))
+			hdrs++;
+
+		if (hdrs >= limit)
+			break;
+
+		name = nameEnd = value = valueEnd = NULL;
+
+		name = hdrs;
+		while (hdrs < limit && *hdrs >= ' ' && *hdrs != ':')
+			hdrs++;
+		nameEnd = hdrs;
+
+		if (hdrs < limit && *hdrs == ':')
+		{
+			hdrs++;
+			while (hdrs < limit && (*hdrs == ' ' || *hdrs == '\t'))
+				hdrs++;
+			value = hdrs;
+			while (hdrs < limit && *hdrs >= ' ')
+				hdrs++;
+			valueEnd = hdrs;
+		}
+
+		if (s->headers_names != NULL && s->headers_values != NULL)
+		{
+			s->headers_names[hdrCount]	= MemDup(ws, name, nameEnd);
+			s->headers_values[hdrCount] = MemDup(ws, value, valueEnd);
+			DEBUG(("%s = %s\n", s->headers_names[hdrCount], s->headers_values[hdrCount]));
+		}
+		hdrCount++;
+	}
+
+	return hdrCount;
+}
+
+/* Set up all the necessary jk_* workspace based on the current HTTP request.
+ */
+static int InitService(private_ws_t *ws, jk_ws_service_t *s)
+{
+	/* This is the only fixed size buffer left. It won't be overflowed
+	 * because the Domino API that reads into the buffer accepts a length
+	 * constraint, and it's unlikely ever to be exhausted because the
+	 * strings being will typically be short, but it's still aesthetically
+	 * troublesome.
+	 */
+	char workBuf[16 * 1024];
+	FilterRequest fr;
+	char *hdrs, *qp;
+	int hdrsz;
+	int errID;
+	int hdrCount;
+	int rc /*, dummy*/;
+
+	static char *methodName[] = { "", "HEAD", "GET", "POST", "PUT", "DELETE" };
+
+	rc = ws->context->GetRequest(ws->context, &fr, &errID);
+
+	s->jvm_route		= NULL;
+	s->start_response	= StartResponse;
+	s->read				= Read;
+	s->write			= Write;
+
+	s->req_uri = jk_pool_strdup(&ws->p, fr.URL);
+	s->query_string = NULL;
+	if (qp = strchr(s->req_uri, '?'), qp != NULL)
+	{
+		*qp++ = '\0';
+		if (strlen(qp))
+			s->query_string = qp;
+	}
+
+	GETVARIABLE("AUTH_TYPE", &s->auth_type, "");
+	GETVARIABLE("REMOTE_USER", &s->remote_user, "");
+	GETVARIABLE("SERVER_PROTOCOL", &s->protocol, "");
+	GETVARIABLE("REMOTE_HOST", &s->remote_host, "");
+	GETVARIABLE("REMOTE_ADDR", &s->remote_addr, "");
+	GETVARIABLE("SERVER_NAME", &s->server_name, "");
+	GETVARIABLEINT("SERVER_PORT", &s->server_port, 80);
+	GETVARIABLE("SERVER_SOFTWARE", &s->server_software, SERVERDFLT);
+	GETVARIABLEINT("CONTENT_LENGTH", &s->content_length, 0);
+
+	/* SSL Support
+	 */
+	GETVARIABLEBOOL("HTTPS", &s->is_ssl, 0);
+
+
+	if (ws->reqData->requestMethod < 0 ||
+
+		ws->reqData->requestMethod >= sizeof(methodName) / sizeof(methodName[0]))
+
+		return JK_FALSE;
+
+
+
+	s->method = methodName[ws->reqData->requestMethod];
+
+
+
+	s->headers_names	= NULL;
+
+	s->headers_values	= NULL;
+
+	s->num_headers		= 0;
+
+
+	s->ssl_cert_len	= fr.clientCertLen;
+	s->ssl_cert		= fr.clientCert;
+	s->ssl_cipher	= NULL;		/* required by Servlet 2.3 Api */
+	s->ssl_session	= NULL;
+
+
+#if defined(JK_VERSION) && JK_VERSION >= MAKEVERSION(1, 2, 0, 1)
+	s->ssl_key_size = -1;       /* required by Servlet 2.3 Api, added in jtc */
+#endif
+
+	if (s->is_ssl)
+	{
+		int dummy;
+
+#if 0
+
+		char *sslNames[] =
+		{
+			"CERT_ISSUER", "CERT_SUBJECT", "CERT_COOKIE", "CERT_FLAGS", "CERT_SERIALNUMBER",
+			"HTTPS_SERVER_SUBJECT", "HTTPS_SECRETKEYSIZE", "HTTPS_SERVER_ISSUER", "HTTPS_KEYSIZE"
+		};
+
+		char *sslValues[] =
+		{
+			NULL, NULL, NULL, NULL, NULL,
+			NULL, NULL, NULL, NULL
+		};
+
+		unsigned i, varCount = 0;
+#endif
+
+		DEBUG(("SSL request\n"));
+
+#if defined(JK_VERSION) && JK_VERSION >= MAKEVERSION(1, 2, 0, 1)
+		/* Read the variable into a dummy variable: we do this for the side effect of
+		 * reading it into workBuf.
+		 */
+		GETVARIABLEINT("HTTPS_KEYSIZE", &dummy, 0);
+		if (workBuf[0] == '[')
+			s->ssl_key_size = atoi(workBuf+1);
+#else
+		(void) dummy;
+#endif
+
+#if 0
+		for (i = 0; i < sizeof(sslNames)/sizeof(sslNames[0]); i++)
+		{
+			GETVARIABLE(sslNames[i], &sslValues[i], NULL);
+			if (sslValues[i]) varCount++;
+		}
+
+		/* Andy, some SSL vars must be mapped directly in  s->ssl_cipher,
+         * ssl->session and s->ssl_key_size
+		 * ie:
+		 * Cipher could be "RC4-MD5"
+		 * KeySize 128 (bits)
+	     * SessionID a string containing the UniqID used in SSL dialogue
+         */
+		if (varCount > 0)
+		{
+			unsigned j;
+
+			s->attributes_names = jk_pool_alloc(&ws->p, varCount * sizeof (char *));
+			s->attributes_values = jk_pool_alloc(&ws->p, varCount * sizeof (char *));
+
+			j = 0;
+			for (i = 0; i < sizeof(sslNames)/sizeof(sslNames[0]); i++)
+			{
+				if (sslValues[i])
+				{
+					s->attributes_names[j] = sslNames[i];
+					s->attributes_values[j] = sslValues[i];
+					j++;
+				}
+			}
+			s->num_attributes = varCount;
+		}
+#endif
+
+	}
+
+	/* Duplicate all the headers now */
+
+	hdrsz = ws->reqData->GetAllHeaders(ws->context, &hdrs, &errID);
+	DEBUG(("\nGot headers (length %d)\n--------\n%s\n--------\n\n", hdrsz, hdrs));
+
+	s->headers_names =
+	s->headers_values = NULL;
+	hdrCount = ParseHeaders(ws, hdrs, hdrsz, s);
+	DEBUG(("Found %d headers\n", hdrCount));
+	s->num_headers = hdrCount;
+	s->headers_names	= jk_pool_alloc(&ws->p, hdrCount * sizeof(char *));
+	s->headers_values	= jk_pool_alloc(&ws->p, hdrCount * sizeof(char *));
+	hdrCount = ParseHeaders(ws, hdrs, hdrsz, s);
+
+	return JK_TRUE;
+}
+
+/* Handle an HTTP request. Works out whether Tomcat will be interested then either
+ * despatches it to Tomcat or passes it back to Domino.
+ */
+static unsigned int ParsedRequest(FilterContext *context, FilterParsedRequest *reqData)
+{
+	unsigned int errID;
+	int rc;
+	FilterRequest fr;
+	int result = kFilterNotHandled;
+
+	DEBUG(("\nParsedRequest starting\n"));
+
+	rc = context->GetRequest(context, &fr, &errID);
+
+	if (fr.URL && strlen(fr.URL))
+	{
+		char *uri = fr.URL;
+		char *workerName, *qp, *turi;
+
+		if (!initDone)
+		{
+			/* One time initialisation which is deferred so that we have the name of
+			 * the server software to plug into worker_env
+			 */
+			int ok = JK_FALSE;
+			jk_map_t *map = NULL;
+
+			DEBUG(("Initialising worker map\n"));
+
+			if (map_alloc(&map))
+			{
+				if (map_read_properties(map, workerMountFile))
+					if (uri_worker_map_alloc(&uw_map, map, logger))
+						ok = JK_TRUE;
+				map_free(&map);
+			}
+
+			DEBUG(("Got the URI worker map\n"));
+
+			if (ok)
+			{
+				ok = JK_FALSE;
+				DEBUG(("About to allocate map\n"));
+				if (map_alloc(&map))
+				{
+					DEBUG(("About to read %s\n", workerFile));
+					if (map_read_properties(map, workerFile))
+					{
+#if defined(JK_VERSION) && JK_VERSION >= MAKEVERSION(1, 2, 0, 1)
+						char server[256];
+
+						worker_env.uri_to_worker = uw_map;
+						if (context->GetServerVariable(context, "SERVER_SOFTWARE", server, sizeof(server)-1, &errID))
+							worker_env.server_name = jk_pool_strdup(&cfgPool, server);
+						else
+							worker_env.server_name = SERVERDFLT;
+
+						DEBUG(("Server name %s\n", worker_env.server_name));
+
+						if (wc_open(map, &worker_env, logger))
+							ok = JK_TRUE;
+#else
+						if (wc_open(map, logger))
+							ok = JK_TRUE;
+#endif
+						DEBUG(("OK = %d\n", ok));
+					}
+
+					DEBUG(("Read %s, OK = %d\n", workerFile, ok));
+					map_free(&map);
+				}
+			}
+
+			if (!ok) return kFilterError;
+			initDone = JK_TRUE;
+                }
+
+                turi = strdup(uri);
+                if (qp = strchr(turi, '?'), tqp != NULL) *qp = '\0';
+                workerName = map_uri_to_worker(uw_map, turi, logger);
+                free(turi);
+
+		DEBUG(("Worker for this URL is %s\n", workerName));
+
+		if (NULL != workerName)
+		{
+			private_ws_t ws;
+			jk_ws_service_t s;
+			jk_pool_atom_t buf[SMALL_POOL_SIZE];
+
+			if (BadURI(uri))
+				return RejectBadURI(context);
+
+			/* Go dispatch the call */
+
+			jk_init_ws_service(&s);
+			jk_open_pool(&ws.p, buf, sizeof (buf));
+
+			ws.responseStarted	= JK_FALSE;
+			ws.context			= context;
+			ws.reqData			= reqData;
+
+			ws.reqSize = context->GetRequestContents(context, &ws.reqBuffer, &errID);
+
+			s.ws_private = &ws;
+			s.pool = &ws.p;
+
+			if (InitService(&ws, &s))
+			{
+				jk_worker_t *worker = wc_get_worker_for_name(workerName, logger);
+
+				jk_log(logger, JK_LOG_DEBUG, "HttpExtensionProc %s a worker for name %s\n",
+					   worker ? "got" : "could not get", workerName);
+
+				if (worker)
+				{
+					jk_endpoint_t *e = NULL;
+
+					if (worker->get_endpoint(worker, &e, logger))
+					{
+						int recover = JK_FALSE;
+
+						if (e->service(e, &s, logger, &recover))
+						{
+							result = kFilterHandledRequest;
+							jk_log(logger, JK_LOG_DEBUG, "HttpExtensionProc service() returned OK\n");
+							DEBUG(("HttpExtensionProc service() returned OK\n"));
+						}
+						else
+						{
+							result = kFilterError;
+							jk_log(logger, JK_LOG_ERROR, "HttpExtensionProc error, service() failed\n");
+							DEBUG(("HttpExtensionProc error, service() failed\n"));
+						}
+						e->done(&e, logger);
+					}
+				}
+				else
+				{
+					jk_log(logger, JK_LOG_ERROR,
+						   "HttpExtensionProc error, could not get a worker for name %s\n",
+						   workerName);
+				}
+			}
+
+			jk_close_pool(&ws.p);
+		}
+	}
+
+	return result;
+}
diff --git a/connectors/jk/native/domino/mkini.sh b/connectors/jk/native/domino/mkini.sh
new file mode 100755
index 0000000..32ed3f0
--- /dev/null
+++ b/connectors/jk/native/domino/mkini.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+echo "log_file=$1/logs/domino.log"
+echo "log_level=debug"
+echo "worker_file=$1/conf/workers.properties"
+echo "worker_mount_file=$1/conf/uriworkermap.properties"
+echo "tomcat_start=$1/bin/tomcat.sh start"
+echo "tomcat_stop=$1/bin/tomcat.sh stop"
diff --git a/connectors/jk/native/domino/tomcat_redirector.reg b/connectors/jk/native/domino/tomcat_redirector.reg
new file mode 100755
index 0000000..c1fbb52
--- /dev/null
+++ b/connectors/jk/native/domino/tomcat_redirector.reg
@@ -0,0 +1,9 @@
+REGEDIT4
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Dsapi Redirector\1.0]
+"log_file"="D:\\tomcat\\logs\\domino.log"
+"log_level"="debug"
+"worker_file"="D:\\tomcat\\conf\\workers.properties"
+"worker_mount_file"="D:\\tomcat\\conf\\uriworkermap.properties"
+"tomcat_start"="D:\\tomcat\\bin\\tomcat.bat start"
+"tomcat_stop"="D:\\tomcat\\bin\\tomcat.bat stop"
diff --git a/connectors/jk/native/iis/README b/connectors/jk/native/iis/README
new file mode 100644
index 0000000..30267d7
--- /dev/null
+++ b/connectors/jk/native/iis/README
@@ -0,0 +1,46 @@
+ABOUT
+-----
+
+The Tomcat redirector was developed using Visual C++ Ver.6.0, 
+so having this environment is a prerequisite if you want to perform 
+a custom build.
+
+REQUIREMENT
+-----------
+
+* MS VC 6.0 (+ update, latest service pack is sp5)
+  isapi_redirector.dll can be built using the command line tools, or 
+  from within the Visual Studio IDE Workbench. The command line build 
+  requires the environment to reflect the PATH, INCLUDE, LIB and other 
+  variables that can be configured with the vcvars32 batch file: 
+  
+  "c:\Program Files\DevStudio\VC\Bin\vcvars32.bat"
+
+* MS PLATFORM SDK
+  Visual C++ 6.0 builds require an updated Microsoft Windows Platform SDK 
+  (http://www.microsoft.com/msdownload/platformsdk/sdkupdate/) to enable 
+  some isapi_redirector.dll features. For command line builds,
+  the Platform SDK environment is prepared by the setenv batch file:
+  
+  "c:\Program Files\Microsoft Platform SDK\setenv.bat"
+
+  Note that the Windows Platform SDK is only needed if you want authenticate 
+  using IIS to compile a isapi_redirector.dll.. 
+
+
+
+BUILDING
+--------
+ 
+The steps that you need to take are:
+
+   1. Change directory to the isapi redirector plugins source directory.
+   
+   2. Execute the following command:
+      MSDEV isapi.dsp /MAKE ALL
+      If msdev is not in your path, enter the full path to msdev.exe
+
+This will build both release and debug versions of the redirector plugin.
+
+An alternative will be to open the isapi workspace file (isapi.dsw) in msdev and 
+build it using the build menu.
diff --git a/connectors/jk/native/iis/isapi.def b/connectors/jk/native/iis/isapi.def
new file mode 100644
index 0000000..cd05816
--- /dev/null
+++ b/connectors/jk/native/iis/isapi.def
@@ -0,0 +1,9 @@
+LIBRARY	     "isapi_redirect"
+
+EXPORTS
+	HttpFilterProc
+	GetFilterVersion
+	GetExtensionVersion
+	HttpExtensionProc	
+	TerminateFilter
+	TerminateExtension
diff --git a/connectors/jk/native/iis/isapi.dsp b/connectors/jk/native/iis/isapi.dsp
new file mode 100644
index 0000000..93a4bd1
--- /dev/null
+++ b/connectors/jk/native/iis/isapi.dsp
@@ -0,0 +1,279 @@
+# Microsoft Developer Studio Project File - Name="isapi" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=isapi - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "isapi.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "isapi.mak" CFG="isapi - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "isapi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "isapi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "isapi - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "isapi_release"
+# PROP Intermediate_Dir "isapi_release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ISAPI_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\common" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ISAPI_EXPORTS" /D "HAVE_JNI" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 wsock32.lib advapi32.lib /nologo /dll /machine:I386 /out:"isapi_release\isapi_redirect.dll"
+
+!ELSEIF  "$(CFG)" == "isapi - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "isapi_debug"
+# PROP Intermediate_Dir "isapi_debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ISAPI_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\common" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ISAPI_EXPORTS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 wsock32.lib advapi32.lib /nologo /dll /debug /machine:I386 /out:"isapi_debug\isapi_redirect.dll" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "isapi - Win32 Release"
+# Name "isapi - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\common\jk_ajp12_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp_common.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_connect.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_context.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_isapi_plugin.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_jni_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_lb_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_msg_buff.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_nwmain.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_sockbuf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_uri_worker_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_util.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp_common.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_channel.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_connect.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_context.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_env.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_global.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_jni_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_lb_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_msg_buff.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_mt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_service.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_uri_worker_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_util.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_version.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\isapi.def
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/connectors/jk/native/iis/isapi_install.vbs b/connectors/jk/native/iis/isapi_install.vbs
new file mode 100644
index 0000000..56f58ac
--- /dev/null
+++ b/connectors/jk/native/iis/isapi_install.vbs
@@ -0,0 +1,223 @@
+'
+' Copyright 1999-2004 The Apache Software Foundation
+'
+' Licensed under the Apache License, Version 2.0 (the "License");
+' you may not use this file except in compliance with the License.
+' You may obtain a copy of the License at
+'
+'    http://www.apache.org/licenses/LICENSE-2.0
+'
+' Unless required by applicable law or agreed to in writing, software
+' distributed under the License is distributed on an "AS IS" BASIS,
+' WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+' See the License for the specific language governing permissions and
+' limitations under the License.
+'
+
+' =========================================================================
+' Description: Install script for Tomcat ISAPI redirector                              
+' Author:      Peter S. Horne <horneps@yahoo.com.au>                           
+' Version:     $Revision$                                           
+' =========================================================================
+'
+' This script automatically installs the tomcat isapi_redirector for use in
+' both out-of and in-process installations on IIS/Win2K. See the command line
+' usage section for usage instructions.
+
+'
+'  Check the command line
+'
+set args = wscript.arguments
+if args.count <> 6 then 
+	info ""
+	info "Tomcat ISAPI Redirector Installation Utility"
+	info "usage: isapi_install <server> <fdir> <worker> <mount> <log> <level>"
+	info "	server:	The Web Server Name (for example 'Default Web Site')"
+	info "	fdir:	the full path to the directory that contains the isapi filter"
+	info "	worker:	Full path and file name of the worker properties file"
+	info "	mount:	Full path and file name of the worker mount properties file"
+	info "	log:	Full path and file name of the log file"
+	info "	level:	The log level emerg | info"
+	info "(Re-runs are ok and will change/reset settings)"
+	info ""
+	fail "Incorrect Arguments"
+end if
+
+' Setup the args
+serverName = args(0)
+filterDir = args(1)
+filterName = "jakarta"
+filterLib = "\isapi_redirect.dll"
+workerFile = args(2)
+mountFile = args(3)
+logFile = args(4)
+logLevel = args(5)
+
+'
+' Get a shell
+'
+dim shell
+set shell = WScript.CreateObject("WScript.Shell")
+
+'
+' Find the indicated server from all the servers in the service 
+' Note: they aren't all Web!
+'
+set service = GetObject("IIS://LocalHost/W3SVC" )
+serverId = ""
+for each thing in service
+	 if thing.Class = "IIsWebServer" then
+		if thing.ServerComment = serverName then 
+			set server = thing
+			serverId = thing.name
+			exit for
+		end if
+	end if
+next
+if serverId = "" then fail "Server " + serverName + " not found."
+info "Found Server <" + serverName + "> at index [" + serverId + "]."
+
+'
+' Stop everything to release any dlls - needed for a re-install
+'
+' info "Stopping server <" + serverName + ">..."
+' server.stop
+' info "Done"
+
+'
+' Get a handle to the filters for the server - we process all errors
+'
+On Error Resume Next
+dim filters
+set filters = GetObject("IIS://LocalHost/W3SVC/" + serverId + "/Filters")
+if err then 
+	err.clear
+	info "Filters not found for server - creating"
+	set filters = server.create( "IIsFilters", "Filters" )
+	filters.setInfo
+	if err then fail "Error Creating Filters"
+end if
+info "Got Filters"
+
+'
+' Create the filter - if it fails then delete it and try again
+'
+name = filterName
+info "Creating Filter  - " + filterName
+dim filter
+set filter = filters.Create( "IISFilter", filterName )
+if err then
+	err.clear
+	info "Filter exists - deleting"
+	filters.delete "IISFilter", filterName
+	if err then fail "Error Deleting Filter"
+	set filter = filters.Create( "IISFilter", filterName )
+	if err then fail "Error Creating Filter"
+end if
+info "Created Filter"
+
+'
+' Set the filter info and save it
+'
+filter.FilterPath = filterDir + filterLib  
+filter.FilterEnabled=true
+filter.description = filterName
+filter.notifyOrderHigh = true
+filter.setInfo
+
+'
+' Set the load order - only if it's not in the list already
+'
+on error goto 0
+loadOrders = filters.FilterLoadOrder
+list = Split( loadOrders, "," )
+found = false
+for each item in list
+	if Trim( item ) = filterName then found = true
+next
+
+if found = false then 
+	info "Filter is not in load order - adding now."
+	if len(loadOrders) <> 0  then loadOrders = loadOrders + ","
+	filters.FilterLoadOrder = loadOrders + filterName
+	filters.setInfo
+	info "Filter added."
+else
+	info "Filter already exists in load order - no update required."
+end if
+
+'
+' Set the registry up
+' 
+regRoot = "HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\1.0\"
+err.clear
+on error resume next
+shell.RegDelete( regRoot )
+if err then 
+	info "Entering Registry Information for the first time"
+else 
+	info "Deleted existing Registry Setting"
+end if
+
+on error goto 0
+info "Updating Registry"
+shell.RegWrite regRoot + "extension_uri", "/jakarta/isapi_redirect.dll"
+shell.RegWrite regRoot + "log_file", logFile
+shell.RegWrite regRoot + "log_level", logLevel
+shell.RegWrite regRoot + "worker_file", workerFile
+shell.RegWrite regRoot + "worker_mount_file", mountFile
+info "Registry Settings Created"
+
+'
+' Finally, create the virtual directory matching th extension uri
+' 
+on error goto 0
+set root = GetObject( "IIS://LocalHost/W3SVC/" + serverID + "/ROOT" )
+on error resume next
+set vdir = root.Create("IISWebVirtualDir", filterName )
+if err then
+	info "Directory exists - deleting"
+	on error resume next
+	root.delete "IISWebVirtualDir", filterName
+	root.setInfo
+	if err then fail "Error Deleting Directory"
+	set vdir = root.create("IISWebVirtualDir", filterName )
+	if err then fail "Error Creating Directory"
+end if
+info "Directory Created"
+
+' Set the directory information - make it an application directory
+info "Setting Directory Information"
+vdir.AppCreate2 1
+vdir.AccessExecute = TRUE
+vdir.AppFriendlyName = filterName
+vdir.AccessRead = false
+vdir.ContentIndexed = false
+vdir.Path = filterDir
+vdir.setInfo
+if err then fail "Error saving new directory"
+info "Directory Saved"
+'
+' Re Start 
+'
+' info "Starting server <" + serverName + ">..."
+' server.start
+' info "Done"
+
+info "All done... Bye."
+wscript.quit(0)
+
+' 
+' Helper function for snafus
+'
+function fail( message )
+	wscript.echo "E: " + message
+	wscript.quit(1)
+end function
+
+'
+' Helper function for info
+'
+function info( message )
+	wscript.echo " " + message
+end function 
diff --git a/connectors/jk/native/iis/isapi_redirect.reg b/connectors/jk/native/iis/isapi_redirect.reg
new file mode 100644
index 0000000..44641ba
--- /dev/null
+++ b/connectors/jk/native/iis/isapi_redirect.reg
@@ -0,0 +1,9 @@
+REGEDIT4
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\1.0]
+"log_file"="C:\\tomcat\\logs\\isapi.log"
+"log_level"="debug"
+"worker_file"="C:\\tomcat\\conf\\workers.properties"
+"worker_mount_file"="C:\\tomcat\\conf\\uriworkermap.properties"
+"tomcat_start"="C:\\tomcat\\bin\\tomcat.bat start"
+"tomcat_stop"="C:\\tomcat\\bin\\tomcat.bat stop"
diff --git a/connectors/jk/native/iis/jk_isapi_plugin.c b/connectors/jk/native/iis/jk_isapi_plugin.c
new file mode 100644
index 0000000..13d9878
--- /dev/null
+++ b/connectors/jk/native/iis/jk_isapi_plugin.c
@@ -0,0 +1,1549 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: ISAPI plugin for IIS/PWS                                   *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Larry Isaacs <larryi@apache.org>                           *
+ * Author:      Ignacio J. Ortega <nacho@apache.org>                       *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+// This define is needed to include wincrypt,h, needed to get client certificates
+#define _WIN32_WINNT 0x0400
+
+#include <httpext.h>
+#include <httpfilt.h>
+#include <wininet.h>
+
+#include "jk_global.h"
+#include "jk_util.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+#include "jk_uri_worker_map.h"
+
+#define VERSION_STRING "Jakarta/ISAPI/" JK_VERSTRING
+
+#define DEFAULT_WORKER_NAME ("ajp13")
+/*
+ * We use special headers to pass values from the filter to the 
+ * extension. These values are:
+ *
+ * 1. The real URI before redirection took place
+ * 2. The name of the worker to be used.
+ * 3. The contents of the Translate header, if any
+ *
+ */
+#define URI_HEADER_NAME         ("TOMCATURI:")
+#define QUERY_HEADER_NAME       ("TOMCATQUERY:")
+#define WORKER_HEADER_NAME      ("TOMCATWORKER:")
+#define TOMCAT_TRANSLATE_HEADER_NAME ("TOMCATTRANSLATE:")
+#define CONTENT_LENGTH          ("CONTENT_LENGTH:")
+
+#define HTTP_URI_HEADER_NAME     ("HTTP_TOMCATURI")
+#define HTTP_QUERY_HEADER_NAME   ("HTTP_TOMCATQUERY")
+#define HTTP_WORKER_HEADER_NAME  ("HTTP_TOMCATWORKER")
+
+#define REGISTRY_LOCATION       ("Software\\Apache Software Foundation\\Jakarta Isapi Redirector\\1.0")
+#define EXTENSION_URI_TAG       ("extension_uri")
+
+#define URI_SELECT_TAG          ("uri_select")
+
+#define URI_SELECT_PARSED_VERB      ("parsed")
+#define URI_SELECT_UNPARSED_VERB    ("unparsed")
+#define URI_SELECT_ESCAPED_VERB     ("escaped")
+
+#define BAD_REQUEST		-1
+#define BAD_PATH		-2
+#define MAX_SERVERNAME			128
+
+
+#define GET_SERVER_VARIABLE_VALUE(name, place) {    \
+    (place) = NULL;                                   \
+    huge_buf_sz = sizeof(huge_buf);                 \
+    if (get_server_value(private_data->lpEcb,        \
+                        (name),                     \
+                        huge_buf,                   \
+                        huge_buf_sz,                \
+                        "")) {                      \
+        (place) = jk_pool_strdup(&private_data->p, huge_buf);   \
+    }   \
+}\
+
+#define GET_SERVER_VARIABLE_VALUE_INT(name, place, def) {   \
+    huge_buf_sz = sizeof(huge_buf);                 \
+    if (get_server_value(private_data->lpEcb,        \
+                        (name),                     \
+                        huge_buf,                   \
+                        huge_buf_sz,                \
+                        "")) {                      \
+        (place) = atoi(huge_buf);                   \
+        if (0 == (place)) {                          \
+            (place) = def;                          \
+        }                                           \
+    } else {    \
+        (place) = def;  \
+    }           \
+}\
+
+static char  ini_file_name[MAX_PATH];
+static int   using_ini_file = JK_FALSE;
+static int   is_inited = JK_FALSE;
+static int	 is_mapread	= JK_FALSE;
+static int   iis5 = -1;
+
+static jk_uri_worker_map_t *uw_map = NULL; 
+static jk_logger_t *logger = NULL; 
+static char *SERVER_NAME = "SERVER_NAME";
+static char *SERVER_SOFTWARE = "SERVER_SOFTWARE";
+
+
+static char extension_uri[INTERNET_MAX_URL_LENGTH] = "/jakarta/isapi_redirect.dll";
+static char log_file[MAX_PATH * 2];
+static int  log_level = JK_LOG_EMERG_LEVEL;
+static char worker_file[MAX_PATH * 2];
+static char worker_mount_file[MAX_PATH * 2];
+
+#define URI_SELECT_OPT_PARSED       0
+#define URI_SELECT_OPT_UNPARSED     1
+#define URI_SELECT_OPT_ESCAPED      2
+
+static int uri_select_option = URI_SELECT_OPT_PARSED;
+
+static jk_worker_env_t worker_env;
+
+struct isapi_private_data {
+    jk_pool_t p;
+    
+    int request_started;
+    unsigned bytes_read_so_far;
+    LPEXTENSION_CONTROL_BLOCK  lpEcb;
+};
+typedef struct isapi_private_data isapi_private_data_t;
+
+
+static int JK_METHOD start_response(jk_ws_service_t *s,
+                                    int status,
+                                    const char *reason,
+                                    const char * const *header_names,
+                                    const char * const *header_values,
+                                    unsigned num_of_headers);
+
+static int JK_METHOD read(jk_ws_service_t *s,
+                          void *b,
+                          unsigned l,
+                          unsigned *a);
+
+static int JK_METHOD write(jk_ws_service_t *s,
+                           const void *b,
+                           unsigned l);
+
+static int init_ws_service(isapi_private_data_t *private_data,
+                           jk_ws_service_t *s,
+                           char **worker_name);
+
+static int init_jk(char *serverName);
+
+static int initialize_extension(void);
+
+static int read_registry_init_data(void);
+
+static int get_registry_config_parameter(HKEY hkey,
+                                         const char *tag, 
+                                         char *b,
+                                         DWORD sz);
+
+
+static int get_server_value(LPEXTENSION_CONTROL_BLOCK lpEcb,
+                            char *name,
+                            char  *buf,
+                            DWORD bufsz,
+                            char  *def_val);
+
+static int base64_encode_cert_len(int len);
+
+static int base64_encode_cert(char *encoded,
+                              const unsigned char *string,
+                              int len);
+
+
+static char x2c(const char *what)
+{
+    register char digit;
+
+    digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
+    digit *= 16;
+    digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
+    return (digit);
+}
+
+static int unescape_url(char *url)
+{
+    register int x, y, badesc, badpath;
+
+    badesc = 0;
+    badpath = 0;
+    for (x = 0, y = 0; url[y]; ++x, ++y) {
+        if (url[y] != '%')
+            url[x] = url[y];
+        else {
+            if (!isxdigit(url[y + 1]) || !isxdigit(url[y + 2])) {
+                badesc = 1;
+                url[x] = '%';
+            }
+            else {
+                url[x] = x2c(&url[y + 1]);
+                y += 2;
+                if (url[x] == '/' || url[x] == '\0')
+                    badpath = 1;
+            }
+        }
+    }
+    url[x] = '\0';
+    if (badesc)
+            return BAD_REQUEST;
+    else if (badpath)
+            return BAD_PATH;
+    else
+            return 0;
+}
+
+static void getparents(char *name)
+{
+    int l, w;
+
+    /* Four paseses, as per RFC 1808 */
+    /* a) remove ./ path segments */
+
+    for (l = 0, w = 0; name[l] != '\0';) {
+        if (name[l] == '.' && name[l + 1] == '/' && (l == 0 || name[l - 1] == '/'))
+            l += 2;
+        else
+            name[w++] = name[l++];
+    }
+
+    /* b) remove trailing . path, segment */
+    if (w == 1 && name[0] == '.')
+        w--;
+    else if (w > 1 && name[w - 1] == '.' && name[w - 2] == '/')
+        w--;
+    name[w] = '\0';
+
+    /* c) remove all xx/../ segments. (including leading ../ and /../) */
+    l = 0;
+
+    while (name[l] != '\0') {
+        if (name[l] == '.' && name[l + 1] == '.' && name[l + 2] == '/' &&
+            (l == 0 || name[l - 1] == '/')) {
+            register int m = l + 3, n;
+
+            l = l - 2;
+            if (l >= 0) {
+                while (l >= 0 && name[l] != '/')
+                    l--;
+                l++;
+            }
+            else
+                l = 0;
+            n = l;
+            while ((name[n] = name[m]))
+                (++n, ++m);
+        }
+        else
+            ++l;
+    }
+
+    /* d) remove trailing xx/.. segment. */
+    if (l == 2 && name[0] == '.' && name[1] == '.')
+        name[0] = '\0';
+    else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.' && name[l - 3] == '/') {
+        l = l - 4;
+        if (l >= 0) {
+            while (l >= 0 && name[l] != '/')
+                l--;
+            l++;
+        }
+        else
+            l = 0;
+        name[l] = '\0';
+    }
+}
+
+/* Apache code to escape a URL */
+
+#define T_OS_ESCAPE_PATH	(4)
+
+static const unsigned char test_char_table[256] = {
+    0,14,14,14,14,14,14,14,14,14,15,14,14,14,14,14,14,14,14,14,
+    14,14,14,14,14,14,14,14,14,14,14,14,14,0,7,6,1,6,1,1,
+    9,9,1,0,8,0,0,10,0,0,0,0,0,0,0,0,0,0,8,15,
+    15,8,15,15,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,15,15,15,7,0,7,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,15,7,15,1,14,6,6,6,6,6,6,6,6,6,6,6,6,
+    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 
+};
+
+#define TEST_CHAR(c, f)	(test_char_table[(unsigned)(c)] & (f))
+
+static const char c2x_table[] = "0123456789abcdef";
+
+static unsigned char *c2x(unsigned what, unsigned char *where)
+{
+    *where++ = '%';
+    *where++ = c2x_table[what >> 4];
+    *where++ = c2x_table[what & 0xf];
+    return where;
+}
+
+static int escape_url(const char *path, char *dest, int destsize)
+{
+    const unsigned char *s = (const unsigned char *)path;
+    unsigned char *d = (unsigned char *)dest;
+    unsigned char *e = dest + destsize - 1;
+    unsigned char *ee = dest + destsize - 3;
+    unsigned c;
+
+    while ((c = *s)) {
+	if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
+            if (d >= ee )
+                return JK_FALSE;
+	    d = c2x(c, d);
+	}
+	else {
+            if (d >= e )
+                return JK_FALSE;
+	    *d++ = c;
+	}
+	++s;
+    }
+    *d = '\0';
+    return JK_TRUE;
+}
+
+static int uri_is_web_inf(char *uri)
+{
+    char *c = uri;
+    while(*c) {
+        *c = tolower(*c);
+        c++;
+    }                    
+    if(strstr(uri, "web-inf")) {
+        return JK_TRUE;
+    }
+    if(strstr(uri, "meta-inf")) {
+        return JK_TRUE;
+    }
+
+    return JK_FALSE;
+}
+
+static void write_error_response(PHTTP_FILTER_CONTEXT pfc,char *status,char * msg)
+{
+    char crlf[3] = { (char)13, (char)10, '\0' };
+    char ctype[30];
+    DWORD len = strlen(msg);
+
+    sprintf(ctype, 
+            "Content-Type:text/html%s%s", 
+            crlf, 
+            crlf);
+
+    /* reject !!! */
+    pfc->ServerSupportFunction(pfc, 
+                               SF_REQ_SEND_RESPONSE_HEADER,
+                               status,
+                               (DWORD)crlf,
+                               (DWORD)ctype);
+    pfc->WriteClient(pfc, msg, &len, 0);
+}
+
+
+static int JK_METHOD start_response(jk_ws_service_t *s,
+                                    int status,
+                                    const char *reason,
+                                    const char * const *header_names,
+                                    const char * const *header_values,
+                                    unsigned num_of_headers)
+{
+    static char crlf[3] = { (char)13, (char)10, '\0' };
+
+    jk_log(logger, JK_LOG_DEBUG, 
+           "Into jk_ws_service_t::start_response\n");
+
+    if (status < 100 || status > 1000) {
+        jk_log(logger, JK_LOG_ERROR, 
+               "jk_ws_service_t::start_response, invalid status %d\n", status);
+        return JK_FALSE;
+    }
+
+    if (s && s->ws_private) {
+        isapi_private_data_t *p = s->ws_private;
+        if (!p->request_started) {
+            DWORD len_of_status;
+            char *status_str;
+            char *headers_str;
+
+            p->request_started = JK_TRUE;
+
+            /*
+             * Create the status line
+             */
+            if (!reason) {
+                reason = "";
+            }
+            status_str = (char *)_alloca((6 + strlen(reason)) * sizeof(char));
+            sprintf(status_str, "%d %s", status, reason);
+            len_of_status = strlen(status_str); 
+        
+            /*
+             * Create response headers string
+             */
+            if (num_of_headers) {
+                unsigned i;
+                unsigned len_of_headers;
+                for(i = 0 , len_of_headers = 0 ; i < num_of_headers ; i++) {
+                    len_of_headers += strlen(header_names[i]);
+                    len_of_headers += strlen(header_values[i]);
+                    len_of_headers += 4; /* extra for colon, space and crlf */
+                }
+
+                len_of_headers += 3;  /* crlf and terminating null char */
+                headers_str = (char *)_alloca(len_of_headers * sizeof(char));
+                headers_str[0] = '\0';
+
+                for(i = 0 ; i < num_of_headers ; i++) {
+                    strcat(headers_str, header_names[i]);
+                    strcat(headers_str, ": ");
+                    strcat(headers_str, header_values[i]);
+                    strcat(headers_str, crlf);
+                }
+                strcat(headers_str, crlf);
+            } else {
+                headers_str = crlf;
+            }
+
+            if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID, 
+                                                HSE_REQ_SEND_RESPONSE_HEADER,
+                                                status_str,
+                                                (LPDWORD)&len_of_status,
+                                                (LPDWORD)headers_str)) {
+                jk_log(logger, JK_LOG_ERROR, 
+                       "jk_ws_service_t::start_response, ServerSupportFunction failed\n");
+                return JK_FALSE;
+            }       
+
+
+        }
+        return JK_TRUE;
+
+    }
+
+    jk_log(logger, JK_LOG_ERROR, 
+           "jk_ws_service_t::start_response, NULL parameters\n");
+
+    return JK_FALSE;
+}
+
+static int JK_METHOD read(jk_ws_service_t *s,
+                          void *b,
+                          unsigned l,
+                          unsigned *a)
+{
+    jk_log(logger, JK_LOG_DEBUG, 
+           "Into jk_ws_service_t::read\n");
+
+    if (s && s->ws_private && b && a) {
+        isapi_private_data_t *p = s->ws_private;
+        
+        *a = 0;
+        if (l) {
+            char *buf = b;
+            DWORD already_read = p->lpEcb->cbAvailable - p->bytes_read_so_far;
+            
+            if (already_read >= l) {
+                memcpy(buf, p->lpEcb->lpbData + p->bytes_read_so_far, l);
+                p->bytes_read_so_far += l;
+                *a = l;
+            } else {
+                /*
+                 * Try to copy what we already have 
+                 */
+                if (already_read > 0) {
+                    memcpy(buf, p->lpEcb->lpbData + p->bytes_read_so_far, already_read);
+                    buf += already_read;
+                    l   -= already_read;
+                    p->bytes_read_so_far = p->lpEcb->cbAvailable;
+                    
+                    *a = already_read;
+                }
+                
+                /*
+                 * Now try to read from the client ...
+                 */
+                if (p->lpEcb->ReadClient(p->lpEcb->ConnID, buf, &l)) {
+                    *a += l;            
+                } else {
+                    jk_log(logger, JK_LOG_ERROR, 
+                           "jk_ws_service_t::read, ReadClient failed\n");
+                    return JK_FALSE;
+                }                   
+            }
+        }
+        return JK_TRUE;
+    }
+
+    jk_log(logger, JK_LOG_ERROR, 
+           "jk_ws_service_t::read, NULL parameters\n");
+    return JK_FALSE;
+}
+
+static int JK_METHOD write(jk_ws_service_t *s,
+                           const void *b,
+                           unsigned l)
+{
+    jk_log(logger, JK_LOG_DEBUG, 
+           "Into jk_ws_service_t::write\n");
+
+    if (s && s->ws_private && b) {
+        isapi_private_data_t *p = s->ws_private;
+
+        if (l) {
+            unsigned written = 0;           
+            char *buf = (char *)b;
+
+            if (!p->request_started) {
+                start_response(s, 200, NULL, NULL, NULL, 0);
+            }
+
+            while(written < l) {
+                DWORD try_to_write = l - written;
+                if (!p->lpEcb->WriteClient(p->lpEcb->ConnID, 
+                                          buf + written, 
+                                          &try_to_write, 
+                                          0)) {
+                    jk_log(logger, JK_LOG_ERROR, 
+                           "jk_ws_service_t::write, WriteClient failed\n");
+                    return JK_FALSE;
+                }
+                written += try_to_write;
+            }
+        }
+
+        return JK_TRUE;
+
+    }
+
+    jk_log(logger, JK_LOG_ERROR, 
+           "jk_ws_service_t::write, NULL parameters\n");
+
+    return JK_FALSE;
+}
+
+BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
+{
+    ULONG http_filter_revision = HTTP_FILTER_REVISION;
+
+    pVer->dwFilterVersion = pVer->dwServerFilterVersion;
+                        
+    if (pVer->dwFilterVersion > http_filter_revision) {
+        pVer->dwFilterVersion = http_filter_revision;
+    }
+
+    pVer->dwFlags = SF_NOTIFY_ORDER_HIGH        | 
+                    SF_NOTIFY_SECURE_PORT       | 
+                    SF_NOTIFY_NONSECURE_PORT    |
+                    SF_NOTIFY_PREPROC_HEADERS   |
+                    SF_NOTIFY_AUTH_COMPLETE;
+                    
+    strcpy(pVer->lpszFilterDesc, VERSION_STRING);
+
+    if (!is_inited) {
+        return initialize_extension();
+    }
+
+    return TRUE;
+}
+
+DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc,
+                            DWORD dwNotificationType, 
+                            LPVOID pvNotification)
+{
+    /* Initialise jk */
+    if (is_inited && !is_mapread) {
+        char serverName[MAX_SERVERNAME];
+        DWORD dwLen = sizeof(serverName);
+
+        if (pfc->GetServerVariable(pfc, SERVER_NAME, serverName, &dwLen)){
+            if (dwLen > 0) serverName[dwLen-1] = '\0';
+            if (init_jk(serverName))
+                is_mapread = JK_TRUE;
+        }
+        /* If we can't read the map we become dormant */
+        if (!is_mapread)
+            is_inited = JK_FALSE;
+    }
+
+    if (is_inited && (iis5 < 0) ) {
+        char serverSoftware[256];
+        DWORD dwLen = sizeof(serverSoftware);
+		iis5=0;
+        if (pfc->GetServerVariable(pfc,SERVER_SOFTWARE, serverSoftware, &dwLen)){
+			iis5=(atof(serverSoftware + 14) >= 5.0);
+			if (iis5) {
+				jk_log(logger, JK_LOG_INFO,"Detected IIS >= 5.0\n");
+			} else {
+				jk_log(logger, JK_LOG_INFO,"Detected IIS < 5.0\n");
+			}
+        }
+    }
+
+    if (is_inited &&
+         (((SF_NOTIFY_PREPROC_HEADERS == dwNotificationType) && !iis5) ||
+		  ((SF_NOTIFY_AUTH_COMPLETE   == dwNotificationType) &&  iis5)
+		  )
+		)
+	{ 
+        char uri[INTERNET_MAX_URL_LENGTH]; 
+        char snuri[INTERNET_MAX_URL_LENGTH]="/";
+        char Host[INTERNET_MAX_URL_LENGTH]="";
+        char Port[INTERNET_MAX_URL_LENGTH]="";
+        char Translate[INTERNET_MAX_URL_LENGTH];
+		BOOL (WINAPI * GetHeader) 
+			(struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName, LPVOID lpvBuffer, LPDWORD lpdwSize );
+		BOOL (WINAPI * SetHeader) 
+			(struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName, LPSTR lpszValue );
+		BOOL (WINAPI * AddHeader) 
+			(struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,LPSTR lpszValue );
+        char *query;
+        DWORD sz = sizeof(uri);
+        DWORD szHost = sizeof(Host);
+        DWORD szPort = sizeof(Port);
+        DWORD szTranslate = sizeof(Translate);
+
+		if (iis5) {
+			GetHeader=((PHTTP_FILTER_AUTH_COMPLETE_INFO)pvNotification)->GetHeader;
+			SetHeader=((PHTTP_FILTER_AUTH_COMPLETE_INFO)pvNotification)->SetHeader;
+			AddHeader=((PHTTP_FILTER_AUTH_COMPLETE_INFO)pvNotification)->AddHeader;
+		} else {
+			GetHeader=((PHTTP_FILTER_PREPROC_HEADERS)pvNotification)->GetHeader;
+			SetHeader=((PHTTP_FILTER_PREPROC_HEADERS)pvNotification)->SetHeader;
+			AddHeader=((PHTTP_FILTER_PREPROC_HEADERS)pvNotification)->AddHeader;
+		}
+
+
+        jk_log(logger, JK_LOG_DEBUG, 
+               "HttpFilterProc started\n");
+
+
+        /*
+         * Just in case somebody set these headers in the request!
+         */
+        SetHeader(pfc, URI_HEADER_NAME, NULL);
+        SetHeader(pfc, QUERY_HEADER_NAME, NULL);
+        SetHeader(pfc, WORKER_HEADER_NAME, NULL);
+        SetHeader(pfc, TOMCAT_TRANSLATE_HEADER_NAME, NULL);
+        
+        if (!GetHeader(pfc, "url", (LPVOID)uri, (LPDWORD)&sz)) {
+            jk_log(logger, JK_LOG_ERROR, 
+                   "HttpFilterProc error while getting the url\n");
+            return SF_STATUS_REQ_ERROR;
+        }
+
+        if (strlen(uri)) {
+            int rc;
+            char *worker=0;
+            query = strchr(uri, '?');
+            if (query) {
+                *query++ = '\0';
+            }
+
+            rc = unescape_url(uri);
+            if (rc == BAD_REQUEST) {
+                jk_log(logger, JK_LOG_ERROR, 
+                       "HttpFilterProc [%s] contains one or more invalid escape sequences.\n", 
+                       uri);
+                write_error_response(pfc,"400 Bad Request",
+                        "<HTML><BODY><H1>Request contains invalid encoding</H1></BODY></HTML>");
+                return SF_STATUS_REQ_FINISHED;
+            }
+            else if(rc == BAD_PATH) {
+                jk_log(logger, JK_LOG_EMERG, 
+                       "HttpFilterProc [%s] contains forbidden escape sequences.\n", 
+                       uri);
+                write_error_response(pfc,"403 Forbidden",
+                        "<HTML><BODY><H1>Access is Forbidden</H1></BODY></HTML>");
+                return SF_STATUS_REQ_FINISHED;
+            }
+            getparents(uri);
+            if (pfc->GetServerVariable(pfc, SERVER_NAME, (LPVOID)Host, (LPDWORD)&szHost)){
+                if (szHost > 0) {
+                    Host[szHost-1] = '\0';
+                }
+            }
+            Port[0] = '\0';
+            if (pfc->GetServerVariable(pfc, "SERVER_PORT", (LPVOID)Port, (LPDWORD)&szPort)){
+                if (szPort > 0) {
+                    Port[szPort-1] = '\0';
+                }
+            }
+            szPort = atoi(Port);
+            if (szPort != 80 && szPort != 443 && szHost > 0){
+                strcat(Host,":");
+                strcat(Host,Port);
+            }
+            if (szHost > 0) {
+                strcat(snuri,Host);
+                strcat(snuri,uri);
+                jk_log(logger, JK_LOG_DEBUG, 
+                       "In HttpFilterProc Virtual Host redirection of %s\n", 
+                       snuri);
+                worker = map_uri_to_worker(uw_map, snuri, logger);
+            }
+            if (!worker) {
+                jk_log(logger, JK_LOG_DEBUG, 
+                       "In HttpFilterProc test Default redirection of %s\n", 
+                       uri);
+                worker = map_uri_to_worker(uw_map, uri, logger);
+            }
+
+            if (worker) {
+                char *forwardURI;
+
+                /* This is a servlet, should redirect ... */
+                jk_log(logger, JK_LOG_DEBUG, 
+                       "HttpFilterProc [%s] is a servlet url - should redirect to %s\n", 
+                       uri, worker);
+                
+                /* get URI we should forward */
+                if (uri_select_option == URI_SELECT_OPT_UNPARSED) {
+                    /* get original unparsed URI */
+                    GetHeader(pfc, "url", (LPVOID)uri, (LPDWORD)&sz);
+                    /* restore terminator for uri portion */
+                    if (query)
+                        *(query - 1) = '\0';
+                    jk_log(logger, JK_LOG_DEBUG, 
+                           "HttpFilterProc fowarding original URI [%s]\n",uri);
+                    forwardURI = uri;
+                } else if (uri_select_option == URI_SELECT_OPT_ESCAPED) {
+                    if (!escape_url(uri,snuri,INTERNET_MAX_URL_LENGTH)) {
+                        jk_log(logger, JK_LOG_ERROR, 
+                               "HttpFilterProc [%s] re-encoding request exceeds maximum buffer size.\n", 
+                               uri);
+                        write_error_response(pfc,"400 Bad Request",
+                                "<HTML><BODY><H1>Request contains too many characters that need to be encoded.</H1></BODY></HTML>");
+                        return SF_STATUS_REQ_FINISHED;
+                    }
+                    jk_log(logger, JK_LOG_DEBUG, 
+                           "HttpFilterProc fowarding escaped URI [%s]\n",snuri);
+                    forwardURI = snuri;
+                } else {
+                    forwardURI = uri;
+                }
+
+                if(!AddHeader(pfc, URI_HEADER_NAME, forwardURI) || 
+                   ( (query != NULL && strlen(query) > 0)
+                           ? !AddHeader(pfc, QUERY_HEADER_NAME, query) : FALSE ) || 
+                   !AddHeader(pfc, WORKER_HEADER_NAME, worker) ||
+                   !SetHeader(pfc, "url", extension_uri)) {
+                    jk_log(logger, JK_LOG_ERROR, 
+                           "HttpFilterProc error while adding request headers\n");
+                    return SF_STATUS_REQ_ERROR;
+                }
+				
+                /* Move Translate: header to a temporary header so
+                 * that the extension proc will be called.
+                 * This allows the servlet to handle 'Translate: f'.
+                 */
+                if(GetHeader(pfc, "Translate:", (LPVOID)Translate, (LPDWORD)&szTranslate) &&
+                    Translate != NULL && szTranslate > 0) {
+                    if (!AddHeader(pfc, TOMCAT_TRANSLATE_HEADER_NAME, Translate)) {
+                        jk_log(logger, JK_LOG_ERROR, 
+                          "HttpFilterProc error while adding Tomcat-Translate headers\n");
+                        return SF_STATUS_REQ_ERROR;
+                    }
+                SetHeader(pfc, "Translate:", NULL);
+                }
+            } else {
+                jk_log(logger, JK_LOG_DEBUG, 
+                       "HttpFilterProc [%s] is not a servlet url\n", 
+                       uri);
+            }
+
+            /*
+             * Check if somebody is feading us with his own TOMCAT data headers.
+             * We reject such postings !
+             */
+            jk_log(logger, JK_LOG_DEBUG, 
+                   "HttpFilterProc check if [%s] is points to the web-inf directory\n", 
+                   uri);
+
+            if(uri_is_web_inf(uri)) {
+                jk_log(logger, JK_LOG_EMERG, 
+                       "HttpFilterProc [%s] points to the web-inf or meta-inf directory.\nSomebody try to hack into the site!!!\n", 
+                       uri);
+
+                write_error_response(pfc,"403 Forbidden",
+                        "<HTML><BODY><H1>Access is Forbidden</H1></BODY></HTML>");
+                return SF_STATUS_REQ_FINISHED;
+            }
+        }
+    }
+    return SF_STATUS_REQ_NEXT_NOTIFICATION;
+}
+
+
+BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO  *pVer)
+{
+    pVer->dwExtensionVersion = MAKELONG( HSE_VERSION_MINOR,
+                                         HSE_VERSION_MAJOR );
+
+    strcpy(pVer->lpszExtensionDesc, VERSION_STRING);
+
+
+    if (!is_inited) {
+        return initialize_extension();
+    }
+
+    return TRUE;
+}
+
+DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK  lpEcb)
+{   
+    DWORD rc = HSE_STATUS_ERROR;
+
+    lpEcb->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
+
+    jk_log(logger, JK_LOG_DEBUG, 
+           "HttpExtensionProc started\n");
+
+	/* Initialise jk */
+	if (is_inited && !is_mapread) {
+		char serverName[MAX_SERVERNAME];
+		DWORD dwLen = sizeof(serverName);
+		if (lpEcb->GetServerVariable(lpEcb->ConnID, SERVER_NAME, serverName, &dwLen)){
+			if (dwLen > 0) serverName[dwLen-1] = '\0';
+			if (init_jk(serverName))
+				is_mapread = JK_TRUE;
+		}
+		if (!is_mapread)
+			is_inited = JK_FALSE;
+	}
+
+	if (is_inited) {
+        isapi_private_data_t private_data;
+        jk_ws_service_t s;
+        jk_pool_atom_t buf[SMALL_POOL_SIZE];
+        char *worker_name;
+
+        jk_init_ws_service(&s);
+        jk_open_pool(&private_data.p, buf, sizeof(buf));
+
+        private_data.request_started = JK_FALSE;
+        private_data.bytes_read_so_far = 0;
+        private_data.lpEcb = lpEcb;
+
+        s.ws_private = &private_data;
+        s.pool = &private_data.p;
+
+        if (init_ws_service(&private_data, &s, &worker_name)) {
+            jk_worker_t *worker = wc_get_worker_for_name(worker_name, logger);
+
+            jk_log(logger, JK_LOG_DEBUG, 
+                   "HttpExtensionProc %s a worker for name %s\n", 
+                   worker ? "got" : "could not get",
+                   worker_name);
+
+            if (worker) {
+                jk_endpoint_t *e = NULL;
+                if (worker->get_endpoint(worker, &e, logger)) {
+                    int recover = JK_FALSE;
+                    if (e->service(e, &s, logger, &recover)) {
+                        rc = HSE_STATUS_SUCCESS;
+                        lpEcb->dwHttpStatusCode = HTTP_STATUS_OK;
+                        jk_log(logger, JK_LOG_DEBUG, 
+                               "HttpExtensionProc service() returned OK\n");
+                    } else {
+                        jk_log(logger, JK_LOG_ERROR, 
+                               "HttpExtensionProc error, service() failed\n");
+                    }
+                    e->done(&e, logger);
+                }
+            } else {
+                jk_log(logger, JK_LOG_ERROR, 
+                       "HttpExtensionProc error, could not get a worker for name %s\n",
+                       worker_name);
+            }
+        }
+        jk_close_pool(&private_data.p);     
+    } else {
+        jk_log(logger, JK_LOG_ERROR, 
+               "HttpExtensionProc error, not initialized\n");
+    }
+
+    return rc;
+}
+
+    
+
+BOOL WINAPI TerminateExtension(DWORD dwFlags) 
+{
+    return TerminateFilter(dwFlags);
+}
+
+BOOL WINAPI TerminateFilter(DWORD dwFlags) 
+{
+    if (is_inited) {
+        is_inited = JK_FALSE;
+
+		if (is_mapread) {
+			uri_worker_map_free(&uw_map, logger);
+			is_mapread = JK_FALSE;
+		}
+        wc_close(logger);
+        if (logger) {
+            jk_close_file_logger(&logger);
+        }
+    }
+    
+    return TRUE;
+}
+
+
+BOOL WINAPI DllMain(HINSTANCE hInst,        // Instance Handle of the DLL
+                    ULONG ulReason,         // Reason why NT called this DLL
+                    LPVOID lpReserved)      // Reserved parameter for future use
+{
+    BOOL fReturn = TRUE;
+    char drive[_MAX_DRIVE];
+    char dir[_MAX_DIR];
+    char fname[_MAX_FNAME];
+    char file_name[_MAX_PATH];
+
+    switch (ulReason) {
+        case DLL_PROCESS_DETACH:
+            __try {
+                TerminateFilter(HSE_TERM_MUST_UNLOAD);
+            } __except(1) {
+            }
+        break;
+
+        default:
+        break;
+    } 
+    if (GetModuleFileName( hInst, file_name, sizeof(file_name))) {
+        _splitpath( file_name, drive, dir, fname, NULL );
+        _makepath( ini_file_name, drive, dir, fname, ".properties" );
+    } else {
+        fReturn = JK_FALSE;
+    }
+
+    return fReturn;
+}
+
+static int init_jk(char *serverName)
+{
+    int rc = JK_FALSE;  
+    jk_map_t *map;
+
+    if (!jk_open_file_logger(&logger, log_file, log_level)) {
+        logger = NULL;
+    }
+    /* Logging the initialization type: registry or properties file in virtual dir
+    */
+    if (using_ini_file) {
+        jk_log(logger, JK_LOG_DEBUG, "Using ini file %s.\n", ini_file_name);
+    } else {
+        jk_log(logger, JK_LOG_DEBUG, "Using registry.\n");
+    }
+    jk_log(logger, JK_LOG_DEBUG, "Using log file %s.\n", log_file);
+    jk_log(logger, JK_LOG_DEBUG, "Using log level %d.\n", log_level);
+    jk_log(logger, JK_LOG_DEBUG, "Using extension uri %s.\n", extension_uri);
+    jk_log(logger, JK_LOG_DEBUG, "Using worker file %s.\n", worker_file);
+    jk_log(logger, JK_LOG_DEBUG, "Using worker mount file %s.\n", worker_mount_file);
+    jk_log(logger, JK_LOG_DEBUG, "Using uri select %d.\n", uri_select_option);
+
+    if (map_alloc(&map)) {
+        if (map_read_properties(map, worker_mount_file)) {
+            /* remove non-mapping entries (assume they were string substitutions) */
+            jk_map_t *map2;
+            if (map_alloc(&map2)) {
+                int sz,i;
+                void* old;
+
+                sz = map_size(map);
+                for(i = 0; i < sz ; i++) {
+                    char *name = map_name_at(map, i);
+                    if ('/' == *name) {
+                        map_put(map2, name, map_value_at(map, i), &old);
+                    } else {
+                        jk_log(logger, JK_LOG_DEBUG,
+                               "Ignoring worker mount file entry %s=%s.\n",
+                               name, map_value_at(map, i));
+                    }
+                }
+
+                if (uri_worker_map_alloc(&uw_map, map2, logger)) {
+                    rc = JK_TRUE;
+                }
+
+                map_free(&map2);
+            }
+        } else {
+            jk_log(logger, JK_LOG_EMERG, 
+                    "Unable to read worker mount file %s.\n", 
+                    worker_mount_file);
+        }
+        map_free(&map);
+    }
+
+    if (rc) {
+        rc = JK_FALSE;
+        if (map_alloc(&map)) {
+            if (map_read_properties(map, worker_file)) {
+                /* we add the URI->WORKER MAP since workers using AJP14 will feed it */
+
+                worker_env.uri_to_worker = uw_map;
+                worker_env.server_name = serverName;
+
+                if (wc_open(map, &worker_env, logger)) {
+                    rc = JK_TRUE;
+                }
+            } else {
+                jk_log(logger, JK_LOG_EMERG, 
+                        "Unable to read worker file %s.\n", 
+                        worker_file);
+            }
+            map_free(&map);
+        }
+    }
+
+    return rc;
+}
+
+static int initialize_extension(void)
+{
+
+    if (read_registry_init_data()) {
+        is_inited = JK_TRUE;
+    }
+    return is_inited;
+}
+
+int parse_uri_select(const char *uri_select)
+{
+    if(0 == strcasecmp(uri_select, URI_SELECT_PARSED_VERB)) {
+        return URI_SELECT_OPT_PARSED;
+    }
+
+    if(0 == strcasecmp(uri_select, URI_SELECT_UNPARSED_VERB)) {
+        return URI_SELECT_OPT_UNPARSED;
+    }
+
+    if(0 == strcasecmp(uri_select, URI_SELECT_ESCAPED_VERB)) {
+        return URI_SELECT_OPT_ESCAPED;
+    }
+
+    return -1;
+}
+
+static int read_registry_init_data(void)
+{
+    char tmpbuf[INTERNET_MAX_URL_LENGTH];
+    HKEY hkey;
+    long rc;
+    int  ok = JK_TRUE;
+    char *tmp;
+    jk_map_t *map;
+
+    if (map_alloc(&map)) {
+        if (map_read_properties(map, ini_file_name)) {
+            using_ini_file = JK_TRUE;
+		}
+    }
+    if (using_ini_file) {
+        tmp = map_get_string(map, JK_LOG_FILE_TAG, NULL);
+        if (tmp) {
+            strcpy(log_file, tmp);
+        } else {
+            ok = JK_FALSE;
+        }
+        tmp = map_get_string(map, JK_LOG_LEVEL_TAG, NULL);
+        if (tmp) {
+            log_level = jk_parse_log_level(tmp);
+        } else {
+            ok = JK_FALSE;
+        }
+        tmp = map_get_string(map, EXTENSION_URI_TAG, NULL);
+        if (tmp) {
+            strcpy(extension_uri, tmp);
+        } else {
+            ok = JK_FALSE;
+        }
+        tmp = map_get_string(map, JK_WORKER_FILE_TAG, NULL);
+        if (tmp) {
+            strcpy(worker_file, tmp);
+        } else {
+            ok = JK_FALSE;
+        }
+        tmp = map_get_string(map, JK_MOUNT_FILE_TAG, NULL);
+        if (tmp) {
+            strcpy(worker_mount_file, tmp);
+        } else {
+            ok = JK_FALSE;
+        }
+        tmp = map_get_string(map, URI_SELECT_TAG, NULL);
+        if (tmp) {
+            int opt = parse_uri_select(tmp);
+            if (opt >= 0) {
+                uri_select_option = opt;
+            } else {
+                ok = JK_FALSE;
+            }
+        }
+    
+    } else {
+        rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                          REGISTRY_LOCATION,
+                          (DWORD)0,
+                          KEY_READ,
+                          &hkey);
+        if(ERROR_SUCCESS != rc) {
+            return JK_FALSE;
+        } 
+
+        if(get_registry_config_parameter(hkey,
+                                         JK_LOG_FILE_TAG, 
+                                         tmpbuf,
+                                         sizeof(log_file))) {
+            strcpy(log_file, tmpbuf);
+        } else {
+            ok = JK_FALSE;
+        }
+    
+        if(get_registry_config_parameter(hkey,
+                                         JK_LOG_LEVEL_TAG,
+                                         tmpbuf,
+                                         sizeof(tmpbuf))) {
+            log_level = jk_parse_log_level(tmpbuf);
+        } else {
+            ok = JK_FALSE;
+        }
+
+        if(get_registry_config_parameter(hkey,
+                                         EXTENSION_URI_TAG,
+                                         tmpbuf,
+                                         sizeof(extension_uri))) {
+            strcpy(extension_uri, tmpbuf);
+        } else {
+            ok = JK_FALSE;
+        }
+
+        if(get_registry_config_parameter(hkey,
+                                         JK_WORKER_FILE_TAG,
+                                         tmpbuf,
+                                         sizeof(worker_file))) {
+            strcpy(worker_file, tmpbuf);
+        } else {
+            ok = JK_FALSE;
+        }
+
+        if(get_registry_config_parameter(hkey,
+                                         JK_MOUNT_FILE_TAG,
+                                         tmpbuf,
+                                         sizeof(worker_mount_file))) {
+            strcpy(worker_mount_file, tmpbuf);
+        } else {
+            ok = JK_FALSE;
+        }
+
+        if(get_registry_config_parameter(hkey,
+                                         URI_SELECT_TAG, 
+                                         tmpbuf,
+                                         sizeof(tmpbuf))) {
+            int opt = parse_uri_select(tmpbuf);
+            if (opt >= 0) {
+                uri_select_option = opt;
+            } else {
+                ok = JK_FALSE;
+            }
+        }
+
+        RegCloseKey(hkey);
+    }    
+    return ok;
+} 
+
+static int get_registry_config_parameter(HKEY hkey,
+                                         const char *tag, 
+                                         char *b,
+                                         DWORD sz)
+{   
+    DWORD type = 0;
+    LONG  lrc;
+
+    lrc = RegQueryValueEx(hkey,     
+                          tag,      
+                          (LPDWORD)0,
+                          &type,    
+                          (LPBYTE)b,
+                          &sz); 
+    if ((ERROR_SUCCESS != lrc) || (type != REG_SZ)) {
+        return JK_FALSE;        
+    }
+    
+    b[sz] = '\0';
+
+    return JK_TRUE;     
+}
+
+static int init_ws_service(isapi_private_data_t *private_data,
+                           jk_ws_service_t *s,
+                           char **worker_name) 
+{
+    char huge_buf[16 * 1024]; /* should be enough for all */
+
+    DWORD huge_buf_sz;
+
+    s->jvm_route = NULL;
+
+    s->start_response = start_response;
+    s->read = read;
+    s->write = write;
+
+    /* Clear RECO status */
+    s->reco_status  = RECO_NONE;
+
+    GET_SERVER_VARIABLE_VALUE(HTTP_WORKER_HEADER_NAME, (*worker_name));           
+    GET_SERVER_VARIABLE_VALUE(HTTP_URI_HEADER_NAME, s->req_uri);     
+    GET_SERVER_VARIABLE_VALUE(HTTP_QUERY_HEADER_NAME, s->query_string);     
+
+    if (s->req_uri == NULL) {
+        s->query_string = private_data->lpEcb->lpszQueryString;
+        *worker_name    = DEFAULT_WORKER_NAME;
+        GET_SERVER_VARIABLE_VALUE("URL", s->req_uri);       
+        if (unescape_url(s->req_uri) < 0)
+            return JK_FALSE;
+        getparents(s->req_uri);
+    }
+    
+    GET_SERVER_VARIABLE_VALUE("AUTH_TYPE", s->auth_type);
+    GET_SERVER_VARIABLE_VALUE("REMOTE_USER", s->remote_user);
+    GET_SERVER_VARIABLE_VALUE("SERVER_PROTOCOL", s->protocol);
+    GET_SERVER_VARIABLE_VALUE("REMOTE_HOST", s->remote_host);
+    GET_SERVER_VARIABLE_VALUE("REMOTE_ADDR", s->remote_addr);
+    GET_SERVER_VARIABLE_VALUE(SERVER_NAME, s->server_name);
+    GET_SERVER_VARIABLE_VALUE_INT("SERVER_PORT", s->server_port, 80)
+    GET_SERVER_VARIABLE_VALUE(SERVER_SOFTWARE, s->server_software);
+    GET_SERVER_VARIABLE_VALUE_INT("SERVER_PORT_SECURE", s->is_ssl, 0);
+
+    s->method           = private_data->lpEcb->lpszMethod;
+    s->content_length   = private_data->lpEcb->cbTotalBytes;
+
+    s->ssl_cert     = NULL;
+    s->ssl_cert_len = 0;
+    s->ssl_cipher   = NULL;
+    s->ssl_session  = NULL;
+	s->ssl_key_size = -1;
+
+    s->headers_names    = NULL;
+    s->headers_values   = NULL;
+    s->num_headers      = 0;
+    
+    /*
+     * Add SSL IIS environment
+     */
+    if (s->is_ssl) {         
+        char *ssl_env_names[9] = {
+            "CERT_ISSUER", 
+            "CERT_SUBJECT", 
+            "CERT_COOKIE", 
+            "HTTPS_SERVER_SUBJECT", 
+            "CERT_FLAGS", 
+            "HTTPS_SECRETKEYSIZE", 
+            "CERT_SERIALNUMBER", 
+            "HTTPS_SERVER_ISSUER", 
+            "HTTPS_KEYSIZE"
+        };
+        char *ssl_env_values[9] = {
+            NULL, 
+            NULL, 
+            NULL, 
+            NULL, 
+            NULL, 
+            NULL, 
+            NULL, 
+            NULL, 
+            NULL
+        };
+        unsigned i;
+        unsigned num_of_vars = 0;
+
+        for(i = 0 ; i < 9 ; i++) {
+            GET_SERVER_VARIABLE_VALUE(ssl_env_names[i], ssl_env_values[i]);
+            if (ssl_env_values[i]) {
+                num_of_vars++;
+            }
+        }
+        if (num_of_vars) {
+            unsigned j;
+
+            s->attributes_names = 
+                jk_pool_alloc(&private_data->p, num_of_vars * sizeof(char *));
+            s->attributes_values = 
+                jk_pool_alloc(&private_data->p, num_of_vars * sizeof(char *));
+
+            j = 0;
+            for(i = 0 ; i < 9 ; i++) {                
+                if (ssl_env_values[i]) {
+                    s->attributes_names[j] = ssl_env_names[i];
+                    s->attributes_values[j] = ssl_env_values[i];
+                    j++;
+                }
+            }
+            s->num_attributes = num_of_vars;
+ 			if (ssl_env_values[4] && ssl_env_values[4][0] == '1') {
+				CERT_CONTEXT_EX cc;
+				DWORD cc_sz = sizeof(cc);
+				cc.cbAllocated = sizeof(huge_buf);
+				cc.CertContext.pbCertEncoded = (BYTE*) huge_buf;
+				cc.CertContext.cbCertEncoded = 0;
+
+				if (private_data->lpEcb->ServerSupportFunction(private_data->lpEcb->ConnID,
+											 (DWORD)HSE_REQ_GET_CERT_INFO_EX,                               
+											 (LPVOID)&cc,NULL,NULL) != FALSE)
+				{
+					jk_log(logger, JK_LOG_DEBUG,"Client Certificate encoding:%d sz:%d flags:%ld\n",
+								cc.CertContext.dwCertEncodingType & X509_ASN_ENCODING ,
+								cc.CertContext.cbCertEncoded,
+								cc.dwCertificateFlags);
+                    s->ssl_cert=jk_pool_alloc(&private_data->p,
+                                base64_encode_cert_len(cc.CertContext.cbCertEncoded));
+
+                    s->ssl_cert_len = base64_encode_cert(s->ssl_cert,
+                                huge_buf,cc.CertContext.cbCertEncoded) - 1;
+				}
+			}
+        }
+    }
+
+    huge_buf_sz = sizeof(huge_buf);         
+    if (get_server_value(private_data->lpEcb,
+                        "ALL_HTTP",             
+                        huge_buf,           
+                        huge_buf_sz,        
+                        "")) {              
+        unsigned cnt = 0;
+        char *tmp;
+
+        for(tmp = huge_buf ; *tmp ; tmp++) {
+            if (*tmp == '\n'){
+                cnt++;
+            }
+        }
+
+        if (cnt) {
+            char *headers_buf = jk_pool_strdup(&private_data->p, huge_buf);
+            unsigned i;
+            unsigned len_of_http_prefix = strlen("HTTP_");
+            BOOL need_content_length_header = (s->content_length == 0);
+            
+            cnt -= 2; /* For our two special headers */
+            /* allocate an extra header slot in case we need to add a content-length header */
+            s->headers_names  = jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
+            s->headers_values = jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
+
+            if (!s->headers_names || !s->headers_values || !headers_buf) {
+                return JK_FALSE;
+            }
+
+            for(i = 0, tmp = headers_buf ; *tmp && i < cnt ; ) {
+                int real_header = JK_TRUE;
+
+                /* Skipp the HTTP_ prefix to the beginning of th header name */
+                tmp += len_of_http_prefix;
+
+                if (!strnicmp(tmp, URI_HEADER_NAME, strlen(URI_HEADER_NAME)) ||
+                   !strnicmp(tmp, WORKER_HEADER_NAME, strlen(WORKER_HEADER_NAME))) {
+                    real_header = JK_FALSE;
+                } else if(need_content_length_header &&
+                   !strnicmp(tmp, CONTENT_LENGTH, strlen(CONTENT_LENGTH))) {
+                    need_content_length_header = FALSE;
+                    s->headers_names[i]  = tmp;
+                } else if (!strnicmp(tmp, TOMCAT_TRANSLATE_HEADER_NAME,
+                                          strlen(TOMCAT_TRANSLATE_HEADER_NAME))) {
+                    tmp += 6; /* TOMCAT */
+                    s->headers_names[i]  = tmp;
+                } else {
+                    s->headers_names[i]  = tmp;
+                }
+
+                while(':' != *tmp && *tmp) {
+                    if ('_' == *tmp) {
+                        *tmp = '-';
+                    } else {
+                        *tmp = tolower(*tmp);
+                    }
+                    tmp++;
+                }
+                *tmp = '\0';
+                tmp++;
+
+                /* Skip all the WS chars after the ':' to the beginning of th header value */
+                while(' ' == *tmp || '\t' == *tmp || '\v' == *tmp) {
+                    tmp++;
+                }
+
+                if (real_header) {
+                    s->headers_values[i]  = tmp;
+                }
+                
+                while(*tmp != '\n' && *tmp != '\r') {
+                    tmp++;
+                }
+                *tmp = '\0';
+                tmp++;
+
+                /* skipp CR LF */
+                while(*tmp == '\n' || *tmp == '\r') {
+                    tmp++;
+                }
+
+                if (real_header) {
+                    i++;
+                }
+            }
+            /* Add a content-length = 0 header if needed.
+             * Ajp13 assumes an absent content-length header means an unknown,
+             * but non-zero length body.
+             */
+            if(need_content_length_header) {
+                s->headers_names[cnt] = "content-length";
+                s->headers_values[cnt] = "0";
+                cnt++;
+            }
+            s->num_headers = cnt;
+        } else {
+            /* We must have our two headers */
+            return JK_FALSE;
+        }
+    } else {
+        return JK_FALSE;
+    }
+    
+    return JK_TRUE;
+}
+
+static int get_server_value(LPEXTENSION_CONTROL_BLOCK lpEcb,
+                            char *name,
+                            char  *buf,
+                            DWORD bufsz,
+                            char  *def_val)
+{
+    if (!lpEcb->GetServerVariable(lpEcb->ConnID, 
+                                 name,
+                                 buf,
+                                 (LPDWORD)&bufsz)) {
+        strcpy(buf, def_val);
+        return JK_FALSE;
+    }
+
+    if (bufsz > 0) {
+        buf[bufsz - 1] = '\0';
+    }
+
+    return JK_TRUE;
+}
+
+static const char begin_cert [] = 
+	"-----BEGIN CERTIFICATE-----\r\n";
+
+static const char end_cert [] = 
+	"-----END CERTIFICATE-----\r\n";
+
+static const char basis_64[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int base64_encode_cert_len(int len)
+{
+	int n = ((len + 2) / 3 * 4) + 1; // base64 encoded size
+	n += (n + 63 / 64) * 2; // add CRLF's
+	n += sizeof(begin_cert) + sizeof(end_cert) - 2;  // add enclosing strings.
+    return n;
+}
+
+static int base64_encode_cert(char *encoded,
+                              const unsigned char *string, int len)
+{
+    int i,c;
+    char *p;
+	const char *t;
+
+    p = encoded;
+
+	t = begin_cert;
+	while (*t != '\0')
+		*p++ = *t++;
+
+    c = 0;
+    for (i = 0; i < len - 2; i += 3) {
+        *p++ = basis_64[(string[i] >> 2) & 0x3F];
+        *p++ = basis_64[((string[i] & 0x3) << 4) |
+                        ((int) (string[i + 1] & 0xF0) >> 4)];
+        *p++ = basis_64[((string[i + 1] & 0xF) << 2) |
+                        ((int) (string[i + 2] & 0xC0) >> 6)];
+        *p++ = basis_64[string[i + 2] & 0x3F];
+        c += 4;
+        if ( c >= 64 ) {
+            *p++ = '\r';
+            *p++ = '\n';
+            c = 0;
+		}
+    }
+    if (i < len) {
+        *p++ = basis_64[(string[i] >> 2) & 0x3F];
+        if (i == (len - 1)) {
+            *p++ = basis_64[((string[i] & 0x3) << 4)];
+            *p++ = '=';
+        }
+        else {
+            *p++ = basis_64[((string[i] & 0x3) << 4) |
+                            ((int) (string[i + 1] & 0xF0) >> 4)];
+            *p++ = basis_64[((string[i + 1] & 0xF) << 2)];
+        }
+        *p++ = '=';
+        c++;
+    }
+    if ( c != 0 ) {
+        *p++ = '\r';
+        *p++ = '\n';
+    }
+
+	t = end_cert;
+	while (*t != '\0')
+		*p++ = *t++;
+
+    *p++ = '\0';
+    return p - encoded;
+}
+
diff --git a/connectors/jk/native/isapi/config.h b/connectors/jk/native/isapi/config.h
new file mode 100644
index 0000000..f8f29af
--- /dev/null
+++ b/connectors/jk/native/isapi/config.h
@@ -0,0 +1,47 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: ISAPI plugin for Tomcat                                    *
+ * Author:      Andy Armstrong <andy@tagish.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef __config_h
+#define __config_h
+
+#define MAKEVERSION(a, b, c, d) \
+	(((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
+
+/* the _memicmp() function is available */
+#if defined(WIN32)
+
+#define HAVE_MEMICMP
+#undef USE_INIFILE
+
+#elif defined(LINUX)
+
+#undef HAVE_MEMICMP
+#define USE_INIFILE
+
+#else
+#error Please define either WIN32 or LINUX
+#endif
+
+#define DEBUG(args) \
+	do { /*printf args ;*/ } while (0)
+
+#endif /* __config_h */
diff --git a/connectors/jk/native/isapi/inifile.c b/connectors/jk/native/isapi/inifile.c
new file mode 100644
index 0000000..102231f
--- /dev/null
+++ b/connectors/jk/native/isapi/inifile.c
@@ -0,0 +1,279 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: DSAPI plugin for Lotus Domino                              *
+ * Author:      Andy Armstrong <andy@tagish.com>                           *
+ * Date:        20010603                                                   *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#include "config.h"
+#include "inifile.h"
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* We have one of these for each ini file line. Once we've read the
+ * file and parsed it we'll have an array in key order containing one
+ * of these for each configuration item in file.
+ */
+typedef struct
+{
+	const char *key;
+	const char *value;
+
+} inifile_key;
+
+static char *file;			/* the text of the ini file				*/
+static inifile_key *keys;	/* an array of keys, one per item		*/
+static size_t klen;			/* length of the key array				*/
+
+/* Text that will prefix all of our error messages */
+#define ERRPFX "INIFILE: "
+/* Various error messages that we can return */
+ERRTYPE inifile_outofmemory		= ERRPFX "Out of memory";
+ERRTYPE inifile_filenotfound	= ERRPFX "File not found";
+ERRTYPE inifile_readerror		= ERRPFX "Error reading file";
+#define SYNFMT ERRPFX "File %s, line %d: %s"
+
+/* Case insensitive string comparison, works like strcmp() */
+static int inifile__stricmp(const char *s1, const char *s2)
+{
+	while (*s1 && tolower(*s1) == tolower(*s2))
+		s1++, s2++;
+	return tolower(*s1) - tolower(*s2);
+}
+
+/* Compare keys, suitable for passing to qsort() */
+static int inifile__cmp(const void *k1, const void *k2)
+{
+	const inifile_key *kk1 = (const inifile_key *) k1;
+	const inifile_key *kk2 = (const inifile_key *) k2;
+	return inifile__stricmp(kk1->key, kk2->key);
+}
+
+/* Return a new syntax error message. */
+static ERRTYPE inifile__syntax(jk_pool_t *p, const char *file, int line, const char *msg)
+{
+	static const char synfmt[] = SYNFMT;
+	size_t len = sizeof(synfmt) + strlen(msg) + strlen(file) + 10 /* fudge for line number */;
+	char *buf = jk_pool_alloc(p, len);
+	sprintf(buf, synfmt, file, line, msg);
+	return buf;
+}
+
+/* Various macros to tidy up the parsing code */
+
+/* Characters that are OK in the keyname */
+#define KEYCHR(c) \
+	(isalnum(c) || (c) == '.' || (c) == '_')
+
+/* Skip whitespace */
+#define SKIPSPC() \
+	while (*fp == '\t' || *fp == ' ') fp++
+
+/* Skip to the end of the current line */
+#define SKIPLN() \
+	while (*fp != '\0' && *fp != '\r' && *fp != '\n') fp++
+
+/* Move from the end of the current line to the start of the next, learning what the
+ * newline character is and counting lines
+ */
+#define NEXTLN() \
+	do { while (*fp == '\r' || *fp == '\n') { if (nlc == -1) nlc = *fp; if (*fp == nlc) ln++; fp++; } } while (0)
+
+/* Build the index. Called when the inifile is loaded by inifile_load()
+ */
+static ERRTYPE inifile__index(jk_pool_t *p, const char *name)
+{
+	int pass;
+	int ln = 1;
+	int nlc = -1;
+
+	/* Make two passes over the data. First time we're just counting
+	 * the lines that contain values so we can allocate the index, second
+	 * time we build the index.
+	 */
+	for (pass = 0; pass < 2; pass++)
+	{
+		char *fp = file;
+		char *ks = NULL, *ke;	/* key start, end */
+		char *vs = NULL, *ve;	/* value start, end */
+		klen = 0;
+
+		while (*fp != '\0')
+		{
+			SKIPSPC();
+
+			/* turn a comment into an empty line by moving to the next \r|\n */
+			if (*fp == '#' || *fp == ';')
+				SKIPLN();
+
+			if (*fp != '\0' && *fp != '\r' && *fp != '\n')
+			{
+				ks = fp;		/* start of key */
+				while (KEYCHR(*fp)) fp++;
+				ke = fp;		/* end of key */
+				SKIPSPC();
+
+				if (*fp != '=')
+					return inifile__syntax(p, name, ln, "Missing '=' or illegal character in key");
+
+				fp++; /* past the = */
+				SKIPSPC();
+				vs = fp;
+				SKIPLN();
+				ve = fp;
+				/* back up over any trailing space */
+				while (ve > vs && (ve[-1] == ' ' || ve[-1] == '\t')) ve--;
+				NEXTLN(); /* move forwards *before* we trash the eol characters */
+
+				if (NULL != keys) /* second pass? if so stash a pointer */
+				{
+					*ke = '\0';
+					*ve = '\0';
+					keys[klen].key = ks;
+					keys[klen].value = vs;
+				}
+
+				klen++;
+			}
+			else
+			{
+				NEXTLN();
+			}
+		}
+
+		if (NULL == keys && (keys = jk_pool_alloc(p, sizeof(inifile_key) * klen), NULL == keys))
+			return inifile_outofmemory;
+	}
+
+	/* got the index now, sort it so we can search it quickly */
+	qsort(keys, klen, sizeof(inifile_key), inifile__cmp);
+
+	return ERRNONE;
+}
+
+/* Read an INI file from disk
+ */
+ERRTYPE inifile_read(jk_pool_t *p, const char *name)
+{
+	FILE *fl;
+	size_t flen;
+	int ok;
+
+	if (fl = fopen(name, "rb"), NULL == fl)
+		return inifile_filenotfound;
+
+	fseek(fl, 0L, SEEK_END);
+	flen = (size_t) ftell(fl);
+	fseek(fl, 0L, SEEK_SET);
+
+	/* allocate one extra byte for trailing \0
+	 */
+	if (file = jk_pool_alloc(p, flen+1), NULL == file)
+	{
+		fclose(fl);
+		return inifile_outofmemory;
+	}
+
+	ok = (fread(file, flen, 1, fl) == 1);
+	fclose(fl);
+	if (!ok) return inifile_readerror;
+
+	file[flen] = '\0';	/* terminate it to simplify parsing */
+
+	return inifile__index(p, name);
+}
+
+/* Find the value associated with the given key returning it or NULL
+ * if no match is found. Key name matching is case insensitive.
+ */
+const char *inifile_lookup(const char *key)
+{
+	int lo, mid, hi, cmp;
+
+	if (NULL == keys)
+		return NULL;
+
+	for (lo = 0, hi = klen-1; lo <= hi; )
+	{
+		mid = (lo + hi) / 2;
+		cmp = inifile__stricmp(key, keys[mid].key);
+		if (cmp < 0)	/* key in array is greater */
+			hi = mid-1;
+		else if (cmp > 0)
+			lo = mid+1;
+		else
+			return keys[mid].value;
+	}
+
+	return NULL;
+}
+
+#ifdef TEST
+
+static jk_pool_t pool;
+extern void jk_dump_pool(jk_pool_t *p, FILE *f); /* not declared in header */
+
+int main(void)
+{
+	ERRTYPE e;
+	unsigned k;
+	int rc = 0;
+
+	jk_open_pool(&pool, NULL, 0);
+
+	e = inifile_read(&pool, "ok.ini");
+	if (e == ERRNONE)
+	{
+		printf("%u keys in ok.ini\n", klen);
+		for (k = 0; k < klen; k++)
+		{
+			const char *val = inifile_lookup(keys[k].key);
+			printf("Key: \"%s\", value: \"%s\"\n", keys[k].key, val);
+		}
+	}
+	else
+	{
+		printf("Error reading ok.ini: %s\n", e);
+		rc = 1;
+	}
+
+	e = inifile_read(&pool, "bad.ini");
+	if (e == ERRNONE)
+	{
+		printf("%u keys in bad.ini\n", klen);
+		for (k = 0; k < klen; k++)
+		{
+			const char *val = inifile_lookup(keys[k].key);
+			printf("Key: \"%s\", value: \"%s\"\n", keys[k].key, val);
+		}
+		rc = 1;		/* should be a syntax error */
+	}
+	else
+	{
+		printf("Error reading bad.ini: %s (which is OK: that's what we expected)\n", e);
+	}
+
+	jk_dump_pool(&pool, stdout);
+	jk_close_pool(&pool);
+
+	return rc;
+}
+#endif
diff --git a/connectors/jk/native/isapi/inifile.h b/connectors/jk/native/isapi/inifile.h
new file mode 100644
index 0000000..b793fb0
--- /dev/null
+++ b/connectors/jk/native/isapi/inifile.h
@@ -0,0 +1,45 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: DSAPI plugin for Lotus Domino                              *
+ * Author:      Andy Armstrong <andy@tagish.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef __inifile_h
+#define __inifile_h
+
+#include "jk_pool.h"
+
+#define ERRTYPE const char *
+#define ERRFMT  "%s"			/* natural printf format for errors */
+#define ERRTXT(e) (e)			/* macro to return text for an error */
+#define ERRNONE NULL
+extern ERRTYPE inifile_outofmemory;
+extern ERRTYPE inifile_filenotfound;
+extern ERRTYPE inifile_readerror;
+
+/* Read an INI file from disk
+ */
+ERRTYPE inifile_read(jk_pool_t *pool, const char *name);
+
+/* Find the value associated with the given key returning it or NULL
+ * if no match is found. Key name matching is case insensitive.
+ */
+const char *inifile_lookup(const char *key);
+
+#endif /* __inifile_h */
diff --git a/connectors/jk/native/isapi/isapi.dsp b/connectors/jk/native/isapi/isapi.dsp
new file mode 100644
index 0000000..f9ccb71
--- /dev/null
+++ b/connectors/jk/native/isapi/isapi.dsp
@@ -0,0 +1,297 @@
+# Microsoft Developer Studio Project File - Name="isapi" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=isapi - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "isapi.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "isapi.mak" CFG="isapi - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "isapi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "isapi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "isapi - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "isapi_EXPORTS" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "..\common" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ISAPI_EXPORTS" /D "NT" /FR /FD /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /dll /machine:I386 /out:"Release/tomcat_redirector.dll" /libpath:"C:\notesapi\lib\mswin32"
+
+!ELSEIF  "$(CFG)" == "isapi - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "isapi_EXPORTS" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "..\common" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ISAPI_EXPORTS" /D "NT" /FR /FD /GZ /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /dll /debug /machine:I386 /out:"Debug/tomcat_redirector.dll" /pdbtype:sept /libpath:"C:\notesapi\lib\mswin32"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "isapi - Win32 Release"
+# Name "isapi - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\inifile.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp12_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp_common.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_connect.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_context.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_isapi_plugin.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_jni_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_lb_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_msg_buff.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_sockbuf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_uri_worker_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_util.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\poolbuf.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\config.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\inifile.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\isapifilter.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp12_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp_common.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_connect.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_context.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_global.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_jni_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_lb_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_msg_buff.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_mt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_service.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_sockbuf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_uri_worker_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_util.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\poolbuf.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# Begin Source File
+
+SOURCE=.\ReadMe.txt
+# End Source File
+# End Target
+# End Project
diff --git a/connectors/jk/native/isapi/jk_isapi_plugin.c b/connectors/jk/native/isapi/jk_isapi_plugin.c
new file mode 100644
index 0000000..2ff44f6
--- /dev/null
+++ b/connectors/jk/native/isapi/jk_isapi_plugin.c
@@ -0,0 +1,1112 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: ISAPI plugin for Tomcat                                    *
+ * Author:      Andy Armstrong <andy@tagish.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+/* Based on the the server redirector which was, in turn, based on the IIS 
+ * redirector by Gal Shachor <shachor@il.ibm.com>
+ */
+
+#include "config.h"
+#include "inifile.h"
+#include "poolbuf.h"
+
+/* ISAPI stuff */
+#include <httpext.h>
+#include <httpfilt.h>
+#include <wininet.h>
+
+/* JK stuff */
+#include "jk_global.h"
+#include "jk_util.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+#include "jk_uri_worker_map.h"
+
+#include <stdarg.h>
+#define NOERROR 0
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#if !defined(DLLEXPORT)
+#ifdef WIN32
+#define DLLEXPORT __declspec(dllexport)
+#else
+#define DLLEXPORT
+#endif
+#endif
+
+#define VERSION				"2.0"
+#define VERSION_STRING		"Jakarta/ISAPI/" VERSION
+
+/* What we call ourselves */
+#define FILTERDESC			"Apache Tomcat Interceptor (" VERSION_STRING ")"
+#define SERVERDFLT			"Microsoft IIS"
+
+/* Registry location of configuration data */
+#define REGISTRY_LOCATION	"Software\\Apache Software Foundation\\Jakarta Isapi Redirector\\2.0"
+
+/* Name of INI file relative to whatever the 'current' directory is when the filter is
+ * loaded. Certainly on Linux this is the the server data directory -- it seems likely that
+ * it's the same on other platforms
+ */
+#define ININAME				"libtomcat.ini"
+
+/* Names of registry keys/ini items that contain commands to start, stop Tomcat */
+#define TOMCAT_START		"tomcat_start"
+#define TOMCAT_STOP			"tomcat_stop"
+#define TOMCAT_STARTSTOP_TO	30000				/* 30 seconds */
+
+static int					initDone	= JK_FALSE;
+static jk_uri_worker_map_t	*uw_map		= NULL;
+static jk_logger_t			*logger		= NULL;
+
+static int					logLevel	= JK_LOG_EMERG_LEVEL;
+static jk_pool_t			cfgPool;
+
+static const char *logFile;
+static const char *workerFile;
+static const char *workerMountFile;
+static const char *tomcatStart;
+static const char *tomcatStop;
+
+#if defined(JK_VERSION) && JK_VERSION >= MAKEVERSION(1, 2, 0, 1)
+static jk_worker_env_t   worker_env;
+#endif
+
+static char					*crlf		= "\r\n";
+
+typedef enum { HDR, BODY } rq_state;
+
+typedef struct private_ws
+{
+	jk_pool_t			p;
+
+	/* Passed in by server, used to access various methods and data.
+	 */
+    LPEXTENSION_CONTROL_BLOCK  lpEcb;
+
+	/* True iff the response headers have been sent
+	 */
+	int					responseStarted;
+
+	rq_state			state;
+
+	poolbuf				hdr;
+	poolbuf				body;
+
+} private_ws_t;
+
+/* These three functions are called back (indirectly) by
+ * Tomcat during request processing. StartResponse() sends
+ * the headers associated with the response.
+ */
+static int JK_METHOD StartResponse(jk_ws_service_t * s, int status, const char *reason,
+									const char *const *hdrNames,
+									const char *const *hdrValues, unsigned hdrCount);
+/* Read() is called by Tomcat to read from the request body (if any).
+ */
+static int JK_METHOD Read(jk_ws_service_t * s, void *b, unsigned l, unsigned *a);
+/* Write() is called by Tomcat to send data back to the client.
+ */
+static int JK_METHOD Write(jk_ws_service_t * s, const void *b, unsigned l);
+
+static int ReadInitData(void);
+
+#ifndef USE_INIFILE
+static const char *GetRegString(HKEY hkey, const char *key);
+#endif
+
+//static unsigned int ParsedRequest(PHTTP_FILTER_CONTEXT *context, FilterParsedRequest *reqData);
+
+/* Case insentive memcmp() clone
+ */
+#ifdef HAVE_MEMICMP
+#define NoCaseMemCmp(ci, cj, l) _memicmp((void *) (ci), (void *) (cj), (l))
+#else
+static int NoCaseMemCmp(const char *ci, const char *cj, int len)
+{
+	if (0 == memcmp(ci, cj, len))
+		return 0;
+	while (len > 0)
+	{
+		int cmp = tolower(*ci) - tolower(*cj);
+		if (cmp != 0) return cmp;
+		ci++;
+		cj++;
+		len--;
+	}
+	return 0;
+}
+#endif
+
+/* Case insentive strcmp() clone
+ */
+#ifdef HAVE_STRICMP
+#define NoCaseStrCmp(si, sj) _stricmp((void *) (si), (void *) (sj))
+#else
+static int NoCaseStrCmp(const char *si, const char *sj)
+{
+	if (0 == strcmp(si, sj))
+		return 0;
+
+	while (*si && tolower(*si) == tolower(*sj))
+		si++, sj++;
+
+	return tolower(*si) - tolower(*sj);
+}
+#endif
+
+/* Case insensitive substring search.
+ * str		string to search
+ * slen		length of string to search
+ * ptn		pattern to search for
+ * plen		length of pattern
+ * returns	1 if there's a match otherwise 0
+ */
+static int FindPathElem(const char *str, int slen, const char *ptn, int plen)
+{
+	const char *sp = str;
+
+	while (slen >= plen)
+	{
+		/* We're looking for a match for the specified string bounded by
+		 * the start of the string, \ or / at the left and the end of the
+		 * string, \ or / at the right. We look for \ as well as / on the
+		 * suspicion that a Windows hosted server might accept URIs
+		 * containing \.
+		 */
+		if (NoCaseMemCmp(sp, ptn, plen) == 0 &&
+			(sp == str || *sp == '\\' || *sp == '/') &&
+			(*sp == '\0' || *sp == '\\' || *sp == '/'))
+			return 1;
+		slen--;
+		sp++;
+	}
+	return 0;
+}
+
+static void LogMessage(char *msg, unsigned short code, ...)
+{
+	va_list ap;
+
+	if (code != NOERROR)
+		printf("Error %d: ", code);
+
+	va_start(ap, code);
+	vprintf(msg, ap);
+	va_end(ap);
+	printf("\n");
+}
+
+/* Get the value of a server (CGI) variable as a string
+ */
+static int GetVariable(private_ws_t *ws, char *hdrName,
+					 char *buf, DWORD bufsz, char **dest, const char *dflt)
+{
+	LPEXTENSION_CONTROL_BLOCK lpEcb = ws->lpEcb;
+
+	if (lpEcb->GetServerVariable(lpEcb->ConnID, hdrName, buf, (LPDWORD) &bufsz))
+	{
+		if (bufsz > 0) buf[bufsz-1] = '\0';
+		*dest = jk_pool_strdup(&ws->p, buf);
+		return JK_TRUE;
+	}
+
+	*dest = jk_pool_strdup(&ws->p, dflt);
+	return JK_FALSE;
+}
+
+/* Get the value of a server (CGI) variable as an integer
+ */
+static int GetVariableInt(private_ws_t *ws, char *hdrName,
+						char *buf, DWORD bufsz, int *dest, int dflt)
+{
+	LPEXTENSION_CONTROL_BLOCK lpEcb = ws->lpEcb;
+
+	if (lpEcb->GetServerVariable(lpEcb->ConnID, hdrName, buf, (LPDWORD) &bufsz))
+	{
+		if (bufsz > 0) buf[bufsz-1] = '\0';
+		*dest = atoi(buf);
+		return JK_TRUE;
+	}
+
+	*dest = dflt;
+	return JK_FALSE;
+}
+/* Get the value of a server (CGI) variable as a boolean switch
+ */
+static int GetVariableBool(private_ws_t *ws, char *hdrName,
+						char *buf, DWORD bufsz, int *dest, int dflt)
+{
+	LPEXTENSION_CONTROL_BLOCK lpEcb = ws->lpEcb;
+
+	if (lpEcb->GetServerVariable(lpEcb->ConnID, hdrName, buf, (LPDWORD) &bufsz))
+	{
+		if (bufsz > 0) buf[bufsz-1] = '\0';
+		if (isdigit(buf[0]))
+			*dest = atoi(buf) != 0;
+		else if (NoCaseStrCmp(buf, "yes") == 0 || NoCaseStrCmp(buf, "on") == 0)
+			*dest = 1;
+		else
+			*dest = 0;
+		return JK_TRUE;
+	}
+
+	*dest = dflt;
+	return JK_FALSE;
+}
+
+/* A couple of utility macros to supply standard arguments to GetVariable() and
+ * GetVariableInt().
+ */
+#define GETVARIABLE(name, dest, dflt)		GetVariable(ws, (name), workBuf, sizeof(workBuf), (dest), (dflt))
+#define GETVARIABLEINT(name, dest, dflt)	GetVariableInt(ws, (name), workBuf, sizeof(workBuf), (dest), (dflt))
+#define GETVARIABLEBOOL(name, dest, dflt)	GetVariableBool(ws, (name), workBuf, sizeof(workBuf), (dest), (dflt))
+
+/* Return 1 iff the supplied string contains "web-inf" (in any case
+ * variation. We don't allow URIs containing web-inf, although
+ * FindPathElem() actually looks for the string bounded by path punctuation
+ * or the ends of the string, so web-inf must appear as a single element
+ * of the supplied URI
+ */
+static int BadURI(const char *uri)
+{
+	static char *wi = "web-inf";
+	return FindPathElem(uri, strlen(uri), wi, strlen(wi));
+}
+
+/* Replacement for strcat() that updates a buffer pointer. It's
+ * probably marginal, but this should be more efficient that strcat()
+ * in cases where the string being concatenated to gets long because
+ * strcat() has to count from start of the string each time.
+ */
+static void Append(char **buf, const char *str)
+{
+	int l = strlen(str);
+	memcpy(*buf, str, l);
+	(*buf)[l] = '\0';
+	*buf += l;
+}
+
+/* Start the response by sending any headers. Invoked by Tomcat. I don't
+ * particularly like the fact that this always allocates memory, but
+ * perhaps jk_pool_alloc() is efficient.
+ */
+static int JK_METHOD StartResponse(jk_ws_service_t *s, int status, const char *reason,
+									const char *const *hdrNames,
+									const char *const *hdrValues, unsigned hdrCount)
+{
+	DEBUG(("StartResponse()\n"));
+	jk_log(logger, JK_LOG_DEBUG, "Into jk_ws_service_t::StartResponse\n");
+
+	if (status < 100 || status > 1000)
+	{
+		jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::StartResponse, invalid status %d\n", status);
+		return JK_FALSE;
+	}
+
+	if (s && s->ws_private)
+	{
+		private_ws_t *p = s->ws_private;
+
+		if (!p->responseStarted)
+		{
+			char *statBuf;
+			char *hdrBuf;
+			size_t statLen;
+
+			p->responseStarted = JK_TRUE;
+
+			if (NULL == reason)
+				reason = "";
+
+			/* TODO: coallesce the to jk_pool_alloc() calls into a single
+			 * buffer alloc
+			 */
+			statLen = 4 + strlen(reason);
+			statBuf = jk_pool_alloc(&p->p, statLen + 1);
+
+			/* slightly quicker than sprintf() we hope */
+			statBuf[0] = (status / 100) % 10 + '0';
+			statBuf[1] = (status /  10) % 10 + '0';
+			statBuf[2] = (status /   1) % 10 + '0';
+			statBuf[3] = ' ';
+			strcpy(statBuf + 4, reason);
+
+			/* Build a single string containing all the headers
+			 * because that's what the server needs.
+			 */
+			if (hdrCount > 0)
+			{
+				unsigned i;
+				unsigned hdrLen;
+				char *bufp;
+
+				for (i = 0, hdrLen = 3; i < hdrCount; i++)
+					hdrLen += strlen(hdrNames[i]) + strlen(hdrValues[i]) + 4;
+
+				hdrBuf = jk_pool_alloc(&p->p, hdrLen);
+				bufp = hdrBuf;
+
+				for (i = 0; i < hdrCount; i++)
+				{
+					Append(&bufp, hdrNames[i]);
+					Append(&bufp, ": ");
+					Append(&bufp, hdrValues[i]);
+					Append(&bufp, crlf);
+				}
+
+				Append(&bufp, crlf);
+			}
+			else
+			{
+				hdrBuf = crlf;
+			}
+
+			DEBUG(("%d %s\n%s", status, reason, hdrBuf));
+
+			/* TODO: check API docs for this */
+			if (!p->lpEcb->ServerSupportFunction(p->lpEcb->ConnID,
+						HSE_REQ_SEND_RESPONSE_HEADER,
+						statBuf, (LPDWORD) &statLen, (LPDWORD) hdrBuf))
+			{
+                jk_log(logger, JK_LOG_ERROR, 
+                       "jk_ws_service_t::start_response, ServerSupportFunction failed\n");
+                return JK_FALSE;
+            }       
+
+		}
+		return JK_TRUE;
+	}
+
+	jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::StartResponse, NULL parameters\n");
+
+	return JK_FALSE;
+}
+
+static int JK_METHOD Read(jk_ws_service_t * s, void *bytes, unsigned len, unsigned *countp)
+{
+#if 0
+	DEBUG(("Read(%p, %p, %u, %p)\n", s, bytes, len, countp));
+	jk_log(logger, JK_LOG_DEBUG, "Into jk_ws_service_t::Read\n");
+
+	if (s && s->ws_private && bytes && countp)
+	{
+		private_ws_t *p = s->ws_private;
+
+		/* Copy data from the server's buffer. Although it seems slightly
+		 * improbably we're believing that the server always buffers the
+		 * entire request in memory. Not properly tested yet.
+		 */
+		if (len > p->reqSize) len = p->reqSize;
+		memcpy(bytes, p->reqBuffer, len);
+		p->reqBuffer += len;
+		p->reqSize -= len;
+		*countp = len;
+		return JK_TRUE;
+	}
+
+	jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::Read, NULL parameters\n");
+
+#endif
+	return JK_FALSE;
+}
+
+static int JK_METHOD Write(jk_ws_service_t *s, const void *bytes, unsigned len)
+{
+	DEBUG(("Write(%p, %p, %u)\n", s, bytes, len));
+	jk_log(logger, JK_LOG_DEBUG, "Into jk_ws_service_t::Write\n");
+
+	if (s && s->ws_private && bytes)
+	{
+		private_ws_t *p = s->ws_private;
+		DWORD dwLen = len;
+
+		/* Make sure the response has really started. I'm almost certain
+		 * this isn't necessary, but it was in the ISAPI code, so it's in
+		 * here too.
+		 */
+		if (!p->responseStarted)
+			StartResponse(s, 200, NULL, NULL, NULL, 0);
+
+		DEBUG(("Writing %d bytes of content\n", len));
+
+		/* Send the data */
+		if (len > 0)
+		{
+			if (!p->lpEcb->WriteClient(p->lpEcb->ConnID, (LPVOID) bytes, &dwLen, 0))
+			{
+                jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::Write, WriteClient failed\n");
+                return JK_FALSE;
+			}
+		}
+		
+		return JK_TRUE;
+	}
+
+	jk_log(logger, JK_LOG_ERROR, "jk_ws_service_t::Write, NULL parameters\n");
+
+	return JK_FALSE;
+}
+
+static int RunProg(const char *cmd)
+{
+#ifdef WIN32
+    STARTUPINFO si;
+    PROCESS_INFORMATION pi;
+
+	ZeroMemory(&si, sizeof(si));
+    si.cb			= sizeof(si);    // Start the child process.
+	si.dwFlags		= STARTF_USESHOWWINDOW;
+	si.wShowWindow	= SW_SHOWMAXIMIZED;
+
+	if (!CreateProcess(NULL, (char *) cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
+	{
+		DWORD err = GetLastError();
+		LogMessage("Command \"%s\" (error %u)", NOERROR, cmd, err);
+		return FALSE;
+	}
+
+	if (WAIT_OBJECT_0 == WaitForSingleObject(pi.hProcess, TOMCAT_STARTSTOP_TO))
+		return TRUE;
+
+	LogMessage("Command \"%s\" didn't complete in time", NOERROR, cmd);
+	return FALSE;
+
+#else
+	int err = system(cmd);
+	if (0 == err) return 1;
+	LogMessage("Command \"%s\" failed (error %d)", NOERROR, cmd, err);
+	return 0;
+#endif
+}
+
+/* Called when the filter is unloaded. Free various resources and
+ * display a banner.
+ */
+BOOL WINAPI TerminateFilter(DWORD dwFlags)
+{
+	if (initDone)
+	{
+		uri_worker_map_free(&uw_map, logger);
+		wc_close(logger);
+		if (logger)
+			jk_close_file_logger(&logger);
+
+		initDone = JK_FALSE;
+	}
+
+	if (NULL != tomcatStop && '\0' != *tomcatStop)
+	{
+		LogMessage("Attempting to stop Tomcat: %s", NOERROR, tomcatStop);
+		RunProg(tomcatStop);
+	}
+
+	LogMessage(FILTERDESC " unloaded", NOERROR);
+
+	jk_close_pool(&cfgPool);
+
+	return TRUE;
+}
+
+/* Called when the server loads the filter. Reads a load of config data from
+ * the registry and elsewhere and displays a banner.
+ */
+BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
+{    
+	jk_open_pool(&cfgPool, NULL, 0);		/* empty pool for config data */
+
+	if (!ReadInitData())
+		goto initFailed;
+
+	if (!jk_open_file_logger(&logger, logFile, logLevel))
+		logger = NULL;
+
+	if (NULL != tomcatStart && '\0' != *tomcatStart)
+	{
+		LogMessage("Attempting to start Tomcat: %s", NOERROR, tomcatStart);
+		RunProg(tomcatStart);
+	}
+
+    pVer->dwFilterVersion = pVer->dwServerFilterVersion;
+                        
+    //if (pVer->dwFilterVersion > HTTP_FILTER_REVISION)
+    //    pVer->dwFilterVersion = HTTP_FILTER_REVISION;
+
+	/* Come back and check these... */
+    pVer->dwFlags = SF_NOTIFY_ORDER_HIGH        | 
+                    SF_NOTIFY_SECURE_PORT       | 
+                    SF_NOTIFY_NONSECURE_PORT    |
+                    SF_NOTIFY_PREPROC_HEADERS;
+                    
+    strcpy(pVer->lpszFilterDesc, FILTERDESC);
+
+	/* Banner */
+	LogMessage("%s loaded", NOERROR, pVer->lpszFilterDesc);
+
+	return TRUE;
+
+initFailed:
+	LogMessage("Error loading %s", NOERROR, FILTERDESC);
+
+	return FALSE;
+}
+
+/* Read parameters from the registry
+ */
+static int ReadInitData(void)
+{
+	int ok = JK_TRUE;
+	const char *v;
+
+#ifdef USE_INIFILE
+// Using an INIFILE
+
+#define GETV(key) inifile_lookup(key)
+
+	ERRTYPE e;
+
+	if (e = inifile_read(&cfgPool, ININAME), ERRNONE != e)
+	{
+		LogMessage("Error reading: %s, %s", NOERROR, ININAME, ERRTXT(e));
+		return JK_FALSE;
+	}
+
+#else
+// Using the registry
+#define GETV(key) GetRegString(hkey, key)
+
+	HKEY hkey;
+	long rc;
+
+	rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_LOCATION, (DWORD) 0, KEY_READ, &hkey);
+	if (ERROR_SUCCESS != rc) return JK_FALSE;
+
+#endif
+
+#define GETVNB(tag, var) \
+	var = GETV(tag); \
+	if (NULL == var) \
+	{ \
+		LogMessage("%s not defined in %s", NOERROR, tag, ININAME); \
+		ok = JK_FALSE; \
+	}
+
+	GETVNB(JK_LOG_FILE_TAG, logFile)
+	GETVNB(JK_LOG_LEVEL_TAG, v);
+	GETVNB(JK_WORKER_FILE_TAG, workerFile);
+	GETVNB(JK_MOUNT_FILE_TAG, workerMountFile);
+
+	logLevel = (NULL == v) ? 0 : jk_parse_log_level(v);
+
+	tomcatStart	= GETV(TOMCAT_START);
+	tomcatStop	= GETV(TOMCAT_STOP);
+
+#ifndef USE_INIFILE
+	RegCloseKey(hkey);
+#endif
+
+	return ok;
+}
+
+#ifndef USE_INIFILE
+static const char *GetRegString(HKEY hkey, const char *key)
+{
+	DWORD type = 0;
+	DWORD sz = 0;
+	LONG rc;
+	char *val;
+
+	rc = RegQueryValueEx(hkey, key, (LPDWORD) 0, &type, NULL, &sz);
+	if (rc != ERROR_SUCCESS || type != REG_SZ)
+		return NULL;
+
+	if (val = jk_pool_alloc(&cfgPool, sz), NULL == val)
+		return NULL;
+
+	rc = RegQueryValueEx(hkey, key, (LPDWORD) 0, &type, val, &sz);
+	if (rc == ERROR_SUCCESS)
+		return val;
+
+	return NULL;
+}
+#endif
+
+/* Main entry point for the filter. Called by the server for every HTTP request.
+ */
+DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc,
+                            DWORD dwNotificationType, 
+                            LPVOID pvNotification)
+{
+#if 0
+	switch (dwNotificationType)
+	{
+	case kFilterParsedRequest:
+		return ParsedRequest(context, (FilterParsedRequest *) eventData);
+	default:
+		break;
+	}
+	return kFilterNotHandled;
+#endif
+
+	return SF_STATUS_REQ_NEXT_NOTIFICATION;
+}
+
+/* Send a simple response. Used when we don't want to bother Tomcat,
+ * which in practice means for various error conditions that we can
+ * detect internally.
+ */
+static void SimpleResponse(PHTTP_FILTER_CONTEXT *context, int status, char *reason, char *body)
+{
+#if 0
+	FilterResponseHeaders frh;
+	int rc, errID;
+	char hdrBuf[35];
+
+	sprintf(hdrBuf, "Content-type: text/html%s%s", crlf, crlf);
+
+	frh.responseCode = status;
+	frh.reasonText = reason;
+	frh.headerText = hdrBuf;
+
+	rc = context->ServerSupport(context, kWriteResponseHeaders, &frh, NULL, 0, &errID);
+	rc = context->WriteClient(context, body, strlen(body), 0, &errID);
+#endif
+}
+
+/* Called to reject a URI that contains the string "web-inf". We block
+ * these because they may indicate an attempt to invoke arbitrary code.
+ */
+static DWORD RejectBadURI(PHTTP_FILTER_CONTEXT *context)
+{
+	static char *msg = "<HTML><BODY><H1>Access is Forbidden</H1></BODY></HTML>";
+
+	SimpleResponse(context, 403, "Forbidden", msg);
+	return SF_STATUS_REQ_NEXT_NOTIFICATION;
+}
+
+/* Allocate space for a string given a start pointer and an end pointer
+ * and return a pointer to the allocated, copied string.
+ */
+static char *MemDup(private_ws_t *ws, const char *start, const char *end)
+{
+	char *out = NULL;
+
+	if (start != NULL && end != NULL && end > start)
+	{
+		int len = end - start;
+		out = jk_pool_alloc(&ws->p, len + 1);
+		memcpy(out, start, len);
+		out[len] = '\0';
+	}
+
+	return out;
+}
+
+/* Given all the HTTP headers as a single string parse them into individual
+ * name, value pairs. Called twice: once to work out how many headers there
+ * are, then again to copy them.
+ */
+static int ParseHeaders(private_ws_t *ws, const char *hdrs, int hdrsz, jk_ws_service_t *s)
+{
+	int hdrCount = 0;
+	const char *limit = hdrs + hdrsz;
+	const char *name, *nameEnd;
+	const char *value, *valueEnd;
+
+    	/* Clear RECO status */
+    	s->reco_status  = RECO_NONE;
+
+	while (hdrs < limit)
+	{
+		/* Skip line *before* doing anything, cos we want to lose the first line which
+		 * contains the request.
+		 */
+		while (hdrs < limit && (*hdrs != '\n' && *hdrs != '\r'))
+			hdrs++;
+		while (hdrs < limit && (*hdrs == '\n' || *hdrs == '\r'))
+			hdrs++;
+
+		if (hdrs >= limit)
+			break;
+
+		name = nameEnd = value = valueEnd = NULL;
+
+		name = hdrs;
+		while (hdrs < limit && *hdrs >= ' ' && *hdrs != ':')
+			hdrs++;
+		nameEnd = hdrs;
+
+		if (hdrs < limit && *hdrs == ':')
+		{
+			hdrs++;
+			while (hdrs < limit && (*hdrs == ' ' || *hdrs == '\t'))
+				hdrs++;
+			value = hdrs;
+			while (hdrs < limit && *hdrs >= ' ')
+				hdrs++;
+			valueEnd = hdrs;
+		}
+
+		if (s->headers_names != NULL && s->headers_values != NULL)
+		{
+			s->headers_names[hdrCount]	= MemDup(ws, name, nameEnd);
+			s->headers_values[hdrCount] = MemDup(ws, value, valueEnd);
+			DEBUG(("%s = %s\n", s->headers_names[hdrCount], s->headers_values[hdrCount]));
+		}
+		hdrCount++;
+	}
+
+	return hdrCount;
+}
+
+#if 0
+/* Set up all the necessary jk_* workspace based on the current HTTP request.
+ */
+static int InitService(private_ws_t *ws, jk_ws_service_t *s)
+{
+	/* This is the only fixed size buffer left. It won't be overflowed
+	 * because the the server API that reads into the buffer accepts a length
+	 * constraint, and it's unlikely ever to be exhausted because the
+	 * strings being will typically be short, but it's still aesthetically
+	 * troublesome.
+	 */
+	char workBuf[16 * 1024];
+	FilterRequest fr;
+	char *hdrs, *qp;
+	int hdrsz;
+	int errID;
+	int hdrCount;
+	int rc /*, dummy*/;
+
+	static char *methodName[] = { "", "HEAD", "GET", "POST", "PUT", "DELETE" };
+
+	rc = ws->context->GetRequest(ws->context, &fr, &errID);
+
+	s->jvm_route		= NULL;
+	s->start_response	= StartResponse;
+	s->read				= Read;
+	s->write			= Write;
+
+	s->req_uri = jk_pool_strdup(&ws->p, fr.URL);
+	s->query_string = NULL;
+	if (qp = strchr(s->req_uri, '?'), qp != NULL)
+	{
+		*qp++ = '\0';
+		if (strlen(qp))
+			s->query_string = qp;
+	}
+
+	GETVARIABLE("AUTH_TYPE", &s->auth_type, "");
+	GETVARIABLE("REMOTE_USER", &s->remote_user, "");
+	GETVARIABLE("SERVER_PROTOCOL", &s->protocol, "");
+	GETVARIABLE("REMOTE_HOST", &s->remote_host, "");
+	GETVARIABLE("REMOTE_ADDR", &s->remote_addr, "");
+	GETVARIABLE("SERVER_NAME", &s->server_name, "");
+	GETVARIABLEINT("SERVER_PORT", &s->server_port, 80);
+	GETVARIABLE("SERVER_SOFTWARE", &s->server_software, SERVERDFLT);
+	GETVARIABLEINT("CONTENT_LENGTH", &s->content_length, 0);
+
+
+	/* SSL Support
+	 */
+	GETVARIABLEBOOL("HTTPS", &s->is_ssl, 0);
+
+	if (ws->reqData->requestMethod < 0 ||
+		ws->reqData->requestMethod >= sizeof(methodName) / sizeof(methodName[0]))
+		return JK_FALSE;
+
+	s->method = methodName[ws->reqData->requestMethod];
+
+	s->headers_names	= NULL;
+	s->headers_values	= NULL;
+	s->num_headers		= 0;
+
+	s->ssl_cert_len	= fr.clientCertLen;
+	s->ssl_cert		= fr.clientCert;
+	s->ssl_cipher	= NULL;		/* required by Servlet 2.3 Api */
+	s->ssl_session	= NULL;
+
+#if defined(JK_VERSION) && JK_VERSION >= MAKEVERSION(1, 2, 0, 1)
+	s->ssl_key_size = -1;       /* required by Servlet 2.3 Api, added in jtc */
+#endif
+
+	if (s->is_ssl)
+	{
+		int dummy;
+#if 0
+		char *sslNames[] =
+		{
+			"CERT_ISSUER", "CERT_SUBJECT", "CERT_COOKIE", "CERT_FLAGS", "CERT_SERIALNUMBER",
+			"HTTPS_SERVER_SUBJECT", "HTTPS_SECRETKEYSIZE", "HTTPS_SERVER_ISSUER", "HTTPS_KEYSIZE"
+		};
+
+		char *sslValues[] =
+		{
+			NULL, NULL, NULL, NULL, NULL,
+			NULL, NULL, NULL, NULL
+		};
+
+
+		unsigned i, varCount = 0;
+#endif
+
+		DEBUG(("SSL request\n"));
+
+#if defined(JK_VERSION) && JK_VERSION >= MAKEVERSION(1, 2, 0, 1)
+		/* Read the variable into a dummy variable: we do this for the side effect of
+		 * reading it into workBuf.
+		 */
+		GETVARIABLEINT("HTTPS_KEYSIZE", &dummy, 0);
+		if (workBuf[0] == '[')
+			s->ssl_key_size = atoi(workBuf+1);
+#else
+		(void) dummy;
+#endif
+
+#if 0
+		for (i = 0; i < sizeof(sslNames)/sizeof(sslNames[0]); i++)
+		{
+			GETVARIABLE(sslNames[i], &sslValues[i], NULL);
+			if (sslValues[i]) varCount++;
+		}
+
+		/* Andy, some SSL vars must be mapped directly in  s->ssl_cipher,
+         * ssl->session and s->ssl_key_size
+		 * ie:
+		 * Cipher could be "RC4-MD5"
+		 * KeySize 128 (bits)
+	     * SessionID a string containing the UniqID used in SSL dialogue
+         */
+		if (varCount > 0)
+		{
+			unsigned j;
+
+			s->attributes_names = jk_pool_alloc(&ws->p, varCount * sizeof (char *));
+			s->attributes_values = jk_pool_alloc(&ws->p, varCount * sizeof (char *));
+
+			j = 0;
+			for (i = 0; i < sizeof(sslNames)/sizeof(sslNames[0]); i++)
+			{
+				if (sslValues[i])
+				{
+					s->attributes_names[j] = sslNames[i];
+					s->attributes_values[j] = sslValues[i];
+					j++;
+				}
+			}
+			s->num_attributes = varCount;
+		}
+#endif
+	}
+
+	/* Duplicate all the headers now */
+
+	hdrsz = ws->reqData->GetAllHeaders(ws->context, &hdrs, &errID);
+	DEBUG(("\nGot headers (length %d)\n--------\n%s\n--------\n\n", hdrsz, hdrs));
+
+	s->headers_names =
+	s->headers_values = NULL;
+	hdrCount = ParseHeaders(ws, hdrs, hdrsz, s);
+	DEBUG(("Found %d headers\n", hdrCount));
+	s->num_headers = hdrCount;
+	s->headers_names	= jk_pool_alloc(&ws->p, hdrCount * sizeof(char *));
+	s->headers_values	= jk_pool_alloc(&ws->p, hdrCount * sizeof(char *));
+	hdrCount = ParseHeaders(ws, hdrs, hdrsz, s);
+
+	return JK_TRUE;
+}
+#endif
+
+#if 0
+/* Handle an HTTP request. Works out whether Tomcat will be interested then either
+ * despatches it to Tomcat or passes it back to the server.
+ */
+static unsigned int ParsedRequest(PHTTP_FILTER_CONTEXT *context, FilterParsedRequest *reqData)
+{
+	unsigned int errID;
+	int rc;
+	FilterRequest fr;
+	int result = kFilterNotHandled;
+
+	DEBUG(("\nParsedRequest starting\n"));
+
+	rc = context->GetRequest(context, &fr, &errID);
+
+	if (fr.URL && strlen(fr.URL))
+	{
+		char *uri = fr.URL;
+		char *workerName, *qp;
+
+		if (!initDone)
+		{
+			/* One time initialisation which is deferred so that we have the name of
+			 * the server software to plug into worker_env
+			 */
+			int ok = JK_FALSE;
+			jk_map_t *map = NULL;
+
+			DEBUG(("Initialising worker map\n"));
+
+			if (map_alloc(&map))
+			{
+				if (map_read_properties(map, workerMountFile))
+					if (uri_worker_map_alloc(&uw_map, map, logger))
+						ok = JK_TRUE;
+				map_free(&map);
+			}
+
+			DEBUG(("Got the URI worker map\n"));
+
+			if (ok)
+			{
+				ok = JK_FALSE;
+				DEBUG(("About to allocate map\n"));
+				if (map_alloc(&map))
+				{
+					DEBUG(("About to read %s\n", workerFile));
+					if (map_read_properties(map, workerFile))
+					{
+#if defined(JK_VERSION) && JK_VERSION >= MAKEVERSION(1, 2, 0, 1)
+						char server[256];
+
+						worker_env.uri_to_worker = uw_map;
+						if (context->GetServerVariable(context, "SERVER_SOFTWARE", server, sizeof(server)-1, &errID))
+							worker_env.server_name = jk_pool_strdup(&cfgPool, server);
+						else
+							worker_env.server_name = SERVERDFLT;
+
+						DEBUG(("Server name %s\n", worker_env.server_name));
+
+						if (wc_open(map, &worker_env, logger))
+							ok = JK_TRUE;
+#else
+						if (wc_open(map, logger))
+							ok = JK_TRUE;
+#endif
+						DEBUG(("OK = %d\n", ok));
+					}
+
+					DEBUG(("Read %s, OK = %d\n", workerFile, ok));
+					map_free(&map);
+				}
+			}
+
+			if (!ok) return kFilterError;
+			initDone = JK_TRUE;
+		}
+
+
+		if (qp = strchr(uri, '?'), qp != NULL) *qp = '\0';
+		workerName = map_uri_to_worker(uw_map, uri, logger);
+		if (qp) *qp = '?';
+
+		DEBUG(("Worker for this URL is %s\n", workerName));
+
+		if (NULL != workerName)
+		{
+			private_ws_t ws;
+			jk_ws_service_t s;
+			jk_pool_atom_t buf[SMALL_POOL_SIZE];
+
+			if (BadURI(uri))
+				return RejectBadURI(context);
+
+			/* Go dispatch the call */
+
+			jk_init_ws_service(&s);
+			jk_open_pool(&ws.p, buf, sizeof (buf));
+
+			ws.responseStarted	= JK_FALSE;
+			ws.context			= context;
+			ws.reqData			= reqData;
+
+			ws.reqSize = context->GetRequestContents(context, &ws.reqBuffer, &errID);
+
+			s.ws_private = &ws;
+			s.pool = &ws.p;
+
+			if (InitService(&ws, &s))
+			{
+				jk_worker_t *worker = wc_get_worker_for_name(workerName, logger);
+
+				jk_log(logger, JK_LOG_DEBUG, "HttpExtensionProc %s a worker for name %s\n",
+					   worker ? "got" : "could not get", workerName);
+
+				if (worker)
+				{
+					jk_endpoint_t *e = NULL;
+
+					if (worker->get_endpoint(worker, &e, logger))
+					{
+						int recover = JK_FALSE;
+
+						if (e->service(e, &s, logger, &recover))
+						{
+							result = kFilterHandledRequest;
+							jk_log(logger, JK_LOG_DEBUG, "HttpExtensionProc service() returned OK\n");
+							DEBUG(("HttpExtensionProc service() returned OK\n"));
+						}
+						else
+						{
+							result = kFilterError;
+							jk_log(logger, JK_LOG_ERROR, "HttpExtensionProc error, service() failed\n");
+							DEBUG(("HttpExtensionProc error, service() failed\n"));
+						}
+						e->done(&e, logger);
+					}
+				}
+				else
+				{
+					jk_log(logger, JK_LOG_ERROR,
+						   "HttpExtensionProc error, could not get a worker for name %s\n",
+						   workerName);
+				}
+			}
+
+			jk_close_pool(&ws.p);
+		}
+	}
+
+	return result;
+}
+#endif
+
+BOOL WINAPI DllMain(HINSTANCE hInst, ULONG ulReason, LPVOID lpReserved)
+{
+    BOOL fReturn = TRUE;
+
+    switch (ulReason) {
+
+    case DLL_PROCESS_DETACH:
+        TerminateFilter(HSE_TERM_MUST_UNLOAD);
+        break;
+
+    default:
+        break;
+    } 
+
+    return fReturn;
+}
diff --git a/connectors/jk/native/isapi/poolbuf.c b/connectors/jk/native/isapi/poolbuf.c
new file mode 100644
index 0000000..1d911b7
--- /dev/null
+++ b/connectors/jk/native/isapi/poolbuf.c
@@ -0,0 +1,148 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: ISAPI plugin for Tomcat                                    *
+ * Author:      Andy Armstrong <andy@tagish.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#include "poolbuf.h"
+
+/* Macro to return the address of the first byte in a poolbuf__chunk on
+ * the understanding that the buffer follows the structure in memory.
+ */
+#define poolbuf__buf(chnk) \
+	((char *) ((poolbuf__chunk *) chnk + 1))
+
+void poolbuf_init(poolbuf *pb, jk_pool_t *p)
+{
+	pb->p			= p;
+	pb->head		=
+	pb->current		= NULL;
+	pb->readPos		=
+	pb->writePos	= 0;
+	pb->avail		= 0;
+	pb->state		= WRITE;
+}
+
+/* Write bytes to the buffer returning the number of bytes successfully
+ * written. Can't be called again once poolbuf_read() has been called.
+ */
+size_t poolbuf_write(poolbuf *pb, const void *buf, size_t size)
+{
+	const char *cbuf = (const char *) buf;
+	size_t left = size;
+
+	if (READ == pb->state)
+		return 0;
+
+	/* first work out what we can write into the current buffer */
+	if (pb->current != NULL && pb->writePos < pb->current->size)
+	{
+		char *chbuf = poolbuf__buf(pb->current) + pb->writePos;
+		size_t sz = pb->current->size - pb->writePos;
+		if (sz > left) sz = left;
+		memcpy(chbuf, cbuf, sz);
+		pb->writePos += sz;
+		pb->avail += sz;
+		cbuf += sz;
+		left -= sz;
+	}
+
+	/* something left that we couldn't fit in the last chunk */
+	if (left > 0)
+	{
+		poolbuf__chunk *chnk;
+		size_t sz = size;
+
+		if (sz < poolbuf__MINCHUNK)
+			sz = poolbuf__MINCHUNK;
+		if (NULL == pb->p || NULL == (chnk = jk_pool_alloc(pb->p, sz + sizeof(poolbuf__chunk))))
+			return size - left;
+
+		chnk->next = NULL;
+		chnk->size = sz;
+		if (NULL == pb->head) pb->head = chnk;
+		if (NULL != pb->current) pb->current->next = chnk;
+		pb->current = chnk;
+		memcpy(poolbuf__buf(chnk), cbuf, left);
+		pb->avail += left;
+		pb->writePos = left;
+	}
+
+	return size;
+}
+
+/* Read bytes from the buffer returning the number of bytes read (which
+ * will be less than desired when the end of the buffer is reached). Once
+ * poolbuf_read() has been called poolbuf_write() may not be called again.
+ */
+size_t poolbuf_read(poolbuf *pb, void *buf, size_t size)
+{
+	char *cbuf = (char *) buf;
+	size_t nread = 0;
+
+	if (WRITE == pb->state)
+	{
+		/* Move to read mode. Once we've done this subsequent
+		 * writes are not allowed.
+		 */
+		pb->current = pb->head;
+		pb->readPos	= 0;
+		pb->state	= READ;
+	}
+
+	while (size > 0 && pb->avail > 0)
+	{
+		size_t sz = pb->current->size - pb->readPos;
+		if (sz > pb->avail) sz = pb->avail;
+		if (sz > size) sz = size;
+		memcpy(cbuf, poolbuf__buf(pb->current) + pb->readPos, sz);
+		pb->readPos += sz;
+		if (pb->readPos == pb->current->size)
+		{
+			pb->current = pb->current->next;
+			pb->readPos = 0;
+		}
+		pb->avail -= sz;
+		nread += sz;
+	}
+
+	return nread;
+}
+
+/* Find out how many bytes are available for reading.
+ */
+size_t poolbuf_available(poolbuf *pb)
+{
+	return pb->avail;
+}
+
+/* Destroy the buffer. This doesn't actually free any memory
+ * because the jk_pool functions don't support freeing individual
+ * chunks, but it does recycle the buffer for subsequent use.
+ */
+void poolbuf_destroy(poolbuf *pb)
+{
+	pb->p			= NULL;
+	pb->head		=
+	pb->current		= NULL;
+	pb->readPos		=
+	pb->writePos	= 0;
+	pb->avail		= 0;
+	pb->state		= WRITE;
+}
diff --git a/connectors/jk/native/isapi/poolbuf.h b/connectors/jk/native/isapi/poolbuf.h
new file mode 100644
index 0000000..28579f0
--- /dev/null
+++ b/connectors/jk/native/isapi/poolbuf.h
@@ -0,0 +1,66 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: ISAPI plugin for Tomcat                                    *
+ * Author:      Andy Armstrong <andy@tagish.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef __poolbuf_h
+#define __poolbuf_h
+
+#include <stddef.h>
+#include "jk_pool.h"
+
+#define poolbuf__MINCHUNK 2048
+
+typedef struct poolbuf__chunk
+{
+	size_t					size;
+	struct poolbuf__chunk	*next;
+
+} poolbuf__chunk;
+
+typedef enum { WRITE, READ } poolbuf__state;
+
+typedef struct
+{
+	jk_pool_t				*p;
+	poolbuf__chunk			*head;
+	poolbuf__chunk			*current;
+
+	/* current state */
+	poolbuf__state			state;
+
+	/* total number of bytes available to read */
+	size_t					avail;
+
+	/* offsets within the current chunk */
+	unsigned int			writePos;
+	unsigned int			readPos;
+
+} poolbuf;
+
+/* Initialise a poolbuf. */
+void poolbuf_init(poolbuf *pb, jk_pool_t *p);
+size_t poolbuf_write(poolbuf *pb, const void *buf, size_t size);
+size_t poolbuf_read(poolbuf *pb, void *buf, size_t size);
+size_t poolbuf_available(poolbuf *pb);
+void poolbuf_destroy(poolbuf *pb);
+
+
+#endif /* __poolbuf_h */
diff --git a/connectors/jk/native/isapi/tomcat_redirector.reg b/connectors/jk/native/isapi/tomcat_redirector.reg
new file mode 100644
index 0000000..d09ea0e
--- /dev/null
+++ b/connectors/jk/native/isapi/tomcat_redirector.reg
@@ -0,0 +1,9 @@
+REGEDIT4
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\2.0]
+"log_file"="D:\\tomcat\\logs\\domino.log"
+"log_level"="debug"
+"worker_file"="D:\\tomcat\\conf\\workers.properties"
+"worker_mount_file"="D:\\tomcat\\conf\\uriworkermap.properties"
+"tomcat_start"="D:\\tomcat\\bin\\tomcat.bat start"
+"tomcat_stop"="D:\\tomcat\\bin\\tomcat.bat stop"
diff --git a/connectors/jk/native/jni/Makefile.in b/connectors/jk/native/jni/Makefile.in
new file mode 100644
index 0000000..a7124e2
--- /dev/null
+++ b/connectors/jk/native/jni/Makefile.in
@@ -0,0 +1,29 @@
+OEXT=.lo
+include @APACHE_CONFIG_VARS@
+
+JK=../common/
+COMMON_OBJECTS=${JK}jk_map${OEXT} ${JK}jk_util${OEXT} ${JK}jk_pool${OEXT}
+JNI_OBJECTS=jk_jnicb${OEXT} ${COMMON_OBJECTS}
+
+
+JAVA_INCL=-I @JAVA_HOME@/include -I @JAVA_HOME@/include/@OS@ -I../common
+CFLAGS=@apache_include@ @CFLAGS@ ${JAVA_INCL} -D_REENTRANT 
+
+include ../scripts/build/rules.mk
+
+all: Makefile jni_connect.so 
+
+Makefile: Makefile.in
+	echo Regenerating Makefile
+	( cd ..; ./config.status )
+
+jk_jnicb.la: ${JNI_OBJECTS}
+	$(LIBTOOL) --mode=link $(COMPILE) -module -o $@ -rpath `pwd` -g -O2 -avoid-version ${JNI_OBJECTS} 
+
+jni_connect.so: jk_jnicb.la
+	$(LIBTOOL) --mode=install cp $< `pwd`/jni_connect.so
+
+install:
+
+clean:
+	rm -f *.o *.slo *.lo *.so *.la .libs/*
diff --git a/connectors/jk/native/jni/Makefile.linux b/connectors/jk/native/jni/Makefile.linux
new file mode 100644
index 0000000..7cce546
--- /dev/null
+++ b/connectors/jk/native/jni/Makefile.linux
@@ -0,0 +1,31 @@
+# Defines for example NSAPI programs running under SOLARIS
+
+CC_CMD=gcc -D_REENTRANT
+LD_SHAREDCMD=ld -G
+
+all:
+
+prepare:
+
+OS_TYPE=linux
+INCLUDEDIR=../common
+JAVA_INCLUDE=$(JAVA_HOME)/include
+
+JK_OBJS =  ../common/jk_map.o ../common/jk_util.o ../common/jk_pool.o jk_jnicb.o
+
+INCLUDE_FLAGS=-I$(INCLUDEDIR) -I$(JAVA_INCLUDE) -I$(JAVA_INCLUDE)/$(OS_TYPE)
+COMMON_DEFS=
+
+
+all: jni_connect.so 
+
+
+jni_connect.so: $(JK_OBJS)
+#	$(MAKE) prepare
+	$(LD_SHAREDCMD) $(JK_OBJS) -o jni_connect.so $(EXTRA_LDDEFINES)
+
+.c.o:
+	$(CC_CMD) $(COMMON_DEFS) $(INCLUDE_FLAGS) -c $< 
+
+clean:
+	rm $(JK_OBJS)
diff --git a/connectors/jk/native/jni/Makefile.netware b/connectors/jk/native/jni/Makefile.netware
new file mode 100644
index 0000000..ff716b7
--- /dev/null
+++ b/connectors/jk/native/jni/Makefile.netware
@@ -0,0 +1,241 @@
+#
+# Makefile for jk_nsapi_plugin (NetWare version - gnu make)
+# created by Guenter Knauf <eflash@gmx.net>
+#
+
+# Edit the path below to point to the base of your Netscape includes.
+ifndef NS_HOME
+NS_HOME	= c:/projects/sdks/netscape
+endif
+# Edit the path below to point to the base of your NetWare Java SDK.
+ifndef NW_JDK
+NW_JDK	= c:/projects/sdks/java-nw
+endif
+# Edit the path below to point to the base of your Novell NDK.
+ifndef NDKBASE
+NDKBASE	= c:/novell
+endif
+INSTDIR = /mnt/sys/novonyx/modules
+
+# Edit the vars below to change NLM target settings.
+TARGET  = jni_conn
+VERSION	= $(JK_VERSION)
+COPYR	= Copyright (c) 2000-2004 The Apache Software Foundation. All rights reserved.
+DESCR	= JNI natives for Jakarta/Tomcat $(JK_VERSION_STR)
+MTSAFE	= NO
+STACK	= 64000
+#SCREEN	= System Console
+MODULES	= nsapi
+EXPORTS	= \
+	Java_org_apache_tomcat_modules_server_JNIConnectionHandler_write \
+	Java_org_apache_tomcat_modules_server_JNIConnectionHandler_startReasponse \
+	Java_org_apache_tomcat_modules_server_JNIConnectionHandler_readHeaders \
+	Java_org_apache_tomcat_modules_server_JNIConnectionHandler_readEnvironment \
+	Java_org_apache_tomcat_modules_server_JNIConnectionHandler_read \
+	Java_org_apache_tomcat_modules_server_JNIConnectionHandler_getNumberOfHeaders
+
+#IMPORTS	= __nsapi30_table
+
+# Edit the var below to point to your lib architecture.
+ifndef LIBARCH
+LIBARCH = CLIB
+# LIBARCH = LIBC
+endif
+
+# must be equal to DEBUG or NDEBUG
+DB	= NDEBUG
+# DB	= DEBUG
+# Optimization: -O<n> or debugging: -g
+ifeq ($(DB),NDEBUG)
+	OPT	= -O2
+	OBJDIR	= release
+else
+	OPT	= -g
+	OBJDIR	= debug
+endif
+
+# Include the version info retrieved from jk_version.h
+-include $(OBJDIR)/version.inc
+
+# The following line defines your compiler.
+ifdef METROWERKS
+	CC = mwccnlm
+else
+	CC = gcc
+endif
+# RM	= rm -f
+# CP	= cp -fv
+# if you want to mark the target as MTSAFE you will need a tool for
+# generating the xdc data for the linker; here's a minimal tool:
+# http://www.gknw.com/development/prgtools/mkxdc.zip
+MPKXDC	= mkxdc
+
+# Global flags for all compilers
+CFLAGS	= $(OPT) -D$(DB) -DNETWARE -DXP_NETWARE -nostdinc
+
+ifeq ($(CC),mwccnlm)
+LD	= mwldnlm
+LDFLAGS	= -nostdlib $(PRELUDE) $(OBJDIR)/*.o -o $(OBJDIR)/$(TARGET).nlm -commandfile
+CFLAGS	+= -gccinc -inline off -opt nointrinsics
+#CFLAGS	+= -w on
+ifeq ($(LIBARCH),LIBC)
+	PRELUDE = $(SDK_LIBC)/imports/libcpre.o
+	CFLAGS += -align 4 -inst mmx -proc 686
+#	CFLAGS += -D__ANSIC__
+else
+	PRELUDE = "$(METROWERKS)/Novell Support/libraries/runtime/prelude.obj"
+#	CFLAGS += -include "$(METROWERKS)/Novell Support/headers/nlm_prefix.h"
+	CFLAGS += -align 1 -proc 586
+endif
+else
+LD	= nlmconv
+LDFLAGS	= -T
+CFLAGS	+= -fno-builtin -fpack-struct -fpcc-struct-return
+CFLAGS	+= -Wall -Wno-main # -pedantic
+ifeq ($(LIBARCH),LIBC)
+	PRELUDE = $(SDK_LIBC)/imports/libcpre.gcc.o
+#	CFLAGS += -D__ANSIC__
+else
+	PRELUDE = $(SDK_CLIB)/imports/clibpre.gcc.o
+	CFLAGS += -include $(NDKBASE)/nlmconv/genlm.h
+endif
+endif
+
+LDLIBS	=
+
+NDK_ROOT = $(NDKBASE)/ndk
+SDK_CLIB = $(NDK_ROOT)/nwsdk
+SDK_LIBC = $(NDK_ROOT)/libc
+JKCOMMON = ../common
+
+INCLUDES = -I$(NS_HOME)/include -I$(NS_HOME)/include/base
+INCLUDES += -I$(JKCOMMON) -I$(NW_JDK)/include -I$(NW_JDK)/include/netware 
+
+ifeq ($(LIBARCH),LIBC)
+	INCLUDES += -I$(SDK_LIBC)/include -I$(SDK_LIBC)/include/nks
+	INCLUDES += -I$(SDK_LIBC)/include/winsock
+else
+	INCLUDES += -I$(SDK_CLIB)/include/nlm -I$(SDK_CLIB)/include
+	INCLUDES += -I$(SDK_CLIB)/include/nlm/obsolete
+	CFLAGS += -DNETDB_USE_INTERNET
+endif
+CFLAGS	+= $(INCLUDES)
+
+ifeq ($(MTSAFE),YES)
+	XDCDATA = $(OBJDIR)/$(TARGET).xdc
+endif
+
+ifeq ($(findstring linux,$(OSTYPE)),linux)
+DL	= '
+#-include $(NDKBASE)/nlmconv/ncpfs.inc
+endif
+
+OBJS	= \
+	$(OBJDIR)/jk_nwmain.o \
+	$(OBJDIR)/jk_map.o \
+	$(OBJDIR)/jk_pool.o \
+	$(OBJDIR)/jk_util.o \
+	$(OBJDIR)/jk_jnicb.o
+
+
+all: $(OBJDIR) $(OBJDIR)/version.inc $(OBJDIR)/$(TARGET).nlm 
+
+$(OBJDIR)/%.o: %.c
+	@echo Compiling $<
+	@$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJDIR)/%.o: $(JKCOMMON)/%.c
+	@echo Compiling $<
+	@$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJDIR)/version.inc: $(JKCOMMON)/jk_version.h $(OBJDIR)
+	@echo Creating $@
+	@awk -f ../../../common/build/get_ver.awk $< > $@
+
+dist: all
+	-$(RM) $(OBJDIR)/*.o $(OBJDIR)/$(TARGET).map $(OBJDIR)/$(TARGET).ncv
+	-$(RM) $(OBJDIR)/$(TARGET).def $(OBJDIR)/version.inc $(XDCDATA)
+#	-$(CP) ../changes.txt $(OBJDIR)/
+
+install: all
+	@[ -d $(INSTDIR) ] || mkdir $(INSTDIR)
+	@$(CP) $(TARGET).nlm $(INSTDIR)
+
+clean:
+	-$(RM) -r $(OBJDIR)
+
+$(OBJDIR):
+	@mkdir $(OBJDIR)
+
+$(OBJDIR)/$(TARGET).nlm: $(OBJS) $(OBJDIR)/$(TARGET).def $(XDCDATA)
+	@echo Linking $@
+	@-$(RM) $@
+	@$(LD) $(LDFLAGS) $(OBJDIR)/$(TARGET).def
+
+$(OBJDIR)/%.xdc: Makefile.netware
+	@echo Creating $@
+	@$(MPKXDC) $(XDCOPT) $@
+
+$(OBJDIR)/%.def: Makefile.netware
+	@echo $(DL)# DEF file for linking with $(LD)$(DL) > $@
+	@echo $(DL)# Do not edit this file - it is created by make!$(DL) >> $@
+	@echo $(DL)# All your changes will be lost!!$(DL) >> $@
+	@echo $(DL)#$(DL) >> $@
+	@echo $(DL)copyright "$(COPYR)"$(DL) >> $@
+	@echo $(DL)description "$(DESCR)"$(DL) >> $@
+	@echo $(DL)version $(VERSION)$(DL) >> $@
+ifdef NLMTYPE
+	@echo $(DL)type $(NLMTYPE)$(DL) >> $@
+endif
+ifdef STACK
+	@echo $(DL)stack $(STACK)$(DL) >> $@
+endif
+ifdef SCREEN
+	@echo $(DL)screenname "$(SCREEN)"$(DL) >> $@
+else
+	@echo $(DL)screenname "DEFAULT"$(DL) >> $@
+endif
+ifeq ($(DB),DEBUG)
+	@echo $(DL)debug$(DL) >> $@
+endif
+	@echo $(DL)threadname "$(TARGET)"$(DL) >> $@
+ifdef XDCDATA
+	@echo $(DL)xdcdata $(XDCDATA)$(DL) >> $@
+endif
+ifeq ($(LIBARCH),CLIB)
+	@echo $(DL)start _Prelude$(DL) >> $@
+	@echo $(DL)exit _Stop$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/clib.imp$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/threads.imp$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/nlmlib.imp$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/socklib.imp$(DL) >> $@
+	@echo $(DL)module clib$(DL) >> $@
+else
+ifeq ($(LD),nlmconv)
+	@echo $(DL)flag_on 64$(DL) >> $@
+else
+	@echo $(DL)autounload$(DL) >> $@
+endif
+	@echo $(DL)pseudopreemption$(DL) >> $@
+	@echo $(DL)start _LibCPrelude$(DL) >> $@
+	@echo $(DL)exit _LibCPostlude$(DL) >> $@
+	@echo $(DL)check _LibCCheckUnload$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/libc/imports/libc.imp$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/libc/imports/netware.imp$(DL) >> $@
+	@echo $(DL)module libc$(DL) >> $@
+endif
+ifdef MODULES
+	@echo $(DL)module $(MODULES)$(DL) >> $@
+endif
+ifdef EXPORTS
+	@echo $(DL)export $(EXPORTS)$(DL) >> $@
+endif
+ifdef IMPORTS
+	@echo $(DL)import $(IMPORTS)$(DL) >> $@
+endif
+ifeq ($(LD),nlmconv)
+	@echo $(DL)input $(OBJS)$(DL) >> $@
+	@echo $(DL)input $(PRELUDE)$(DL) >> $@
+	@echo $(DL)output $(TARGET).nlm$(DL) >> $@
+endif
+
diff --git a/connectors/jk/native/jni/Makefile.solaris b/connectors/jk/native/jni/Makefile.solaris
new file mode 100644
index 0000000..368af75
--- /dev/null
+++ b/connectors/jk/native/jni/Makefile.solaris
@@ -0,0 +1,30 @@
+# Defines for example NSAPI programs running under SOLARIS
+
+CC_CMD=gcc -DSOLARIS -D_REENTRANT
+LD_SHAREDCMD=ld -G
+
+all:
+prepare:
+
+OS_TYPE=solaris
+INCLUDEDIR=../common
+JAVA_INCLUDE=$(JAVA_HOME)/include
+
+JK_OBJS =  ../common/jk_map.o ../common/jk_util.o ../common/jk_pool.o jk_jnicb.o
+
+INCLUDE_FLAGS=-I$(INCLUDEDIR) -I$(JAVA_INCLUDE) -I$(JAVA_INCLUDE)/$(OS_TYPE)
+COMMON_DEFS=
+
+
+all: jni_connect.so 
+
+
+jni_connect.so: $(JK_OBJS)
+	$(MAKE) prepare
+	$(LD_SHAREDCMD) $(JK_OBJS) -o jni_connect.so $(EXTRA_LDDEFINES)
+
+.c.o:
+	$(CC_CMD) $(COMMON_DEFS) $(INCLUDE_FLAGS) -c $< 
+
+clean:
+	rm $(JK_OBJS)
diff --git a/connectors/jk/native/jni/jk_jnicb.c b/connectors/jk/native/jni/jk_jnicb.c
new file mode 100644
index 0000000..9c0cc1e
--- /dev/null
+++ b/connectors/jk/native/jni/jk_jnicb.c
@@ -0,0 +1,462 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: JNI callbacks implementation for the JNI in process adapter*
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#include "jk_jnicb.h"
+#include "jk_service.h"
+#include "jk_util.h"
+#include "jk_pool.h"
+
+/*
+ * Class:     org_apache_tomcat_modules_server_JNIConnectionHandler
+ * Method:    getNumberOfHeaders
+ * Signature: (JJ)I
+ */
+JNIEXPORT jint JNICALL 
+Java_org_apache_tomcat_modules_server_JNIConnectionHandler_getNumberOfHeaders
+  (JNIEnv *env, jobject o, jlong s, jlong l)
+{
+    /* [V] Convert indirectly from jlong -> int -> pointer to shut up gcc */
+    /*     I hope it's okay on other compilers and/or machines...         */
+    jk_ws_service_t *ps = (jk_ws_service_t *)(int)s;
+    jk_logger_t *pl = (jk_logger_t *)(int)l;
+
+    jk_log(pl, JK_LOG_DEBUG, "Into JNIConnectionHandler::getNumberOfHeaders\n");
+
+    if(!ps) {
+	jk_log(pl, JK_LOG_ERROR, 
+               "In JNIConnectionHandler::getNumberOfHeaders, NULL ws service object\n");
+	/* [V] JNIConnectionHandler doesn't handle this */
+	return -1;
+    }
+
+    jk_log(pl, JK_LOG_DEBUG,
+	   "Done JNIConnectionHandler::getNumberOfHeaders, found %d headers\n",
+	   ps->num_headers);
+    return (jint)ps->num_headers;
+}
+
+/*
+ * Class:     org_apache_tomcat_modules_server_JNIConnectionHandler
+ * Method:    read
+ * Signature: (JJ[BII)I
+ */
+JNIEXPORT jint JNICALL 
+Java_org_apache_tomcat_modules_server_JNIConnectionHandler_read
+  (JNIEnv *env, jobject o, jlong s, jlong l, jbyteArray buf, jint from, jint cnt)
+{
+    jk_ws_service_t *ps = (jk_ws_service_t *)(int)s;
+    jk_logger_t *pl = (jk_logger_t *)(int)l;
+    jint rc = -1;
+    jboolean iscommit;
+    jbyte *nbuf;
+    unsigned nfrom = (unsigned)from;
+    unsigned ncnt = (unsigned)cnt;
+    unsigned acc = 0;
+
+    jk_log(pl, JK_LOG_DEBUG, "Into JNIConnectionHandler::read\n");
+
+    if(!ps) {
+        jk_log(pl, JK_LOG_ERROR, 
+               "In JNIConnectionHandler::read, NULL ws service object\n");
+	return -1;
+    }
+
+    nbuf = (*env)->GetByteArrayElements(env, buf, &iscommit);
+
+    if(!nbuf) {
+        jk_log(pl, JK_LOG_ERROR, 
+               "In JNIConnectionHandler::read, GetByteArrayElements error\n");
+	return -1;
+    }
+    
+    if(!ps->read(ps, nbuf + nfrom, ncnt, &acc)) {
+        jk_log(pl, JK_LOG_ERROR, 
+               "In JNIConnectionHandler::read, failed to read from web server\n");
+    } else {
+        rc = (jint)acc;
+    }
+
+    (*env)->ReleaseByteArrayElements(env, buf, nbuf, 0);
+
+    jk_log(pl, JK_LOG_DEBUG, "Done JNIConnectionHandler::read\n");
+    return rc;
+}
+
+/*
+ * Class:     org_apache_tomcat_modules_server_JNIConnectionHandler
+ * Method:    readEnvironment
+ * Signature: (JJ[Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL 
+Java_org_apache_tomcat_modules_server_JNIConnectionHandler_readEnvironment
+  (JNIEnv *env, jobject o, jlong s, jlong l, jobjectArray envbuf)
+{
+    jk_ws_service_t *ps = (jk_ws_service_t *)(int)s;
+    jk_logger_t *pl = (jk_logger_t *)(int)l;
+    char port[10];
+
+    jk_log(pl, JK_LOG_DEBUG, 
+           "Into JNIConnectionHandler::readEnvironment. Environment follows --->\n");
+
+    if(!ps) {
+        jk_log(pl, JK_LOG_ERROR, 
+               "In JNIConnectionHandler::readEnvironment, NULL ws service object\n");
+	return JK_FALSE;
+    }
+
+    sprintf(port, "%d", ps->server_port);
+        
+    if(ps->method) {
+        (*env)->SetObjectArrayElement(env, 
+                                      envbuf, 
+                                      0, 
+                                      (*env)->NewStringUTF(env, ps->method));
+	jk_log(pl, JK_LOG_DEBUG, "---> method: %s\n", ps->method);
+    }
+    if(ps->req_uri) {
+        (*env)->SetObjectArrayElement(env, 
+                                      envbuf, 
+                                      1, 
+                                      (*env)->NewStringUTF(env, ps->req_uri));
+	jk_log(pl, JK_LOG_DEBUG, "---> req_uri: %s\n", ps->req_uri);
+    }
+    if(ps->query_string) {
+        (*env)->SetObjectArrayElement(env, 
+                                      envbuf, 
+                                      2, 
+                                      (*env)->NewStringUTF(env, ps->query_string));
+        jk_log(pl, JK_LOG_DEBUG, "---> query_string: %s\n", ps->query_string);
+    }
+    if(ps->remote_addr) {
+        (*env)->SetObjectArrayElement(env, 
+                                      envbuf, 
+                                      3, 
+                                      (*env)->NewStringUTF(env, ps->remote_addr));
+	jk_log(pl, JK_LOG_DEBUG, "---> remote_addr: %s\n", ps->remote_addr);
+    }
+    if(ps->remote_host) {
+        (*env)->SetObjectArrayElement(env, 
+                                      envbuf, 
+                                      4, 
+                                      (*env)->NewStringUTF(env, ps->remote_host));
+	jk_log(pl, JK_LOG_DEBUG, "---> remote_host: %s\n", ps->remote_host);
+    }
+    if(ps->server_name) {
+        (*env)->SetObjectArrayElement(env, 
+                                      envbuf, 
+                                      5, 
+                                      (*env)->NewStringUTF(env, ps->server_name));
+	jk_log(pl, JK_LOG_DEBUG, "---> server_name: %s\n", ps->server_name);
+    }
+
+    (*env)->SetObjectArrayElement(env, 
+                                  envbuf, 
+                                  6, 
+                                  (*env)->NewStringUTF(env, port));
+    jk_log(pl, JK_LOG_DEBUG, "---> server_port: %s\n", port);
+
+    if(ps->auth_type) {
+        (*env)->SetObjectArrayElement(env, 
+                                      envbuf, 
+                                      7, 
+                                      (*env)->NewStringUTF(env, ps->auth_type));
+	jk_log(pl, JK_LOG_DEBUG, "---> auth_type: %s\n", ps->auth_type);
+    }
+    if(ps->remote_user) {
+        (*env)->SetObjectArrayElement(env, 
+                                      envbuf, 
+                                      8, 
+                                      (*env)->NewStringUTF(env, ps->remote_user));
+	jk_log(pl, JK_LOG_DEBUG, "---> remote_user: %s\n", ps->remote_user);
+    }
+    if(ps->is_ssl) {
+        (*env)->SetObjectArrayElement(env, 
+                                      envbuf, 
+                                      9, 
+                                      (*env)->NewStringUTF(env, "https"));
+    } else {
+        (*env)->SetObjectArrayElement(env, 
+                                      envbuf, 
+                                      9, 
+                                      (*env)->NewStringUTF(env, "http"));
+    }
+    jk_log(pl, JK_LOG_DEBUG, "---> is_ssl: %s\n", ps->is_ssl ? "yes" : "no");
+
+    if(ps->protocol) {
+        (*env)->SetObjectArrayElement(env, 
+                                      envbuf, 
+                                      10, 
+                                      (*env)->NewStringUTF(env, ps->protocol));
+        jk_log(pl, JK_LOG_DEBUG, "---> protocol: %s\n", ps->protocol);
+    }
+    if(ps->server_software) {
+        (*env)->SetObjectArrayElement(env, 
+                                      envbuf, 
+                                      11, 
+                                      (*env)->NewStringUTF(env, ps->server_software));
+        jk_log(pl, JK_LOG_DEBUG, "---> server_software: %s\n", ps->server_software);
+    }
+    if(ps->is_ssl) {
+        if(ps->ssl_cert) {
+            (*env)->SetObjectArrayElement(env, 
+                                          envbuf, 
+                                          12, 
+                                          (*env)->NewStringUTF(env, ps->ssl_cert));
+            jk_log(pl, JK_LOG_DEBUG, "---> ssl_cert: %s\n", ps->ssl_cert);
+        }
+        
+        if(ps->ssl_cipher) {
+            (*env)->SetObjectArrayElement(env, 
+                                          envbuf, 
+                                          13, 
+                                          (*env)->NewStringUTF(env, ps->ssl_cipher));
+            jk_log(pl, JK_LOG_DEBUG, "---> ssl_cipher: %s\n", ps->ssl_cipher);
+        }
+
+        if(ps->ssl_session) {
+            (*env)->SetObjectArrayElement(env, 
+                                          envbuf, 
+                                          14, 
+                                          (*env)->NewStringUTF(env, ps->ssl_session));
+            jk_log(pl, JK_LOG_DEBUG, "---> ssl_session: %s\n", ps->ssl_session);
+        }
+    }
+
+    jk_log(pl, JK_LOG_DEBUG, 
+           "Done JNIConnectionHandler::readEnvironment\n");
+
+
+    return JK_TRUE;
+}
+
+/*
+ * Class:     org_apache_tomcat_modules_server_JNIConnectionHandler
+ * Method:    readHeaders
+ * Signature: (JJ[Ljava/lang/String;[Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL 
+Java_org_apache_tomcat_modules_server_JNIConnectionHandler_readHeaders
+  (JNIEnv *env, jobject o, jlong s, jlong l, jobjectArray hnames, jobjectArray hvalues)
+{
+    jk_ws_service_t *ps = (jk_ws_service_t *)(int)s;
+    jk_logger_t *pl = (jk_logger_t *)(int)l;
+    unsigned i;
+
+    jk_log(pl, JK_LOG_DEBUG, "Into JNIConnectionHandler::readHeaders\n");
+
+    if(!ps) {
+        jk_log(pl, JK_LOG_ERROR, 
+           "In JNIConnectionHandler::readHeaders, NULL ws service object\n");
+	   return JK_FALSE;
+    }
+
+    jk_log(pl, JK_LOG_DEBUG, "In JNIConnectionHandler::readHeaders, %d headers follow --->\n",
+	   ps->num_headers);
+
+    for(i = 0 ; i < ps->num_headers ; i++) {
+        (*env)->SetObjectArrayElement(env, 
+                                      hnames, 
+                                      i, 
+                                      (*env)->NewStringUTF(env, ps->headers_names[i]));
+        (*env)->SetObjectArrayElement(env, 
+                                      hvalues, 
+                                      i, 
+                                      (*env)->NewStringUTF(env, ps->headers_values[i]));
+	jk_log(pl, JK_LOG_DEBUG, "---> %s = %s\n",
+	       ps->headers_names[i], ps->headers_values[i]);
+    }
+    jk_log(pl, JK_LOG_DEBUG, 
+	   "Done JNIConnectionHandler::readHeaders\n");
+
+    return JK_TRUE;
+}
+
+/*
+ * Class:     org_apache_tomcat_modules_server_JNIConnectionHandler
+ * Method:    startReasponse
+ * Signature: (JJILjava/lang/String;[Ljava/lang/String;[Ljava/lang/String;I)I
+ */
+JNIEXPORT jint JNICALL 
+Java_org_apache_tomcat_modules_server_JNIConnectionHandler_startReasponse
+  (JNIEnv *env, jobject o, jlong s, jlong l, 
+   jint sc, jstring msg, jobjectArray hnames, jobjectArray hvalues, jint hcnt)
+{
+    jk_ws_service_t *ps = (jk_ws_service_t *)(int)s;
+    jk_logger_t *pl = (jk_logger_t *)(int)l;
+    const char *nmsg = NULL;
+    char **nhnames = NULL;
+    char **nhvalues = NULL;
+    jstring *shnames = NULL;
+    jstring *shvalues = NULL;
+    int i = 0;
+    int ok = JK_TRUE;
+
+    jk_log(pl, JK_LOG_DEBUG, "Into JNIConnectionHandler::startReasponse\n");
+
+    if(!ps) {
+        jk_log(pl, JK_LOG_ERROR, 
+	       "In JNIConnectionHandler::startReasponse, NULL ws service object\n");
+	return JK_FALSE;
+    }
+
+    jk_log(pl, JK_LOG_DEBUG, "In JNIConnectionHandler::startReasponse, %d headers follow --->\n",
+	   hcnt);
+
+    if(hcnt) {
+        ok = JK_FALSE;
+
+        nhnames = (char **)jk_pool_alloc(ps->pool, hcnt * sizeof(char *));
+        nhvalues = (char **)jk_pool_alloc(ps->pool, hcnt * sizeof(char *));            
+        shnames = (jstring *)jk_pool_alloc(ps->pool, hcnt * sizeof(jstring));
+        shvalues = (jstring *)jk_pool_alloc(ps->pool, hcnt * sizeof(jstring));
+
+        if(nhvalues && nhnames && shnames && shnames) {	    
+            for( ; i < hcnt ; i++) {
+                jboolean iscommit;
+
+                shvalues[i] = shnames[i] = NULL;
+                nhnames[i] = nhvalues[i] = NULL;
+
+                shnames[i] = (*env)->GetObjectArrayElement(env, hnames, i);
+                shvalues[i] = (*env)->GetObjectArrayElement(env, hvalues, i);
+
+                if(!shvalues[i] || !shnames[i]) {
+                    jk_log(pl, JK_LOG_ERROR, 
+                           "In JNIConnectionHandler::startReasponse, GetObjectArrayElement error\n");
+                    break;
+                }
+
+                nhnames[i] = (char *)(*env)->GetStringUTFChars(env, shnames[i], &iscommit);
+                nhvalues[i] = (char *)(*env)->GetStringUTFChars(env, shvalues[i], &iscommit);
+
+                if(!nhvalues[i] || !nhnames[i]) {
+                    jk_log(pl, JK_LOG_ERROR, 
+                           "In JNIConnectionHandler::startReasponse, GetStringUTFChars error\n");
+                    break;
+                }
+		
+		jk_log(pl, JK_LOG_DEBUG, "---> %s=%s\n", nhnames[i], nhvalues[i]);	
+            }
+            if(i == hcnt) {
+                ok = JK_TRUE;
+		jk_log(pl, JK_LOG_DEBUG, 
+                       "In JNIConnectionHandler::startReasponse. ----- End headers.\n", hcnt);
+            }
+	} else {
+	    jk_log(pl, JK_LOG_ERROR,
+	           "In JNIConnectionHandler::startReasponse, memory allocation error\n");
+	}
+    }			        
+
+    if(msg && ok) {
+        jboolean iscommit;
+        nmsg = (*env)->GetStringUTFChars(env, msg, &iscommit);
+        if(!nmsg) {
+            ok = JK_FALSE;
+        }
+    }
+
+    if(ok) {
+        if(!ps->start_response(ps, sc, nmsg, (const char**)nhnames, (const char**)nhvalues, hcnt)) {
+            ok = JK_FALSE;
+            jk_log(pl, JK_LOG_ERROR, 
+                   "In JNIConnectionHandler::startReasponse, servers startReasponse failed\n");
+        }
+    }
+
+    if(nmsg) {
+        (*env)->ReleaseStringUTFChars(env, msg, nmsg);
+    }
+        
+    if(i < hcnt) {
+        i++;
+    }
+
+    if(nhvalues) {
+        int j;
+
+        for(j = 0 ; j < i ; j++) {
+            if(nhvalues[j]) {
+                (*env)->ReleaseStringUTFChars(env, shvalues[j], nhvalues[j]);
+            }
+        }
+    }
+
+    if(nhnames) {
+        int j;
+
+        for(j = 0 ; j < i ; j++) {
+            if(nhnames[j]) {
+                (*env)->ReleaseStringUTFChars(env, shnames[j], nhnames[j]);
+            }
+        }
+    }
+    jk_log(pl, JK_LOG_DEBUG, "Done JNIConnectionHandler::startReasponse.\n");
+
+    return ok;
+}
+
+/*
+ * Class:     org_apache_tomcat_modules_server_JNIConnectionHandler
+ * Method:    write
+ * Signature: (JJ[BII)I
+ */
+JNIEXPORT jint JNICALL 
+Java_org_apache_tomcat_modules_server_JNIConnectionHandler_write
+  (JNIEnv *env, jobject o, jlong s, jlong l, jbyteArray buf, jint from, jint cnt)
+{
+    jk_ws_service_t *ps = (jk_ws_service_t *)(int)s;
+    jk_logger_t *pl = (jk_logger_t *)(int)l;
+    jint rc = JK_FALSE;   
+    jboolean iscommit;
+    jbyte *nbuf;
+    unsigned nfrom = (unsigned)from;
+    unsigned ncnt = (unsigned)cnt;
+
+    jk_log(pl, JK_LOG_DEBUG, 
+           "In JNIConnectionHandler::write\n");
+
+    if(!ps) {
+    	jk_log(pl, JK_LOG_ERROR, 
+	    "In JNIConnectionHandler::write, NULL ws service object\n");
+	return JK_FALSE;
+    }
+    
+    nbuf = (*env)->GetByteArrayElements(env, buf, &iscommit);
+
+    if(!nbuf) {
+        jk_log(pl, JK_LOG_ERROR, 
+               "In JNIConnectionHandler::write, GetByteArrayElements error\n");
+	return JK_FALSE;
+    }
+
+    if(!ps->write(ps, nbuf + nfrom, ncnt)) {
+        jk_log(pl, JK_LOG_ERROR, 
+                   "In JNIConnectionHandler::write, failed to write to the web server\n");
+    } else {
+        rc = (jint)JK_TRUE;
+    }
+
+    (*env)->ReleaseByteArrayElements(env, buf, nbuf, JNI_ABORT);
+
+    return rc; 
+}
diff --git a/connectors/jk/native/jni/jk_jnicb.exp b/connectors/jk/native/jni/jk_jnicb.exp
new file mode 100644
index 0000000..fd70978
--- /dev/null
+++ b/connectors/jk/native/jni/jk_jnicb.exp
@@ -0,0 +1,7 @@
+Java_org_apache_tomcat_modules_server_JNIConnectionHandler_getNumberOfHeaders,
+Java_org_apache_tomcat_modules_server_JNIConnectionHandler_read,
+Java_org_apache_tomcat_modules_server_JNIConnectionHandler_readEnvironment,
+Java_org_apache_tomcat_modules_server_JNIConnectionHandler_readHeaders,
+Java_org_apache_tomcat_modules_server_JNIConnectionHandler_startReasponse,
+Java_org_apache_tomcat_modules_server_JNIConnectionHandler_write
+
diff --git a/connectors/jk/native/jni/jk_jnicb.h b/connectors/jk/native/jni/jk_jnicb.h
new file mode 100644
index 0000000..2b2061d
--- /dev/null
+++ b/connectors/jk/native/jni/jk_jnicb.h
@@ -0,0 +1,61 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_apache_tomcat_modules_server_JNIConnectionHandler */
+
+#ifndef _Included_org_apache_tomcat_modules_server_JNIConnectionHandler
+#define _Included_org_apache_tomcat_modules_server_JNIConnectionHandler
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     org_apache_tomcat_modules_server_JNIConnectionHandler
+ * Method:    getNumberOfHeaders
+ * Signature: (JJ)I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_tomcat_modules_server_JNIConnectionHandler_getNumberOfHeaders
+  (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class:     org_apache_tomcat_modules_server_JNIConnectionHandler
+ * Method:    read
+ * Signature: (JJ[BII)I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_tomcat_modules_server_JNIConnectionHandler_read
+  (JNIEnv *, jobject, jlong, jlong, jbyteArray, jint, jint);
+
+/*
+ * Class:     org_apache_tomcat_modules_server_JNIConnectionHandler
+ * Method:    readEnvironment
+ * Signature: (JJ[Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_tomcat_modules_server_JNIConnectionHandler_readEnvironment
+  (JNIEnv *, jobject, jlong, jlong, jobjectArray);
+
+/*
+ * Class:     org_apache_tomcat_modules_server_JNIConnectionHandler
+ * Method:    readHeaders
+ * Signature: (JJ[Ljava/lang/String;[Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_tomcat_modules_server_JNIConnectionHandler_readHeaders
+  (JNIEnv *, jobject, jlong, jlong, jobjectArray, jobjectArray);
+
+/*
+ * Class:     org_apache_tomcat_modules_server_JNIConnectionHandler
+ * Method:    startReasponse
+ * Signature: (JJILjava/lang/String;[Ljava/lang/String;[Ljava/lang/String;I)I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_tomcat_modules_server_JNIConnectionHandler_startReasponse
+  (JNIEnv *, jobject, jlong, jlong, jint, jstring, jobjectArray, jobjectArray, jint);
+
+/*
+ * Class:     org_apache_tomcat_modules_server_JNIConnectionHandler
+ * Method:    write
+ * Signature: (JJ[BII)I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_tomcat_modules_server_JNIConnectionHandler_write
+  (JNIEnv *, jobject, jlong, jlong, jbyteArray, jint, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/connectors/jk/native/jni/jni_connect.dsp b/connectors/jk/native/jni/jni_connect.dsp
new file mode 100644
index 0000000..84f4fd0
--- /dev/null
+++ b/connectors/jk/native/jni/jni_connect.dsp
@@ -0,0 +1,151 @@
+# Microsoft Developer Studio Project File - Name="jni_connect" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=jni_connect - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "jni_connect.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "jni_connect.mak" CFG="jni_connect - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "jni_connect - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "jni_connect - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "jni_connect - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "release"
+# PROP Intermediate_Dir "release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JNI_CONNECT_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "..\common" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JNI_CONNECT_EXPORTS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+
+!ELSEIF  "$(CFG)" == "jni_connect - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "debug"
+# PROP Intermediate_Dir "debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JNI_CONNECT_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "..\common" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JNI_CONNECT_EXPORTS" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "jni_connect - Win32 Release"
+# Name "jni_connect - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\jk_jnicb.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_util.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\common\jk_ajp12_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_global.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_jnicb.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_service.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_util.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/connectors/jk/native/netscape/Makefile.netware b/connectors/jk/native/netscape/Makefile.netware
new file mode 100644
index 0000000..22aadf4
--- /dev/null
+++ b/connectors/jk/native/netscape/Makefile.netware
@@ -0,0 +1,249 @@
+#
+# Makefile for jk_nsapi_plugin (NetWare version - gnu make)
+# created by Guenter Knauf <eflash@gmx.net>
+#
+
+# Edit the path below to point to the base of your Netscape includes.
+ifndef NS_HOME
+NS_HOME	= c:/projects/sdks/netscape
+endif
+# Edit the path below to point to the base of your NetWare Java SDK.
+ifndef NW_JDK
+NW_JDK	= c:/projects/sdks/java-nw
+endif
+# Edit the path below to point to the base of your Novell NDK.
+ifndef NDKBASE
+NDKBASE	= c:/novell
+endif
+INSTDIR = /mnt/sys/novonyx/modules
+
+# Edit the vars below to change NLM target settings.
+TARGET  = nsapi_rd
+VERSION	= $(JK_VERSION)
+COPYR	= Copyright (c) 2000-2004 The Apache Software Foundation. All rights reserved.
+DESCR	= Netscape plugin for Jakarta/Tomcat $(JK_VERSION_STR)
+MTSAFE	= NO
+STACK	= 64000
+#SCREEN	= System Console
+MODULES	= nshttpd
+EXPORTS	= jk_init jk_service
+IMPORTS	= __nsapi30_table
+
+# Edit the var below to point to your lib architecture.
+ifndef LIBARCH
+LIBARCH = CLIB
+# LIBARCH = LIBC
+endif
+
+# must be equal to DEBUG or NDEBUG
+DB	= NDEBUG
+# DB	= DEBUG
+# Optimization: -O<n> or debugging: -g
+ifeq ($(DB),NDEBUG)
+	OPT	= -O2
+	OBJDIR	= release
+else
+	OPT	= -g
+	OBJDIR	= debug
+endif
+
+# Include the version info retrieved from jk_version.h
+-include $(OBJDIR)/version.inc
+
+# The following line defines your compiler.
+ifdef METROWERKS
+	CC = mwccnlm
+else
+	CC = gcc
+endif
+# RM	= rm -f
+# CP	= cp -fv
+# if you want to mark the target as MTSAFE you will need a tool for
+# generating the xdc data for the linker; here's a minimal tool:
+# http://www.gknw.de/development/prgtools/mkxdc.zip
+MPKXDC	= mkxdc
+
+# Global flags for all compilers
+CFLAGS	= $(OPT) -D$(DB) -DNETWARE -DXP_NETWARE -nostdinc
+
+ifeq ($(CC),mwccnlm)
+LD	= mwldnlm
+LDFLAGS	= -nostdlib $(PRELUDE) $(OBJDIR)/*.o -o $(OBJDIR)/$(TARGET).nlm -commandfile
+CFLAGS	+= -gccinc -inline off -opt nointrinsics
+#CFLAGS	+= -w on
+ifeq ($(LIBARCH),LIBC)
+	PRELUDE = $(SDK_LIBC)/imports/libcpre.o
+	CFLAGS += -align 4 -inst mmx -proc 686
+#	CFLAGS += -D__ANSIC__
+else
+	PRELUDE = "$(METROWERKS)/Novell Support/libraries/runtime/prelude.obj"
+#	CFLAGS += -include "$(METROWERKS)/Novell Support/headers/nlm_prefix.h"
+	CFLAGS += -align 1 -proc 586
+endif
+else
+LD	= nlmconv
+LDFLAGS	= -T
+CFLAGS	+= -fno-builtin -fpack-struct -fpcc-struct-return
+CFLAGS	+= -Wall -Wno-main # -pedantic
+ifeq ($(LIBARCH),LIBC)
+	PRELUDE = $(SDK_LIBC)/imports/libcpre.gcc.o
+#	CFLAGS += -D__ANSIC__
+else
+	PRELUDE = $(SDK_CLIB)/imports/clibpre.gcc.o
+	CFLAGS += -include $(NDKBASE)/nlmconv/genlm.h
+endif
+endif
+
+LDLIBS	=
+
+NDK_ROOT = $(NDKBASE)/ndk
+SDK_CLIB = $(NDK_ROOT)/nwsdk
+SDK_LIBC = $(NDK_ROOT)/libc
+JKCOMMON = ../common
+
+INCLUDES = -I$(NS_HOME)/include -I$(NS_HOME)/include/base
+INCLUDES += -I$(JKCOMMON) -I$(NW_JDK)/include -I$(NW_JDK)/include/netware 
+
+ifeq ($(LIBARCH),LIBC)
+	INCLUDES += -I$(SDK_LIBC)/include -I$(SDK_LIBC)/include/nks
+	INCLUDES += -I$(SDK_LIBC)/include/winsock
+else
+	INCLUDES += -I$(SDK_CLIB)/include/nlm -I$(SDK_CLIB)/include
+	INCLUDES += -I$(SDK_CLIB)/include/nlm/obsolete
+	CFLAGS += -DNETDB_USE_INTERNET
+endif
+CFLAGS	+= $(INCLUDES)
+
+ifeq ($(MTSAFE),YES)
+	XDCDATA = $(OBJDIR)/$(TARGET).xdc
+endif
+
+ifeq ($(findstring linux,$(OSTYPE)),linux)
+DL	= '
+#-include $(NDKBASE)/nlmconv/ncpfs.inc
+endif
+
+OBJS	= \
+	$(OBJDIR)/jk_nwmain.o \
+	$(OBJDIR)/jk_ajp12_worker.o \
+	$(OBJDIR)/jk_ajp13.o \
+	$(OBJDIR)/jk_ajp13_worker.o \
+	$(OBJDIR)/jk_ajp14.o \
+	$(OBJDIR)/jk_ajp14_worker.o \
+	$(OBJDIR)/jk_ajp_common.o \
+	$(OBJDIR)/jk_connect.o \
+	$(OBJDIR)/jk_context.o \
+	$(OBJDIR)/jk_jni_worker.o \
+	$(OBJDIR)/jk_lb_worker.o \
+	$(OBJDIR)/jk_map.o \
+	$(OBJDIR)/jk_md5.o \
+	$(OBJDIR)/jk_msg_buff.o \
+	$(OBJDIR)/jk_pool.o \
+	$(OBJDIR)/jk_sockbuf.o \
+	$(OBJDIR)/jk_uri_worker_map.o \
+	$(OBJDIR)/jk_util.o \
+	$(OBJDIR)/jk_worker.o \
+	$(OBJDIR)/jk_nsapi_plugin.o
+
+
+all: $(OBJDIR) $(OBJDIR)/version.inc $(OBJDIR)/$(TARGET).nlm 
+
+$(OBJDIR)/%.o: %.c
+	@echo Compiling $<
+	@$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJDIR)/%.o: $(JKCOMMON)/%.c
+	@echo Compiling $<
+	@$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJDIR)/version.inc: $(JKCOMMON)/jk_version.h $(OBJDIR)
+	@echo Creating $@
+	@awk -f ../../../common/build/get_ver.awk $< > $@
+
+dist: all
+	-$(RM) $(OBJDIR)/*.o $(OBJDIR)/$(TARGET).map $(OBJDIR)/$(TARGET).ncv
+	-$(RM) $(OBJDIR)/$(TARGET).def $(OBJDIR)/version.inc $(XDCDATA)
+#	-$(CP) ../changes.txt $(OBJDIR)/
+
+install: all
+	@[ -d $(INSTDIR) ] || mkdir $(INSTDIR)
+	@$(CP) $(TARGET).nlm $(INSTDIR)
+
+clean:
+	-$(RM) -r $(OBJDIR)
+
+$(OBJDIR):
+	@mkdir $(OBJDIR)
+
+$(OBJDIR)/$(TARGET).nlm: $(OBJS) $(OBJDIR)/$(TARGET).def $(XDCDATA)
+	@echo Linking $@
+	@-$(RM) $@
+	@$(LD) $(LDFLAGS) $(OBJDIR)/$(TARGET).def
+
+$(OBJDIR)/%.xdc: Makefile.netware
+	@echo Creating $@
+	@$(MPKXDC) $(XDCOPT) $@
+
+$(OBJDIR)/%.def: Makefile.netware
+	@echo $(DL)# DEF file for linking with $(LD)$(DL) > $@
+	@echo $(DL)# Do not edit this file - it is created by make!$(DL) >> $@
+	@echo $(DL)# All your changes will be lost!!$(DL) >> $@
+	@echo $(DL)#$(DL) >> $@
+	@echo $(DL)copyright "$(COPYR)"$(DL) >> $@
+	@echo $(DL)description "$(DESCR)"$(DL) >> $@
+	@echo $(DL)version $(VERSION)$(DL) >> $@
+ifdef NLMTYPE
+	@echo $(DL)type $(NLMTYPE)$(DL) >> $@
+endif
+ifdef STACK
+	@echo $(DL)stack $(STACK)$(DL) >> $@
+endif
+ifdef SCREEN
+	@echo $(DL)screenname "$(SCREEN)"$(DL) >> $@
+else
+	@echo $(DL)screenname "DEFAULT"$(DL) >> $@
+endif
+ifeq ($(DB),DEBUG)
+	@echo $(DL)debug$(DL) >> $@
+endif
+	@echo $(DL)threadname "$(TARGET)"$(DL) >> $@
+ifdef XDCDATA
+	@echo $(DL)xdcdata $(XDCDATA)$(DL) >> $@
+endif
+ifeq ($(LIBARCH),CLIB)
+	@echo $(DL)start _Prelude$(DL) >> $@
+	@echo $(DL)exit _Stop$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/clib.imp$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/threads.imp$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/nlmlib.imp$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/socklib.imp$(DL) >> $@
+	@echo $(DL)module clib$(DL) >> $@
+else
+ifeq ($(LD),nlmconv)
+	@echo $(DL)flag_on 64$(DL) >> $@
+else
+	@echo $(DL)autounload$(DL) >> $@
+endif
+	@echo $(DL)pseudopreemption$(DL) >> $@
+	@echo $(DL)start _LibCPrelude$(DL) >> $@
+	@echo $(DL)exit _LibCPostlude$(DL) >> $@
+	@echo $(DL)check _LibCCheckUnload$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/libc/imports/libc.imp$(DL) >> $@
+	@echo $(DL)import @$(NDK_ROOT)/libc/imports/netware.imp$(DL) >> $@
+	@echo $(DL)module libc$(DL) >> $@
+endif
+ifdef MODULES
+	@echo $(DL)module $(MODULES)$(DL) >> $@
+endif
+ifdef EXPORTS
+	@echo $(DL)export $(EXPORTS)$(DL) >> $@
+endif
+ifdef IMPORTS
+	@echo $(DL)import $(IMPORTS)$(DL) >> $@
+endif
+ifeq ($(LD),nlmconv)
+	@echo $(DL)input $(OBJS)$(DL) >> $@
+	@echo $(DL)input $(PRELUDE)$(DL) >> $@
+	@echo $(DL)output $(TARGET).nlm$(DL) >> $@
+endif
+
diff --git a/connectors/jk/native/netscape/Makefile.solaris b/connectors/jk/native/netscape/Makefile.solaris
new file mode 100644
index 0000000..96a82ca
--- /dev/null
+++ b/connectors/jk/native/netscape/Makefile.solaris
@@ -0,0 +1,36 @@
+# Defines for example NSAPI programs running under SOLARIS
+
+CC_CMD=gcc -DNET_SSL -DSOLARIS -D_REENTRANT -DXP_UNIX \
+	-DMCC_HTTPD -DSPAPI20 \
+	-fPIC
+LD_SHAREDCMD=ld -G -fPIC
+
+all:
+
+prepare:
+
+OS_TYPE=solaris
+INCLUDEDIR=$(SUITSPOT_HOME)/include
+JAVA_INCLUDE=$(JAVA_HOME)/include
+JK_DIR=../common
+VPATH=.:$(JK_DIR)
+
+JK_SRCS = $(shell ls $(JK_DIR)/*.c)
+JK_OBJS = $(patsubst $(JK_DIR)/%.c,%.o,$(JK_SRCS))
+
+PLUGIN_OBJ = jk_nsapi_plugin.o
+
+INCLUDE_FLAGS=	-I$(JK_DIR) -I$(INCLUDEDIR) -I$(INCLUDEDIR)/base \
+		-I$(INCLUDEDIR)/frame -I$(JAVA_INCLUDE) -I$(JAVA_INCLUDE)/$(OS_TYPE)
+
+all: nsapi_redirector.so 
+
+
+nsapi_redirector.so: $(JK_OBJS) $(PLUGIN_OBJ)
+	$(LD_SHAREDCMD) $(JK_OBJS) $(PLUGIN_OBJ) -o nsapi_redirector.so $(EXTRA_LDDEFINES)
+
+clean:
+	rm -f *.o nsapi_redirector.so 
+
+%.o : %.c
+	$(CC_CMD) $(INCLUDE_FLAGS) -c $< 
diff --git a/connectors/jk/native/netscape/README b/connectors/jk/native/netscape/README
new file mode 100644
index 0000000..57ed628
--- /dev/null
+++ b/connectors/jk/native/netscape/README
@@ -0,0 +1,34 @@
+ABOUT
+-----
+
+The redirector was originally developed using Visual C++ Ver.6.0, 
+so having this environment is a prerequisite if you want to perform 
+a custom build on Windows systems
+
+On Unix system, a Makefile.solaris is provided and should be 
+adapted to tailor to your own configuration.
+
+
+REQUIREMENT for Windows build
+-----------------------------
+
+MS VC 6.0 (+ update, latest service pack is 5)
+
+BUILDING on Windows
+-------------------
+ 
+The steps that you need to take are:
+
+   1. Change directory to the nsapi redirector plugins source directory.
+
+   2. Execute the following command:
+      MSDEV nsapi.dsp /MAKE ALL
+      If msdev is not in your path, enter the full path to msdev.exe
+
+This will build both release and debug versions of the redirector plugin.
+
+An alternative will be to open the isapi workspace file (nsapi.dsw) in msdev and 
+build it using the build menu.
+
+
+
diff --git a/connectors/jk/native/netscape/jk_nsapi_plugin.c b/connectors/jk/native/netscape/jk_nsapi_plugin.c
new file mode 100644
index 0000000..3ffa653
--- /dev/null
+++ b/connectors/jk/native/netscape/jk_nsapi_plugin.c
@@ -0,0 +1,507 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: NSAPI plugin for Netscape servers                          *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+
+#include "jk_global.h"
+#include "jk_util.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+
+#include "nsapi.h"
+
+#define URI_PATTERN "path"
+#define DEFAULT_WORKER_NAME ("ajp13")
+
+struct nsapi_private_data {
+    jk_pool_t p;
+    
+    int request_started;
+
+    pblock *pb;
+    Session *sn;
+    Request *rq;
+};
+typedef struct nsapi_private_data nsapi_private_data_t;
+
+static int init_on_other_thread_is_done = JK_FALSE;
+static int init_on_other_thread_is_ok = JK_FALSE;
+
+static jk_logger_t *logger = NULL;
+static jk_worker_env_t	 worker_env;
+
+#ifdef NETWARE
+int (*PR_IsSocketSecure)(SYS_NETFD *csd); /* pointer to PR_IsSocketSecure function */
+#endif
+
+static int JK_METHOD start_response(jk_ws_service_t *s,
+                                    int status,
+                                    const char *reason,
+                                    const char * const *header_names,
+                                    const char * const *header_values,
+                                    unsigned num_of_headers);
+
+static int JK_METHOD ws_read(jk_ws_service_t *s,
+                             void *b,
+                             unsigned l,
+                             unsigned *a);
+
+static int JK_METHOD ws_write(jk_ws_service_t *s,
+                              const void *b,
+                              unsigned l);
+
+NSAPI_PUBLIC int jk_init(pblock *pb, 
+                         Session *sn, 
+                         Request *rq);
+
+NSAPI_PUBLIC void jk_term(void *p);
+
+NSAPI_PUBLIC int jk_service(pblock *pb,
+                            Session *sn,
+                            Request *rq);
+
+static int init_ws_service(nsapi_private_data_t *private_data,
+                           jk_ws_service_t *s);
+
+static int setup_http_headers(nsapi_private_data_t *private_data,
+                              jk_ws_service_t *s);
+
+static void init_workers_on_other_threads(void *init_d) 
+{
+    jk_map_t *init_map = (jk_map_t *)init_d;
+    /* we add the URI->WORKER MAP since workers using AJP14 will feed it */
+	/* but where are they here in Netscape ? */
+    if(wc_open(init_map, &worker_env, logger)) {
+        init_on_other_thread_is_ok = JK_TRUE;
+    } else {
+        jk_log(logger, JK_LOG_EMERG, "In init_workers_on_other_threads, failed\n");
+    }
+
+    init_on_other_thread_is_done = JK_TRUE;
+}
+
+static int JK_METHOD start_response(jk_ws_service_t *s,
+                                    int status,
+                                    const char *reason,
+                                    const char * const *header_names,
+                                    const char * const *header_values,
+                                    unsigned num_of_headers)
+{
+    if(s && s->ws_private) {
+        nsapi_private_data_t *p = s->ws_private;
+        if(!p->request_started) {
+            unsigned i;
+
+            p->request_started = JK_TRUE;
+
+            /* Remove "old" content type */
+            param_free(pblock_remove("content-type", p->rq->srvhdrs));
+
+            if(num_of_headers) {
+                for (i = 0 ; i < (int)num_of_headers ; i++) {
+                    pblock_nvinsert(header_names[i],
+                                    header_values[i],
+                                    p->rq->srvhdrs);    
+                }
+            } else {
+                pblock_nvinsert("content-type",
+                                "text/plain",
+                                p->rq->srvhdrs);    
+            }
+
+            protocol_status(p->sn,
+                            p->rq,
+                            status,
+                            (char *)reason);
+
+            protocol_start_response(p->sn, p->rq);
+        }
+        return JK_TRUE;
+
+    }
+    return JK_FALSE;
+}
+
+static int JK_METHOD ws_read(jk_ws_service_t *s,
+                             void *b,
+                             unsigned l,
+                             unsigned *a)
+{
+    if(s && s->ws_private && b && a) {
+        nsapi_private_data_t *p = s->ws_private;
+        
+        *a = 0;
+        if(l) {
+            char *buf = b;
+            unsigned i;
+            netbuf *inbuf = p->sn->inbuf;
+
+/* Until we get a service pack for NW5.1 and earlier that has the latest */
+/* Enterprise Server, we have to go through the else version of this code*/
+#if defined(netbuf_getbytes) && !defined(NETWARE) 
+            i = netbuf_getbytes(inbuf, b, l);
+            if(NETBUF_EOF == i || NETBUF_ERROR == i) {
+                return JK_FALSE;
+            }
+
+#else 
+            int ch;
+            for(i = 0 ; i < l ; i++) {
+                ch = netbuf_getc(inbuf);           
+                /*
+                 * IO_EOF is 0 (zero) which is a very reasonable byte
+                 * when it comes to binary data. So we are not breaking 
+                 * out of the read loop when reading it.
+                 *
+                 * We are protected from an infinit loop by the Java part of
+                 * Tomcat.
+                 */
+                if(IO_ERROR == ch) {
+                    break;
+                }
+
+                buf[i] = ch;
+            }
+
+            if(0 == i) {
+                return JK_FALSE;
+            }
+#endif
+            *a = i;
+
+        }
+        return JK_TRUE;
+    }
+    return JK_FALSE;
+}
+
+static int JK_METHOD ws_write(jk_ws_service_t *s,
+                              const void *b,
+                              unsigned l)
+{
+    if(s && s->ws_private && b) {
+        nsapi_private_data_t *p = s->ws_private;
+
+        if(l) {
+            if(!p->request_started) {
+                start_response(s, 200, NULL, NULL, NULL, 0);
+            }
+
+            if(net_write(p->sn->csd, (char *)b, (int)l) == IO_ERROR) {
+                return JK_FALSE;
+            }
+        }
+
+        return JK_TRUE;
+
+    }
+    return JK_FALSE;
+}
+
+NSAPI_PUBLIC int jk_init(pblock *pb, 
+                         Session *sn, 
+                         Request *rq)
+{
+    char *worker_prp_file = pblock_findval(JK_WORKER_FILE_TAG, pb); 
+    char *log_level_str = pblock_findval(JK_LOG_LEVEL_TAG, pb); 
+    char *log_file = pblock_findval(JK_LOG_FILE_TAG, pb); 
+
+    int rc = REQ_ABORTED;
+    jk_map_t *init_map;
+
+    fprintf(stderr, "In jk_init.\n   Worker file = %s.\n   Log level = %s.\n   Log File = %s\n",worker_prp_file, log_level_str,  log_file);
+    if(!worker_prp_file) {
+        worker_prp_file = JK_WORKER_FILE_DEF;
+    }
+
+    if(!log_level_str) {
+        log_level_str = JK_LOG_LEVEL_DEF;
+    }    
+    
+    if(!jk_open_file_logger(&logger, log_file, 
+                            jk_parse_log_level(log_level_str))) {
+        logger = NULL;
+    }
+
+    if(map_alloc(&init_map)) {
+        if(map_read_properties(init_map, worker_prp_file)) {
+            int sleep_cnt;
+            SYS_THREAD s;
+            
+            s = systhread_start(SYSTHREAD_DEFAULT_PRIORITY, 
+                                0, 
+                                init_workers_on_other_threads, 
+                                init_map);
+            for(sleep_cnt = 0 ; sleep_cnt < 60 ; sleep_cnt++) {
+                systhread_sleep(1000);
+                jk_log(logger, JK_LOG_DEBUG, "jk_init, a second passed\n");
+                if(init_on_other_thread_is_done) {
+                    break;
+                }
+            }
+
+            if(init_on_other_thread_is_done &&
+               init_on_other_thread_is_ok) {
+                magnus_atrestart(jk_term, NULL);
+                rc = REQ_PROCEED;
+            }
+
+/*            if(wc_open(init_map, NULL, logger)) {
+                magnus_atrestart(jk_term, NULL);
+                rc = REQ_PROCEED;
+            }
+*/
+        }
+        
+        map_free(&init_map);
+    }
+
+#ifdef NETWARE
+    PR_IsSocketSecure = (int (*)(void **))ImportSymbol(GetNLMHandle(), "PR_IsSocketSecure");
+#endif
+    return rc;    
+}
+
+NSAPI_PUBLIC void jk_term(void *p)
+{
+#ifdef NETWARE
+    if (NULL != PR_IsSocketSecure)
+    {
+       UnimportSymbol(GetNLMHandle(), "PR_IsSocketSecure");
+       PR_IsSocketSecure = NULL;
+    }
+#endif
+    wc_close(logger);
+    if(logger) {
+        jk_close_file_logger(&logger);
+    }
+}
+
+NSAPI_PUBLIC int jk_service(pblock *pb,
+                            Session *sn,
+                            Request *rq)
+{
+    char *worker_name = pblock_findval(JK_WORKER_NAME_TAG, pb); 
+    char *uri_pattern = pblock_findval(URI_PATTERN, pb);     
+    jk_worker_t *worker;    
+    int rc = REQ_ABORTED;
+
+    if(uri_pattern) {
+        char *uri = pblock_findval("uri", rq->reqpb);
+
+        if(0 != shexp_match(uri, uri_pattern)) {
+            return REQ_NOACTION;
+        }        
+    }
+
+    if(!worker_name) {
+        worker_name = DEFAULT_WORKER_NAME;
+    }
+
+    worker = wc_get_worker_for_name(worker_name, logger);
+    if(worker) {
+        nsapi_private_data_t private_data;
+        jk_ws_service_t s;
+        jk_pool_atom_t buf[SMALL_POOL_SIZE];
+
+        jk_open_pool(&private_data.p, buf, sizeof(buf));
+
+        private_data.request_started = JK_FALSE;
+        private_data.pb = pb;
+        private_data.sn = sn;
+        private_data.rq = rq;
+
+        jk_init_ws_service(&s);
+
+        s.ws_private = &private_data;
+        s.pool = &private_data.p;
+
+        if(init_ws_service(&private_data, &s)) {
+            jk_endpoint_t *e = NULL;
+            if(worker->get_endpoint(worker, &e, logger)) {                
+                int recover = JK_FALSE;
+                if(e->service(e, &s, logger, &recover)) {
+                    rc = REQ_PROCEED;
+                }
+                e->done(&e, logger);
+            }
+        }
+        jk_close_pool(&private_data.p);
+    }
+
+    return rc;
+}
+
+static int init_ws_service(nsapi_private_data_t *private_data,
+                           jk_ws_service_t *s) 
+{
+    char *tmp;
+    int rc;
+
+    s->jvm_route = NULL;
+    s->start_response = start_response;
+    s->read = ws_read;
+    s->write = ws_write;
+    
+    /* Clear RECO status */
+    s->reco_status  = RECO_NONE;
+
+    s->auth_type        = pblock_findval("auth-type", private_data->rq->vars);
+    s->remote_user      = pblock_findval("auth-user", private_data->rq->vars);
+
+    s->content_length   = 0;
+    tmp = NULL;
+    rc  = request_header("content-length", 
+                         &tmp, 
+                         private_data->sn, 
+                         private_data->rq);
+
+    if((rc != REQ_ABORTED) && tmp) {
+        s->content_length = atoi(tmp);
+    }
+    
+    s->method           = pblock_findval("method", private_data->rq->reqpb);
+    s->protocol         = pblock_findval("protocol", private_data->rq->reqpb);
+    
+    s->remote_host      = session_dns(private_data->sn);
+    s->remote_addr      = pblock_findval("ip", private_data->sn->client);
+    
+    s->req_uri          = pblock_findval("uri", private_data->rq->reqpb);
+    s->query_string     = pblock_findval("query", private_data->rq->reqpb);
+
+    s->server_name      = server_hostname;
+    
+#ifdef NETWARE
+    /* On NetWare, since we have virtual servers, we have a different way of
+     * getting the port that we need to try first.
+     */
+    tmp = pblock_findval("server_port", private_data->sn->client);
+    if (NULL != tmp)
+        s->server_port = atoi(tmp);
+    else
+#endif
+    s->server_port      = server_portnum;
+    s->server_software  = system_version();
+
+
+    s->headers_names    = NULL;
+    s->headers_values   = NULL;
+    s->num_headers      = 0;
+    
+#ifdef NETWARE
+    /* on NetWare, we can have virtual servers that are secure.  
+     * PR_IsSocketSecure is an api made available with virtual servers to check
+     * if the socket is secure or not
+     */
+    if (NULL != PR_IsSocketSecure)
+        s->is_ssl       = PR_IsSocketSecure(private_data->sn->csd);
+    else
+#endif
+    s->is_ssl           = security_active;
+
+    s->ssl_key_size     = -1; /* required by Servlet 2.3 Api, added in jtc */
+    if(s->is_ssl) {
+        s->ssl_cert     = pblock_findval("auth-cert", private_data->rq->vars);
+        if(s->ssl_cert) {
+            s->ssl_cert_len = strlen(s->ssl_cert);
+        }
+        s->ssl_cipher   = pblock_findval("cipher", private_data->sn->client);
+        s->ssl_session  = pblock_findval("ssl-id", private_data->sn->client);
+    } else {
+        s->ssl_cert     = NULL;
+        s->ssl_cert_len = 0;
+        s->ssl_cipher   = NULL;
+        s->ssl_session  = NULL;
+    }
+
+    return setup_http_headers(private_data, s);
+}
+
+static int setup_http_headers(nsapi_private_data_t *private_data,
+                              jk_ws_service_t *s) 
+{
+    int need_content_length_header = (s->content_length == 0) ? JK_TRUE : JK_FALSE; 
+
+    pblock *headers_jar = private_data->rq->headers;
+    int cnt;
+    int i;
+
+    for(i = 0, cnt = 0 ; i < headers_jar->hsize ; i++) {
+        struct pb_entry *h = headers_jar->ht[i];
+        while(h && h->param) {
+            cnt++;
+            h = h->next;
+        }
+    }
+
+    s->headers_names  = NULL;
+    s->headers_values = NULL;
+    s->num_headers = cnt;
+    if(cnt) {
+        /* allocate an extra header slot in case we need to add a content-length header */
+        s->headers_names  = jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
+        s->headers_values = jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
+
+        if(s->headers_names && s->headers_values) {            
+            for(i = 0, cnt = 0 ; i < headers_jar->hsize ; i++) {
+                struct pb_entry *h = headers_jar->ht[i];
+                while(h && h->param) {
+                    s->headers_names[cnt]  = h->param->name;
+                    s->headers_values[cnt] = h->param->value;
+                    if(need_content_length_header &&
+                            !strncmp(h->param->name,"content-length",14)) {
+                        need_content_length_header = JK_FALSE;
+                    }
+                    cnt++;
+                    h = h->next;
+                }
+            }
+            /* Add a content-length = 0 header if needed. 
+             * Ajp13 assumes an absent content-length header means an unknown, 
+             * but non-zero length body. 
+             */ 
+            if(need_content_length_header) {
+                s->headers_names[cnt] = "content-length"; 
+                s->headers_values[cnt] = "0"; 
+                cnt++;
+            }
+            s->num_headers = cnt;
+            return JK_TRUE;
+        }
+    } else {
+        if (need_content_length_header) {
+            s->headers_names  = jk_pool_alloc(&private_data->p, sizeof(char *));
+            s->headers_values = jk_pool_alloc(&private_data->p, sizeof(char *));
+            if(s->headers_names && s->headers_values) {
+                s->headers_names[0] = "content-length"; 
+                s->headers_values[0] = "0"; 
+                s->num_headers++;
+                return JK_TRUE;
+            }
+        }
+        else
+            return JK_TRUE;
+    }
+
+    return JK_FALSE;
+}
diff --git a/connectors/jk/native/netscape/nsapi.dsp b/connectors/jk/native/netscape/nsapi.dsp
new file mode 100644
index 0000000..3cfef58
--- /dev/null
+++ b/connectors/jk/native/netscape/nsapi.dsp
@@ -0,0 +1,252 @@
+# Microsoft Developer Studio Project File - Name="nsapi" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=nsapi - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "nsapi.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "nsapi.mak" CFG="nsapi - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "nsapi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "nsapi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "nsapi - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "nsapi_release"
+# PROP Intermediate_Dir "nsapi_release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NSAPI_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\common" /I "$(NS_HOME)\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NSAPI_EXPORTS" /D "XP_WIN32" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 wsock32.lib ns-httpd36.lib /nologo /dll /machine:I386 /out:"nsapi_release/nsapi_redirect.dll" /libpath:"$(NS_HOME)\lib"
+
+!ELSEIF  "$(CFG)" == "nsapi - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "nsapi___Win32_Debug"
+# PROP BASE Intermediate_Dir "nsapi___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "nsapi_debug"
+# PROP Intermediate_Dir "nsapi_debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NSAPI_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\common" /I "$(NS_HOME)\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NSAPI_EXPORTS" /D "XP_WIN32" /D "MCC_HTTPD" /D "SPAPI20" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 wsock32.lib ns-httpd36.lib /nologo /dll /debug /machine:I386 /out:"nsapi_debug/nsapi_redirect.dll" /pdbtype:sept /libpath:"$(NS_HOME)\lib"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "nsapi - Win32 Release"
+# Name "nsapi - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp_common.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_connect.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_context.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_jni_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_lb_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_msg_buff.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_nsapi_plugin.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_uri_worker_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_util.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp_common.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_connect.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_context.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_global.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_jni_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_lb_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_msg_buff.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_mt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_service.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_uri_worker_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_util.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker.h
+# End Source File
+# End Group
+
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/connectors/jk/native/nt_service/jk_nt_service.c b/connectors/jk/native/nt_service/jk_nt_service.c
new file mode 100644
index 0000000..0cccf28
--- /dev/null
+++ b/connectors/jk/native/nt_service/jk_nt_service.c
@@ -0,0 +1,1229 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: NT System service for Jakarta/Tomcat                       *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ *              Dave Oxley <Dave@JungleMoss.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#include "jk_global.h"
+#include "jk_util.h"
+#include "jk_ajp13.h"
+#include "jk_connect.h"
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <process.h>
+
+#define AJP12_TAG              ("ajp12")
+#define AJP13_TAG              ("ajp13")
+#define BASE_REGISTRY_LOCATION ("SYSTEM\\CurrentControlSet\\Services\\")
+#define IMAGE_NAME             ("ImagePath")
+#define PARAMS_LOCATION        ("Parameters")
+#define PRP_LOCATION           ("PropertyFile")
+
+// internal variables
+static SERVICE_STATUS          ssStatus;       // current status of the service
+static SERVICE_STATUS_HANDLE   sshStatusHandle;
+static DWORD                   dwErr = 0;
+static char                    szErr[1024] = "";
+static HANDLE                  hServerStopEvent = NULL;
+static int                     shutdown_port;
+static char                    *shutdown_protocol = AJP12_TAG;
+static char                    *shutdown_secret = NULL;
+static char                    *shutdown_cmd=NULL;
+
+typedef enum ActionEnum
+{   acNoAction  = 0,
+    acInstall   = 1,
+    acRemove    = 2,
+    acStartTC   = 3,
+    acStopTC    = 4
+}   ActionEnum;
+
+
+struct jk_tomcat_startup_data {
+    char *cmd_line; /* Start command line */
+    char *stdout_file;
+    char *stderr_file;
+    char *extra_path;
+    char *tomcat_home;
+    char *java_bin;
+
+    char *shutdown_protocol;
+    /* for cmd */
+    char *stop_cmd;
+    /* For ajp13/ajp12/catalina */
+    int  shutdown_port;
+    char *shutdown_secret;
+
+    /* Optional/not needed */
+    char *classpath;
+    char *tomcat_class;
+    char *server_file;
+};
+
+typedef struct jk_tomcat_startup_data jk_tomcat_startup_data_t;
+
+// internal function prototypes
+static void WINAPI service_ctrl(DWORD dwCtrlCode);
+static void WINAPI service_main(DWORD dwArgc, 
+                                char **lpszArgv);
+static void install_service(char *name,
+                            char *dname,
+                            char *user, 
+                            char *password, 
+                            char *deps, 
+                            BOOL bAutomatic, 
+                            char *rel_prp_file);
+static void remove_service(char *name);
+static void start_service(char *name,
+                          char *machine);
+static void stop_service(char *name,
+                         char *machine);
+static char *GetLastErrorText(char *lpszBuf, DWORD dwSize);
+static void AddToMessageLog(char *lpszMsg);
+static BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
+                                DWORD dwWin32ExitCode,
+                                DWORD dwWaitHint);
+static void start_jk_service(char *name);
+static void stop_jk_service(void);
+static int set_registry_values(SC_HANDLE   schService, char *name, 
+                               char *prp_file);
+static int create_registry_key(const char *tag, 
+                               HKEY *key);
+static int set_registry_config_parameter(HKEY hkey, 
+                                         const char *tag, 
+                                         char *value);
+static int get_registry_config_parameter(HKEY hkey, 
+                                         const char *tag,  
+                                         char *b, DWORD sz);
+static int start_tomcat(const char *name, 
+                        HANDLE *hTomcat);
+static void stop_tomcat(char *name,
+                        int port, 
+                        const char *protocol,
+                        char *secret,
+                        HANDLE hTomcat);
+static int read_startup_data(jk_map_t *init_map, 
+                             jk_tomcat_startup_data_t *data, 
+                             jk_pool_t *p);
+static int exec_cmd(const char *name, HANDLE *hTomcat, char *cmdLine);
+
+static void usage_message(const char *name)
+{
+    printf("%s - Usage:\n\n", name);
+    printf("To install the service:\n");
+    printf("%s -i <service name> {optional params} <config properties file>\n", name);
+    printf("    Optional parameters\n");
+    printf("        -u <user name> - In the form DomainName\\UserName (.\\UserName for local)\n");
+    printf("        -n <service display name> - In quotes if contains non-lphanumeric chars\n");
+    printf("        -p <user password>\n");
+    printf("        -a - Set startup type to automatic\n");
+    printf("        -d <service dependency> - Can be entered multiple times\n\n");
+    printf("To remove the service:\n");
+    printf("%s -r <service name>\n\n", name);
+    printf("To start the service:\n");
+    printf("%s -s <service name> {optional params}\n", name);
+    printf("    Optional parameters\n");
+    printf("        -m <machine>\n\n");
+    printf("To stop the service:\n");
+    printf("%s -t <service name> {optional params}\n", name);
+    printf("    Optional parameters\n");
+    printf("        -m <machine>\n");
+}
+
+void main(int argc, char **argv)
+{
+    WORD wVersionRequested;
+    WSADATA wsaData;
+    int i;
+    int err;
+    int count;
+    int iAction = acNoAction;
+    char *pServiceDisplayName = NULL;
+    char *pServiceName = NULL;
+    char *pUserName = NULL;
+    char *pPassword = NULL;
+    char *pMachine = NULL;
+    BOOL bAutomatic = FALSE;
+    char strDependancy[256] = "";
+
+    memset(strDependancy, 0, 255);
+
+    wVersionRequested = MAKEWORD(1, 1); 
+    err = WSAStartup(wVersionRequested, &wsaData);
+    if(0 != err) {
+        fprintf(stderr, "Error connecting to winsock");
+        return;
+    } 
+
+    if(LOBYTE( wsaData.wVersion ) != 1 || 
+       HIBYTE( wsaData.wVersion ) != 1)  {
+        fprintf(stderr, 
+                "Error winsock version is %d %d \n", 
+                LOBYTE( wsaData.wVersion ),HIBYTE( wsaData.wVersion ));
+        WSACleanup();
+        return; 
+    } 
+
+    fprintf(stderr, "Asked (and given) winsock %d.%d \n", 
+                    LOBYTE(wsaData.wVersion),
+                    HIBYTE(wsaData.wVersion));
+
+    __try {
+        if(argc > 2) {
+            count=0;
+            for (i=1;i<argc;i++) {
+                if ((*argv[i] == '-') || (*argv[i] == '/')) {
+                    char *cmd = argv[i];
+                    cmd++;
+                    if(0 == stricmp("i", cmd)) {
+                        iAction = acInstall;
+                        pServiceName = argv[i+1];
+                    } else if(0 == stricmp("r", cmd)) {
+                        iAction = acRemove;
+                        pServiceName = argv[i+1];
+                    } else if(0 == stricmp("s", cmd)) {
+                        iAction = acStartTC;
+                        pServiceName = argv[i+1];
+                    } else if(0 == stricmp("t", cmd)) {
+                        iAction = acStopTC;
+                        pServiceName = argv[i+1];
+                    } else if(0 == stricmp("u", cmd)) {
+                        pUserName = argv[i+1];
+                    } else if(0 == stricmp("p", cmd)) {
+                        pPassword = argv[i+1];
+                    } else if(0 == stricmp("m", cmd)) {
+                        pMachine = argv[i+1];
+                    } else if(0 == stricmp("a", cmd)) {
+                        bAutomatic = TRUE;
+                    } else if(0 == stricmp("n", cmd)) {
+                        pServiceDisplayName = argv[i+1];
+                    } else if(0 == stricmp("d", cmd)) {
+                        memcpy(strDependancy+count, argv[i+1], strlen(argv[i+1]));
+                        count+= strlen(argv[i+1])+1;
+                    }
+                }
+            }
+            switch (iAction) {
+            case acInstall:
+                if (pServiceDisplayName == NULL) {
+                    pServiceDisplayName = pServiceName;
+                }
+                install_service(pServiceName, pServiceDisplayName, pUserName,
+                                pPassword, strDependancy, bAutomatic, argv[i-1]);
+                return;
+            case acRemove:
+                remove_service(pServiceName);
+                return;
+            case acStartTC:
+                start_service(pServiceName, pMachine);
+                return;
+            case acStopTC:
+                stop_service(pServiceName, pMachine);
+                return;
+            }
+        } else if(2  == argc) {
+
+            SERVICE_TABLE_ENTRY dispatchTable[] =
+            {
+                { argv[1], (LPSERVICE_MAIN_FUNCTION)service_main },
+                { NULL, NULL }
+            };
+
+            if(!StartServiceCtrlDispatcher(dispatchTable)) {
+                AddToMessageLog("StartServiceCtrlDispatcher failed.");
+            }
+            return;
+        } 
+
+        usage_message(argv[0]);
+        exit(-1);
+    } __finally {
+        WSACleanup();
+    }
+}
+
+void WINAPI service_main(DWORD dwArgc, char **lpszArgv)
+{
+    // register our service control handler:
+    //
+    //
+    sshStatusHandle = RegisterServiceCtrlHandler(lpszArgv[0], service_ctrl);
+
+    if(sshStatusHandle) {
+
+        ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+        ssStatus.dwServiceSpecificExitCode = 0;
+
+        // report the status to the service control manager.
+        //
+        if(ReportStatusToSCMgr(SERVICE_START_PENDING, // service state
+                                NO_ERROR,              // exit code
+                                3000)) {                 // wait hint    
+            start_jk_service(lpszArgv[0]);
+        }
+    }
+
+    // try to report the stopped status to the service control manager.
+    //
+    if(sshStatusHandle) {
+        ReportStatusToSCMgr(SERVICE_STOPPED,
+                            dwErr,
+                            0);
+    }
+}
+
+
+void WINAPI service_ctrl(DWORD dwCtrlCode)
+{
+    /*
+     * Handle the requested control code.
+     */
+    switch(dwCtrlCode)
+    {
+        /*
+         * Stop the service.
+         */
+        case SERVICE_CONTROL_SHUTDOWN:
+        case SERVICE_CONTROL_STOP:
+            ssStatus.dwCurrentState = SERVICE_STOP_PENDING;
+            stop_jk_service();
+            break;
+
+        /*
+         * Update the service status.
+         */
+        case SERVICE_CONTROL_INTERROGATE:
+            break;
+
+        /*
+         * Invalid control code, nothing to do.
+         */
+        default:
+            break;
+
+    }
+
+    ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
+
+}
+
+BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
+                         DWORD dwWin32ExitCode,
+                         DWORD dwWaitHint)
+{
+    static DWORD dwCheckPoint = 1;
+    BOOL fResult = TRUE;
+
+    if(dwCurrentState == SERVICE_START_PENDING) {
+        ssStatus.dwControlsAccepted = 0;
+    } else {
+        ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+    }
+
+    ssStatus.dwCurrentState = dwCurrentState;
+    ssStatus.dwWin32ExitCode = dwWin32ExitCode;
+    ssStatus.dwWaitHint = dwWaitHint;
+
+    if((dwCurrentState == SERVICE_RUNNING) ||
+       (dwCurrentState == SERVICE_STOPPED)) {
+        ssStatus.dwCheckPoint = 0;
+    } else {
+        ssStatus.dwCheckPoint = dwCheckPoint++;
+    }
+
+    if(!(fResult = SetServiceStatus(sshStatusHandle, &ssStatus))) {
+        AddToMessageLog(TEXT("SetServiceStatus"));
+    }
+
+    return fResult;
+}
+
+typedef WINADVAPI BOOL (WINAPI * pfnChangeServiceConfig2_t)
+                       (SC_HANDLE hService, DWORD dwInfoLevel, LPVOID lpInfo);
+
+
+void install_service(char *name, 
+                     char *dname, 
+                     char *user, 
+                     char *password, 
+                     char *deps, 
+                     BOOL bAutomatic,
+                     char *rel_prp_file)
+{
+    SC_HANDLE   schService;
+    SC_HANDLE   schSCManager;
+    char        szExecPath[2048];
+    char        szPropPath[2048];
+    char        szTrueName[256];
+    char        *dummy;
+    char        *src, *dst;
+
+    dst = szTrueName;
+    for (src = name; *src; ++src) {
+        if (dst >= szTrueName + sizeof(szTrueName) - 1) {
+            break;
+        }
+        if (!isspace(*src) && *src != '/' && *src != '\\') {
+            *(dst++) = *src;
+        }
+    }
+    *dst = '\0';
+
+    if (0 == stricmp("", deps))
+        deps = NULL;
+
+    /* XXX strcat( deps, "Tcpip\0Afd\0" ); */
+    
+    if(!GetFullPathName(rel_prp_file, sizeof(szPropPath) - 1, szPropPath, &dummy)) {
+        printf("Unable to install %s - %s\n", 
+               name, 
+               GetLastErrorText(szErr, sizeof(szErr)));
+        return;
+    }
+
+    if(!jk_file_exists(szPropPath)) {
+        printf("Unable to install %s - File [%s] does not exists\n", 
+               name, 
+               szPropPath);
+        return;
+    }
+
+    szExecPath[0] = '\"';
+    if(GetModuleFileName( NULL, szExecPath + 1, sizeof(szExecPath) - 2) == 0) {
+        /* Was: if(GetModuleFileName( NULL, szExecPath, sizeof(szExecPath) - 1) == 0) { */
+        printf("Unable to install %s - %s\n", 
+               name, 
+               GetLastErrorText(szErr, sizeof(szErr)));
+        return;
+    }
+    strcat(szExecPath, "\" ");
+    strcat(szExecPath, szTrueName);
+
+
+    schSCManager = OpenSCManager(NULL,     // machine (NULL == local)
+                                 NULL,     // database (NULL == default)
+                                 SC_MANAGER_ALL_ACCESS);   // access required                       
+    if(schSCManager) {
+
+        schService = CreateService(schSCManager, // SCManager database
+                                   szTrueName,   // name of service
+                                   dname,         // name to display
+                                   SERVICE_ALL_ACCESS, // desired access
+                                   SERVICE_WIN32_OWN_PROCESS,  // service type
+                                   bAutomatic ? SERVICE_AUTO_START : SERVICE_DEMAND_START,       // start type
+                                   SERVICE_ERROR_NORMAL,       // error control type
+                                   szExecPath,                 // service's binary
+                                   NULL,                       // no load ordering group
+                                   NULL,                       // no tag identifier
+                                   deps,                       // dependencies
+                                   user,                       // account
+                                   password);                  // password
+
+        if(schService) {
+            
+            printf("The service named %s was created. Now adding registry entries\n", name);
+            
+            if(set_registry_values(schService, szTrueName, szPropPath)) {
+                CloseServiceHandle(schService);
+            } else {
+                printf("CreateService failed setting the private registry - %s\n", GetLastErrorText(szErr, sizeof(szErr)));
+                DeleteService(schService);
+                CloseServiceHandle(schService);
+            }
+        } else {
+            printf("CreateService failed - %s\n", GetLastErrorText(szErr, sizeof(szErr)));
+        }
+
+        CloseServiceHandle(schSCManager);
+    } else { 
+        printf("OpenSCManager failed - %s\n", GetLastErrorText(szErr, sizeof(szErr)));
+    }
+}
+
+void remove_service(char *name)
+{
+    SC_HANDLE   schService;
+    SC_HANDLE   schSCManager;
+    char        szNameBuff[256];
+    DWORD       lenNameBuff = 256;
+    char        *szTrueName = name;
+
+    schSCManager = OpenSCManager(NULL,          // machine (NULL == local)
+                                 NULL,          // database (NULL == default)
+                                 SC_MANAGER_ALL_ACCESS );  // access required
+                        
+    if(schSCManager) {
+        if (GetServiceKeyName(schSCManager, name, szNameBuff, &lenNameBuff)) {
+            szTrueName = szNameBuff;
+        }
+        schService = OpenService(schSCManager, szTrueName, SERVICE_ALL_ACCESS);
+
+        if(schService) {
+            // try to stop the service
+            if(ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus )) {
+                printf("Stopping %s.", name);
+                Sleep(1000);
+
+                while(QueryServiceStatus(schService, &ssStatus )) {
+                    if(ssStatus.dwCurrentState == SERVICE_STOP_PENDING) {
+                        printf(".");
+                        Sleep(1000);
+                    } else {
+                        break;
+                    }
+                }
+
+                if(ssStatus.dwCurrentState == SERVICE_STOPPED) {
+                    printf("\n%s stopped.\n", name);
+                } else {
+                    printf("\n%s failed to stop.\n", name);
+                }
+            }
+
+            // now remove the service
+            if(DeleteService(schService)) {
+                printf("%s removed.\n", name);
+            } else {
+                printf("DeleteService failed - %s\n", GetLastErrorText(szErr, sizeof(szErr)));
+            }
+
+            CloseServiceHandle(schService);
+        } else {
+            printf("OpenService failed - %s\n", GetLastErrorText(szErr, sizeof(szErr)));
+        }
+
+        CloseServiceHandle(schSCManager);
+    } else {
+        printf("OpenSCManager failed - %s\n", GetLastErrorText(szErr, sizeof(szErr)));
+    }
+}
+
+void start_service(char *name, char *machine)
+{
+    SC_HANDLE   schService;
+    SC_HANDLE   schSCManager;
+
+    schSCManager = OpenSCManager(machine,  // machine (NULL == local)
+                                 NULL,     // database (NULL == default)
+                                 SC_MANAGER_ALL_ACCESS);   // access required                       
+
+    if(schSCManager) {
+        schService = OpenService(schSCManager, name, SERVICE_ALL_ACCESS);
+ 
+       if(schService) {
+            // try to start the service
+            if(StartService(schService, 0, NULL)) {
+                printf("Starting %s.", name);
+                Sleep(1000);
+
+                while(QueryServiceStatus(schService, &ssStatus )) {
+                    if(ssStatus.dwCurrentState == SERVICE_START_PENDING) {
+                        printf(".");
+                        Sleep(1000);
+                    } else {
+                        break;
+                    }
+                }
+
+                if(ssStatus.dwCurrentState == SERVICE_RUNNING) {
+                    printf("\n%s started.\n", name);
+                } else {
+                    printf("\n%s failed to start.\n", name);
+                }
+            }
+            else
+                printf("StartService failed - %s\n", GetLastErrorText(szErr, sizeof(szErr)));
+
+            CloseServiceHandle(schService);
+        } else {
+            printf("OpenService failed - %s\n", GetLastErrorText(szErr, sizeof(szErr)));
+        }
+
+        CloseServiceHandle(schSCManager);
+    } else {
+        printf("OpenSCManager failed - %s\n", GetLastErrorText(szErr, sizeof(szErr)));
+    }
+}
+
+void stop_service(char *name, char *machine)
+{
+    SC_HANDLE   schService;
+    SC_HANDLE   schSCManager;
+
+    schSCManager = OpenSCManager(machine,  // machine (NULL == local)
+                                 NULL,     // database (NULL == default)
+                                 SC_MANAGER_ALL_ACCESS);   // access required                       
+
+    if(schSCManager) {
+        schService = OpenService(schSCManager, name, SERVICE_ALL_ACCESS);
+
+        if(schService) {
+            // try to stop the service
+            if(ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus )) {
+                printf("Stopping %s.", name);
+                Sleep(1000);
+
+                while(QueryServiceStatus(schService, &ssStatus )) {
+                    if(ssStatus.dwCurrentState == SERVICE_STOP_PENDING) {
+                        printf(".");
+                        Sleep(1000);
+                    } else {
+                        break;
+                    }
+                }
+
+                if(ssStatus.dwCurrentState == SERVICE_STOPPED) {
+                    printf("\n%s stopped.\n", name);
+                } else {
+                    printf("\n%s failed to stop.\n", name);
+                }
+            }
+            else
+                printf("StopService failed - %s\n", GetLastErrorText(szErr, sizeof(szErr)));
+
+            CloseServiceHandle(schService);
+        } else {
+            printf("OpenService failed - %s\n", GetLastErrorText(szErr, sizeof(szErr)));
+        }
+
+        CloseServiceHandle(schSCManager);
+    } else {
+        printf("OpenSCManager failed - %s\n", GetLastErrorText(szErr, sizeof(szErr)));
+    }
+}
+
+static int set_registry_values(SC_HANDLE   schService, char *name, 
+                               char *prp_file)
+{
+    char  tag[1024];
+    HKEY  hk;
+    int rc;
+    /* Api based */
+    HANDLE hAdvApi32;
+    char *szDescription = "Jakarta Tomcat Server";
+    pfnChangeServiceConfig2_t pfnChangeServiceConfig2;
+            
+    if((hAdvApi32 = GetModuleHandle("advapi32.dll"))
+       && ((pfnChangeServiceConfig2 = (pfnChangeServiceConfig2_t)
+            GetProcAddress(hAdvApi32, "ChangeServiceConfig2A")))) {
+        (void) pfnChangeServiceConfig2(schService, // Service Handle
+                                       1,          // SERVICE_CONFIG_DESCRIPTION
+                                       &szDescription);
+    } else {
+        char value[2024];
+
+        rc = JK_FALSE;
+
+        strcpy(tag, BASE_REGISTRY_LOCATION);
+        strcat(tag, name);
+        
+        if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                                         tag,
+                                         (DWORD)0,         
+                                         KEY_WRITE | KEY_READ,
+                                         &hk)) {
+            rc = get_registry_config_parameter(hk,
+                                               IMAGE_NAME, 
+                                               value,
+                                               sizeof(value));
+            if(rc) {
+                strcat(value, " ");
+                strcat(value, name);
+                rc = set_registry_config_parameter(hk,
+                                                   IMAGE_NAME, 
+                                                   value);
+                if(rc) {
+                    printf("Registry values were added\n");
+                    printf("If you have already updated wrapper.properties you may start the %s"
+                           "service by executing \"jk_nt_service -s %s\" from the command prompt\n",
+                           name,
+                           name);                    
+                }
+            }
+            RegCloseKey(hk);
+        }
+        if(!rc) {
+            printf("Error: Failed to update the service command line - %s\n", 
+                   GetLastErrorText(szErr, sizeof(szErr)));                
+        }
+    }
+    
+    strcpy(tag, BASE_REGISTRY_LOCATION);
+    strcat(tag, name);
+    strcat(tag, "\\");
+    strcat(tag, PARAMS_LOCATION);
+
+    rc = create_registry_key(tag, &hk);
+
+    if(rc) {
+        rc = set_registry_config_parameter(hk, PRP_LOCATION, prp_file);
+        if(!rc) {
+            printf("Error: Can not create value [%s] - %s\n", 
+                    PRP_LOCATION, 
+                    GetLastErrorText(szErr, sizeof(szErr)));                
+        }
+        RegCloseKey(hk);
+    } else {
+        printf("Error: Can not create key [%s] - %s\n", 
+                tag, 
+                GetLastErrorText(szErr, sizeof(szErr)));                
+    }
+    return rc;
+}
+
+static void start_jk_service(char *name)
+{
+    /*
+     * report the status to the service control manager.
+     */
+    if(ReportStatusToSCMgr(SERVICE_START_PENDING, // service state
+                           NO_ERROR,              // exit code
+                           3000)) {               // wait hint
+        
+        /* 
+         * create the event object. The control handler function signals
+         * this event when it receives the "stop" control code.
+         */
+        hServerStopEvent = CreateEvent(NULL,    // no security attributes
+                                       TRUE,    // manual reset event
+                                       FALSE,   // not-signalled
+                                       NULL);   // no name
+
+        if(hServerStopEvent) {
+            if(ReportStatusToSCMgr(SERVICE_START_PENDING, // service state
+                                   NO_ERROR,              // exit code
+                                   20000)) {              // wait hint
+                HANDLE hTomcat = NULL;
+                char   szNameBuff[256];
+                DWORD  lenNameBuff = 256;
+                char   *szTrueName = name;
+                SC_HANDLE   schSCManager;
+                int rc;
+
+                schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS );
+                if(schSCManager) {
+                    if (GetServiceKeyName(schSCManager, name, szNameBuff, &lenNameBuff)) {
+                        szTrueName = szNameBuff;
+                    }
+                    CloseServiceHandle(schSCManager);
+                }
+
+                rc = start_tomcat(szTrueName, &hTomcat);
+
+                if(rc && ReportStatusToSCMgr(SERVICE_RUNNING, // service state
+                                             NO_ERROR,        // exit code
+                                             0)) {            // wait hint       
+                    HANDLE waitfor[] = { hServerStopEvent, hTomcat};
+                    DWORD dwIndex = WaitForMultipleObjects(2, waitfor, FALSE, INFINITE);
+
+                    switch(dwIndex) {
+                    case WAIT_OBJECT_0:
+                        /* 
+                         * Stop order arrived 
+                         */ 
+                        ResetEvent(hServerStopEvent);
+                        stop_tomcat(name, shutdown_port, shutdown_protocol,
+                                    shutdown_secret, hTomcat);
+                        break;
+                    case (WAIT_OBJECT_0 + 1):
+                        /* 
+                         * Tomcat died !!!
+                         */ 
+                        CloseHandle(hServerStopEvent);
+                        CloseHandle(hTomcat);
+                        exit(0); // exit ungracefully so
+                                 // Service Control Manager 
+                                 // will attempt a restart.
+                        break;
+                    default:
+                        /* 
+                         * some error... 
+                         * close the servlet container and exit 
+                         */ 
+                        stop_tomcat(name, shutdown_port, shutdown_protocol,
+                                    shutdown_secret, hTomcat);
+                    }
+                    CloseHandle(hServerStopEvent);
+                    CloseHandle(hTomcat);
+                }                
+            }
+        }
+    }
+
+    if(hServerStopEvent) {
+        CloseHandle(hServerStopEvent);
+    }
+}
+
+
+static void stop_jk_service(void)
+{
+    if(hServerStopEvent) {
+        SetEvent(hServerStopEvent);
+    }
+}
+
+static void AddToMessageLog(char *lpszMsg)
+{   
+    char    szMsg[2048];
+    HANDLE  hEventSource;
+    char *  lpszStrings[2];
+
+    printf("Error: %s\n", lpszMsg);
+
+    dwErr = GetLastError();
+
+    hEventSource = RegisterEventSource(NULL, "Jakrta - Tomcat");
+
+    sprintf(szMsg, "%s error: %d", "Jakrta - Tomcat", dwErr);
+    lpszStrings[0] = szMsg;
+    lpszStrings[1] = lpszMsg;
+
+    if(hEventSource != NULL) {
+        ReportEvent(hEventSource, // handle of event source
+            EVENTLOG_ERROR_TYPE,  // event type
+            0,                    // event category
+            0,                    // event ID
+            NULL,                 // current user's SID
+            2,                    // strings in lpszStrings
+            0,                    // no bytes of raw data
+            lpszStrings,          // array of error strings
+            NULL);                // no raw data
+
+        DeregisterEventSource(hEventSource);
+    }
+    
+}
+
+//
+//  FUNCTION: GetLastErrorText
+//
+//  PURPOSE: copies error message text to string
+//
+//  PARAMETERS:
+//    lpszBuf - destination buffer
+//    dwSize - size of buffer
+//
+//  RETURN VALUE:
+//    destination buffer
+//
+//  COMMENTS:
+//
+char *GetLastErrorText( char *lpszBuf, DWORD dwSize )
+{
+    DWORD dwRet;
+    char *lpszTemp = NULL;
+
+    dwRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
+                          NULL,
+                          GetLastError(),
+                          LANG_NEUTRAL,
+                          (char *)&lpszTemp,
+                          0,
+                          NULL);
+
+    // supplied buffer is not long enough
+    if(!dwRet || ((long)dwSize < (long)dwRet+14)) {
+        lpszBuf[0] = '\0';
+    } else {
+        lpszTemp[lstrlen(lpszTemp)-2] = '\0';  //remove cr and newline character
+        sprintf(lpszBuf, "%s (0x%x)", lpszTemp, GetLastError());
+    }
+
+    if(lpszTemp) {
+        LocalFree((HLOCAL) lpszTemp );
+    }
+
+    return lpszBuf;
+}
+
+static void stop_tomcat(char *name,
+                        int port, 
+                        const char *protocol,
+                        char *secret,
+                        HANDLE hTomcat)
+{
+    struct sockaddr_in in;
+
+    if(strcasecmp(protocol, "cmd") == 0 ) {
+        exec_cmd( name, hTomcat, shutdown_cmd);
+        /* XXX sleep 100 */
+        TerminateProcess(hTomcat, 0);
+        return;
+    } 
+    
+    if(jk_resolve("localhost", port, &in)) {
+        int sd = jk_open_socket(&in, JK_TRUE, 0, NULL);
+        if(sd >0) {
+            int rc = JK_FALSE;
+
+            if(strcasecmp(protocol, "catalina") == 0 ) {
+                char len;
+                
+                if( secret==NULL )
+                    secret="SHUTDOWN";
+                len=strlen( secret );
+                
+                rc = send(sd, secret, len , 0);
+                if(len == rc) {
+                    rc = JK_TRUE;
+                }
+            } else if(!strcasecmp(protocol, "ajp13")) {
+                jk_pool_t pool;
+                jk_msg_buf_t *msg = NULL;
+                jk_pool_atom_t buf[TINY_POOL_SIZE];
+
+                jk_open_pool(&pool, buf, sizeof(buf));
+
+                msg = jk_b_new(&pool);
+                jk_b_set_buffer_size(msg, 512); 
+
+                rc = ajp13_marshal_shutdown_into_msgb(msg, 
+                                                      &pool,
+                                                      NULL);
+                if( secret!=NULL ) {
+                    /** will work with old clients, as well as new
+                     */
+                    rc = jk_b_append_string(msg, secret);
+                }
+                if(rc) {
+                    jk_b_end(msg, AJP13_PROTO);
+    
+                    if(0 > jk_tcp_socket_sendfull(sd, 
+                                                  jk_b_get_buff(msg),
+                                                  jk_b_get_len(msg))) {
+                        rc = JK_FALSE;
+                    }
+                }                                                    
+            } else {
+                char b[] = {(char)254, (char)15};
+                rc = send(sd, b, 2, 0);
+                if(2 == rc) {
+                    rc = JK_TRUE;
+                }
+            }
+            jk_close_socket(sd);
+            if(JK_TRUE == rc) {
+                if(WAIT_OBJECT_0 == WaitForSingleObject(hTomcat, 30*1000)) {
+                    return;
+                }
+            }            
+        }
+    }
+
+    TerminateProcess(hTomcat, 0);    
+}
+
+static int exec_cmd(const char *name, HANDLE *hTomcat, char *cmdLine)
+{
+    char  tag[1024];
+    HKEY  hk;
+
+    strcpy(tag, BASE_REGISTRY_LOCATION);
+    strcat(tag, name);
+    strcat(tag, "\\");
+    strcat(tag, PARAMS_LOCATION);
+
+    if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                                     tag,
+                                     (DWORD)0,         
+                                     KEY_READ,
+                                     &hk)) {
+        char prp_file[2048];
+        if(get_registry_config_parameter(hk,
+                                         PRP_LOCATION, 
+                                         prp_file,
+                                         sizeof(prp_file))) {
+            jk_map_t *init_map;
+            
+            if(map_alloc(&init_map)) {
+                if(map_read_properties(init_map, prp_file)) {
+                    jk_tomcat_startup_data_t data;
+                    jk_pool_t p;
+                    jk_pool_atom_t buf[HUGE_POOL_SIZE];
+                    jk_open_pool(&p, buf, sizeof(buf));
+            
+                    if(read_startup_data(init_map, &data, &p)) {
+                        STARTUPINFO startupInfo;
+                        PROCESS_INFORMATION processInformation;
+                        SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
+
+                        if(data.extra_path) {
+                            jk_append_libpath(&p, data.extra_path);
+                        }
+
+                        memset(&startupInfo, 0, sizeof(startupInfo));
+                        startupInfo.cb = sizeof(startupInfo);
+                        startupInfo.lpTitle = "Jakarta Tomcat";
+                        startupInfo.dwFlags = STARTF_USESTDHANDLES;
+                        startupInfo.hStdInput = NULL;
+                        startupInfo.hStdOutput = CreateFile(data.stdout_file,
+                                                            GENERIC_WRITE,
+                                                            FILE_SHARE_READ,
+                                                            &sa,
+                                                            OPEN_ALWAYS,
+                                                            FILE_ATTRIBUTE_NORMAL,
+                                                            NULL);
+                        SetFilePointer(startupInfo.hStdOutput,
+                                       0,
+                                       NULL,
+                                       FILE_END);
+                        startupInfo.hStdError = CreateFile(data.stderr_file,
+                                                           GENERIC_WRITE,
+                                                           FILE_SHARE_READ,
+                                                           &sa,
+                                                           OPEN_ALWAYS,
+                                                           FILE_ATTRIBUTE_NORMAL,
+                                                           NULL);
+                        SetFilePointer(startupInfo.hStdError,
+                                       0,
+                                       NULL,
+                                       FILE_END);
+
+                        memset(&processInformation, 0, sizeof(processInformation));
+                        
+						if( cmdLine==NULL ) 
+							cmdLine=data.cmd_line;
+
+                        printf(cmdLine);
+                        if(CreateProcess(data.java_bin,
+                                        cmdLine,
+                                        NULL,
+                                        NULL,
+                                        TRUE,
+                                        CREATE_NEW_CONSOLE,
+                                        NULL,
+                                        data.tomcat_home,
+                                        &startupInfo,
+                                        &processInformation)){
+
+                            *hTomcat = processInformation.hProcess;
+                            CloseHandle(processInformation.hThread);
+                            CloseHandle(startupInfo.hStdOutput);
+                            CloseHandle(startupInfo.hStdError);
+
+                            shutdown_port = data.shutdown_port;
+                            shutdown_secret = data.shutdown_secret;
+                            shutdown_protocol = strdup(data.shutdown_protocol);
+							shutdown_cmd = strdup(data.stop_cmd);
+
+                            return JK_TRUE;
+                        } else {
+                            printf("Error: Can not create new process - %s\n", 
+                                    GetLastErrorText(szErr, sizeof(szErr)));                
+                        }
+
+                    }                    
+                }
+            }
+            map_free(&init_map);
+        }
+        RegCloseKey(hk);
+    } 
+
+    return JK_FALSE;
+}
+
+static int start_tomcat(const char *name, HANDLE *hTomcat)
+{
+    return exec_cmd( name, hTomcat, NULL );
+}
+
+static int create_registry_key(const char *tag,
+                               HKEY *key)
+{
+    LONG  lrc = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
+                               tag,
+                               0,
+                               NULL,
+                               REG_OPTION_NON_VOLATILE,
+                               KEY_WRITE,
+                               NULL,
+                               key,
+                               NULL);
+    if(ERROR_SUCCESS != lrc) {
+        return JK_FALSE;        
+    }
+
+    return JK_TRUE;
+}
+
+static int set_registry_config_parameter(HKEY hkey,
+                                         const char *tag, 
+                                         char *value)
+{       
+    LONG  lrc;
+
+    lrc = RegSetValueEx(hkey, 
+                        tag,            
+                        0,              
+                        REG_SZ,  
+                        value, 
+                        strlen(value));
+
+    if(ERROR_SUCCESS != lrc) {
+        return JK_FALSE;        
+    }
+
+    return JK_TRUE;     
+}
+
+
+
+static int get_registry_config_parameter(HKEY hkey,
+                                         const char *tag, 
+                                         char *b,
+                                         DWORD sz)
+{   
+    DWORD type = 0;
+    LONG  lrc;
+
+    lrc = RegQueryValueEx(hkey,     
+                          tag,      
+                          (LPDWORD)0,
+                          &type,    
+                          (LPBYTE)b,
+                          &sz); 
+    if(ERROR_SUCCESS != lrc) {
+        return JK_FALSE;        
+    }
+    
+    b[sz] = '\0';
+
+    return JK_TRUE;     
+}
+
+static int read_startup_data(jk_map_t *init_map, 
+                             jk_tomcat_startup_data_t *data, 
+                             jk_pool_t *p)
+{
+    
+    data->classpath = NULL;
+    data->tomcat_home = NULL;
+    data->stdout_file = NULL;
+    data->stderr_file = NULL;
+    data->java_bin = NULL;
+    data->extra_path = NULL;
+    data->tomcat_class = NULL;
+    data->server_file = NULL;
+
+    /* All this is wrong - you just need to configure cmd_line */
+    /* Optional - you may have cmd_line defined */
+    data->server_file = map_get_string(init_map, 
+                                       "wrapper.server_xml", 
+                                       NULL);
+    data->classpath = map_get_string(init_map, 
+                                     "wrapper.class_path", 
+                                       NULL);
+    data->tomcat_home = map_get_string(init_map, 
+                                       "wrapper.tomcat_home", 
+                                       NULL);
+    data->java_bin = map_get_string(init_map, 
+                                    "wrapper.javabin", 
+                                    NULL);
+    data->tomcat_class = map_get_string(init_map,
+                                        "wrapper.startup_class",
+                                        "org.apache.tomcat.startup.Tomcat");
+
+    data->cmd_line = map_get_string(init_map,
+                                    "wrapper.cmd_line",
+                                    NULL);
+
+    data->stop_cmd = map_get_string(init_map,
+                                    "wrapper.stop_cmd",
+                                    NULL);
+
+    if(NULL == data->cmd_line &&
+       ( (NULL == data->tomcat_class) ||
+         (NULL == data->server_file) ||
+         (NULL == data->tomcat_home) ||
+         (NULL == data->java_bin) )) {
+       return JK_FALSE;
+    }
+
+    if(NULL == data->cmd_line) {
+        data->cmd_line = (char *)jk_pool_alloc(p, (20 + 
+                                                   strlen(data->java_bin) +
+                                                   strlen(" -classpath ") +
+                                                   strlen(data->classpath) +
+                                                   strlen(data->tomcat_class) +
+                                                   strlen(" -home ") +
+                                                   strlen(data->tomcat_home) +
+                                                   strlen(" -config ") +
+                                                   strlen(data->server_file)
+                                                   ) * sizeof(char));
+        if(NULL == data->cmd_line) {
+            return JK_FALSE;
+        }
+
+        strcpy(data->cmd_line, data->java_bin);
+        strcat(data->cmd_line, " -classpath ");
+        strcat(data->cmd_line, data->classpath);
+        strcat(data->cmd_line, " ");
+        strcat(data->cmd_line, data->tomcat_class);
+        strcat(data->cmd_line, " -home ");
+        strcat(data->cmd_line, data->tomcat_home);
+        strcat(data->cmd_line, " -config ");
+        strcat(data->cmd_line, data->server_file);
+    }
+
+    data->shutdown_port = map_get_int(init_map,
+                                      "wrapper.shutdown_port",
+                                      8007);
+
+    data->shutdown_secret = map_get_string(init_map,
+                                           "wrapper.shutdown_secret", NULL );
+    
+    data->shutdown_protocol = map_get_string(init_map,
+                                             "wrapper.shutdown_protocol",
+                                             AJP12_TAG);
+
+    data->extra_path = map_get_string(init_map,
+                                      "wrapper.ld_path",
+                                      NULL);
+
+    data->stdout_file = map_get_string(init_map,
+                                       "wrapper.stdout",
+                                       NULL);
+
+    if(NULL == data->stdout_file && NULL == data->tomcat_home ) {
+        return JK_FALSE;
+    }
+    
+    if(NULL == data->stdout_file) {
+        data->stdout_file = jk_pool_alloc(p, strlen(data->tomcat_home) + 2 + strlen("\\stdout.log"));
+        strcpy(data->stdout_file, data->tomcat_home);
+        strcat(data->stdout_file, "\\stdout.log");        
+    }
+
+    data->stderr_file = map_get_string(init_map,
+                                       "wrapper.stderr",
+                                       NULL);
+
+    if(NULL == data->stderr_file) {
+        data->stderr_file = jk_pool_alloc(p, strlen(data->tomcat_home) + 2 + strlen("\\stderr.log"));
+        strcpy(data->stderr_file, data->tomcat_home);
+        strcat(data->stderr_file, "\\stderr.log");        
+    }
+
+    return JK_TRUE;
+}
+
diff --git a/connectors/jk/native/nt_service/nt_service.dsp b/connectors/jk/native/nt_service/nt_service.dsp
new file mode 100644
index 0000000..f2eb34c
--- /dev/null
+++ b/connectors/jk/native/nt_service/nt_service.dsp
@@ -0,0 +1,199 @@
+# Microsoft Developer Studio Project File - Name="nt_service" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=nt_service - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "nt_service.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "nt_service.mak" CFG="nt_service - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "nt_service - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "nt_service - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "nt_service - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I "../common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"Release/jk_nt_service.exe"
+
+!ELSEIF  "$(CFG)" == "nt_service - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "../common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"Debug/jk_nt_service.exe" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "nt_service - Win32 Release"
+# Name "nt_service - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\common\jk_ajp13.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_connect.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_msg_buff.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_nt_service.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_util.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14_worker.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_context.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_uri_worker_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp_common.c
+# End Source File
+# End Group
+
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\common\jk_connect.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_global.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_uri_worker_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_service.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_util.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp14_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_context.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_ajp_common.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/connectors/jk/native/scripts/build/config_vars.mk b/connectors/jk/native/scripts/build/config_vars.mk
new file mode 100644
index 0000000..abce97e
--- /dev/null
+++ b/connectors/jk/native/scripts/build/config_vars.mk
@@ -0,0 +1,2 @@
+# libtool is given by Apache-2.0 when installed otherwise we provide it.
+LIBTOOL      = $(SHELL) ../libtool
diff --git a/connectors/jk/native/scripts/build/rules.mk b/connectors/jk/native/scripts/build/rules.mk
new file mode 100644
index 0000000..9448674
--- /dev/null
+++ b/connectors/jk/native/scripts/build/rules.mk
@@ -0,0 +1,29 @@
+# That an extract of what is in APR.
+#
+
+# Compile commands
+#VPATH=.:../common
+COMPILE      = $(CC) $(CFLAGS)
+LT_COMPILE   = $(LIBTOOL) --mode=compile $(COMPILE) -c $< 
+# && touch $@
+
+# Implicit rules for creating outputs from input files
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .slo .s
+
+.c.o:
+	$(COMPILE) -c $<
+
+.s.o:
+	$(COMPILE) -c $<
+
+.c.lo:
+	$(LT_COMPILE)
+
+.s.lo:
+	$(LT_COMPILE)
+
+.c.slo:
+	$(SH_COMPILE)
+
diff --git a/connectors/jk/native/scripts/build/unix/dummy b/connectors/jk/native/scripts/build/unix/dummy
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/connectors/jk/native/scripts/build/unix/dummy
diff --git a/connectors/jk/native2/.indent.pro b/connectors/jk/native2/.indent.pro
new file mode 100644
index 0000000..3e5d902
--- /dev/null
+++ b/connectors/jk/native2/.indent.pro
@@ -0,0 +1,19 @@
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 -nut -ncs
+-Tjk_env_t
+-Tjk_bean_t
+-Tjk_endpoint_t
+-Tjk_shm_t
+-Tjk_channel_t
+-Tjk_msg_t
+-Tjk_uriEnv_t
+-Tjk_workerEnv_t
+-Tjk_uriMap_t
+-Tjk_pool_t
+-Tjk_worker_t
+-Tjk_config_t
+-Tjk_stat_t
+-Tjk_logger_t
+-Tjk_map_t
+-Tjk_ws_service_t
+-Tjk_mutex_t
+-Tjk_objCache_t
diff --git a/connectors/jk/native2/BUILD.txt b/connectors/jk/native2/BUILD.txt
new file mode 100644
index 0000000..80e4769
--- /dev/null
+++ b/connectors/jk/native2/BUILD.txt
@@ -0,0 +1,108 @@
+Information on building mod_jk2:
+
+  Starting with 2.0.4, APR is mandatory for jk2. For Apache 2.0
+  or greater jk2 will use APR that was used to build Apache 2.0.
+  For Apache 1.3, jk2 must build APR and APR_UTIL from source. 
+
+DSO build instructions for Unix-like systems:
+
+  The compiler used to build jk2 must match the one used to build
+  Apache. You may need to set an environment variable before 
+  configuring such as CC=cc. `apxs -q CC` will tell you what 
+  compiler was used for Apache.
+
+  The most straightforward way to configure jk2 is to use apxs 
+  that comes with Apache. Linux distributions may need to have 
+  additional rpm's installed such as Apache2 devel rpm, 
+  httpd-devel or apache2-devel or for Apache 13, Apache devel 
+  rpm, httpd-devel or apache-devel depending on your 
+  distribution.
+
+  Example Apache2 build and install:
+
+    cd jakarta-tomcat-connectors/jk/native2
+    ./configure --with-apxs2=/your/path/to/apxs
+    make
+    cd ../build/jk2/apache2
+    apxs -n jk2 -i mod_jk2.so
+
+  Example Apache13 build and install:
+
+    apr and apr-util will be configured and built for you while
+    configuring and building jk2. There is no need to separately
+    configure and build them. 
+
+      cd jakarta-tomcat-connectors/jk/native2
+      ./configure --with-apxs=/your/path/to/apxs \
+                  --with-apr=/absolute/path/to/apr-source \
+                  --with-apr-util=/absolute/path/to/apr-util-source
+      make
+      cd ../build/jk2/apache13
+      apxs -n jk2 -i mod_jk2.so
+
+    NOTE: pthread support may be automatically detected and built
+    into apr. If apache13 was not built with pthread support, you
+    can either disable it by adding --disable-apr-threads while
+    configuring, or load the pthread library in httpd.conf using
+    the LoadFile directive.
+
+  Optional configure arguments (for 1.3 and 2.0):
+
+    If you want to have JNI support, add --with-jni and be sure
+    to have the JAVA_HOME environment variable point to your Java
+    Environment. This will build inprocess jni support into
+    mod_jk2.so and additionally build libjkjni.so. libjkjni.so
+    can be used by tomcat to provide support for channel unix and
+    should be installed in the apache libexec dir. Use 
+    `apxs -q LIBEXECDIR` if you are unsure of its location. 
+    Libjkjni.so will be located in the same directory as 
+    mod_jk2.so after building with this option.
+
+    If you want to have PCRE (Perl Compatible Regular
+    Expressions) support for jk2 uri directives, add --with-pcre
+    while configuring.
+
+
+Quick information on building mod_jk2 :
+
+* IIS 
+
+There is a known issue with the latest APR 1.0 and MSVC6.
+If you want to use MSVC6, please use APR 0.9.x for now.
+MSVC7 doesn't have this issue, and could be used with APR 1.0.
+
+Isapi redirector requires the following libraries to build:
+apr, apr-util, apr-iconv and pcre.
+The easiest way to obtain all those libraries is to download
+the httpd-2.0.49-win32-src.zip from http://www.apache.org/dist/httpd or
+from any of the mirror sites.
+You will only need the srclib part (apr, apr-util, apr-iconv and pcre)
+Unzip the entire srclib folder to j-t-c native2 folder.
+Now open the isapi.dsw from MSVC6 and build.
+
+Building using VS.NET:
+Make sure that the required libraries are inside native2/srclib.
+Open the idapi.dsw and select 'Yes to all' when prompted to convert the project.
+During conversion the custom build adds extra quotations for
+jk_logger_win32_message.mc. Right click on that file and select Properties.
+For Custom Build Step remove all the quotations around ${InputDir}
+and ${InputPath}.
+
+
+
+* Netware
+
+Buid the JK2 connector for NetWare platform.
+
+The current NWNGUmakefile uses the same build system as Apache2 self for NetWare target.
+Simply extract the downloaded archive, and follow the guideline which describes compilation of Apache2 self. 
+
+After you have compiled Apache2 (this is mandatory for now since the prebuild process must have finished) 
+you can simply call the makefile with 'make -f NWGNUmakefile', this builds the connector for Apache2 in a 
+release or debug subdirectory, dependent if you specify to build a debug version or not.
+
+It is recommended to use Metrowerks CodeWarrior compiler for now; although the connector builds with GCC 
+for NetWare, it is not tested yet if it works - there are known issues with a bitfied and alignment which 
+have to be solved.
+
+
diff --git a/connectors/jk/native2/CHANGES.html b/connectors/jk/native2/CHANGES.html
new file mode 100644
index 0000000..821d407
--- /dev/null
+++ b/connectors/jk/native2/CHANGES.html
@@ -0,0 +1,100 @@
+<h3>Changes in Jk2</h2>
+
+</h4>User visible</h4>
+
+<ul>
+<li>Logging. Jk2 will use the 'native' logging mechanism, wherever is available.
+For example in apache the logs will go to apache's error.log. If a 'native'
+implementation is not available jk will use the old mod_jk.log.
+
+<li>Apache(2) configuration 
+  <ul>
+    <li><b>"JkSet name value"</b> can be used to specify the same options as in
+workers.properties. The admin can choose to use only apache's httpd.conf, without
+any external file.</li>
+    <li>(not completed) <b>"JkWorker worker"</b> directive can be used in a Location 
+context to specify a webapp. This is particularly usefull for sites with many
+virtual hosts / webapps where the Location-based and native vhost  mapping is more 
+efficient.</li>
+    <li> 
+  </ul>
+
+<li>JNI worker. Most options on the jni worker are now optional, jk can detect
+and set them automcatically. This reduce the effort needed to configure jni.
+It is _required_ that LD_LIBRARY_PATH or equivalent is set coreclty, otherwise
+java will not start and apache will crash. ( on windows this may not be needed ).
+The user must set either as environment variables or in httpd.conf ( using JkSet )
+or in workers.properties: JAVA_HOME, TOMCAT_HOME ( or CATALINA_HOME ). Jk must
+be installed in the standard location ( modules/ or webapps/. The output from
+the vm will go in apache's error.log ( like all other jk output ) unless
+a special file is requested.
+
+</ul>
+
+<h4>Developer specific</h4>
+
+<ul>
+<li>Created an 'object' registry. All jk components will follow a common pattern
+for creation, and developers will be able to 'plug' their own 
+implementation and override the defaults. 
+
+<li>Memory manangement. Use pools more extensively, almost all mallocs have been
+replaced. 
+
+<li>Native pools. The pool is based on virtual functions and may use the native
+server mechanism. For Apache2 ( and as a default for servers not having a pool )
+we can use APR ( or the original jk_pool if APR is not available for a server ).
+
+<li>Native maps and apr based impl. jk2 maps now use virtual methods that allow
+server-specific implementation. That avoids copying the headers and attributes
+( and lazy access ). The apr based impl will be the default to be used for servers
+not providing a native map. ( we still use jk_map for object storage, as apr_table
+can be used only for string values )
+
+<li>Native loggers. Jk2 logger uses virtual methods and may use the 'native' 
+server-specific logging mechanism. The original file logger can be used
+for servers not providing a logger ( or until a specific logger is implemented ).
+
+<li>Reorganised and simplified data structures. Now most usefull information about
+workers is exposed in the core interfaces.
+
+<li>Pool reuse. In normal operation jk2 will operate in constant memory, the 
+endpoint pool will be reset and will not require a malloc per request. ( same is 
+true for apache2 request processing )
+
+<li>Method signatures. Jk2 uses the same 'patterns' as jni, with a jk_env as 
+first parameter, then 'this' (the pointer to the object ), then regular parameters.
+The same pattern is used no consistently in all methods.
+
+<li>JNI has been refactored. On file, jk_vmutil deals with the creation of the
+vm and guessing of all properties needed to create a java vm. It could be possible
+to create specialized instances of jk_vmutil for different vms ( the default 
+works for most ).
+
+<li>JNI now uses the channel abstraction to send/receive messages. In future we 
+could refine this to use a special 'marshalling' that will map 'messages' into
+method invocations. One big benefit is that we can now reuse all objects, no
+longer need to use strings ( and thus enable the solving of most i18n problems ).
+The code is also more 'uniform' and easier to extend.
+
+<li>JNI worker is no longer singleton and can be used to start multiple java 
+programms in the same process. Probably not very usefull for jk in particular, 
+but the restriction was not needed. 
+
+<li>(not completed) Error handling. The env parameter will provide a mechanism to
+pass error information up the stack ( eventually a stack trace ). It'll also   
+provide per/thread storage and a temp pool.
+
+</ul>
+
+<h4>Features</h4>
+ 
+<h4>Bug fixes</h4>
+
+<ul>
+<li>Initial fixed for thread safety issue in uriMap ( when rewriting is needed )
+
+</ul>
+
+
+
diff --git a/connectors/jk/native2/CHANGES.txt b/connectors/jk/native2/CHANGES.txt
new file mode 100644
index 0000000..b882443
--- /dev/null
+++ b/connectors/jk/native2/CHANGES.txt
@@ -0,0 +1,98 @@
+JAKARTA TOMCAT CONNECTORS 2 (JK2) CHANGELOG:            -*-text-*-
+Last modified at [$Date$]
+
+Changes in JK2 HEAD:
+    * APR is now mandatory, so channel.socket is now using APR and as such
+      channel.apr has been removed.
+    * Added the load balancer stickySession property. If set to 0
+      requests with servlet SESSION ID's can be routed to any Tomcat
+      worker. Default is 1, sessions are sticky.
+    * Port from jk of connect_timeout, reply_timeout, prepost_timeout (AJP13 PING/PONG).
+      You could set connectTimeout, replyTimeout and prepostTimeout for such purpose in ajp properties.
+      [Henri Gomez]
+    * Add hasinput method for channel, which will check if datas are
+      available on input channel (TC->WEBSERVER). 
+      [Henri Gomez]
+    * Make use apr_port_t instead of short to fix problem to have port higher than 32K.
+    * Make default port configurable (9009 on Netware, 8009 elsewhere)
+    * Fix problem when in Load-Balancing and POST 
+      [Henri Gomez].
+    * Add recovery strategy in LB mode, via noRecoveryIfRequestSent and noRecoveryIfHeaderSent options 
+      [Henri Gomez].
+    * Add a noErrorHeader parameter for lb worker, by default set to 1, to prevent jk2 to touch the Headers when an
+      error is detected (which broke Apache 2, mod_alias and ErrorDocument) 
+      [Henri Gomez].
+    * Forward correctly the content-type and as such fix problems with Apache 2 and mod_deflate for example 
+      [Henri Gomez].
+      
+      
+Changes with JK2 2.0.3:
+    * jk2 set correctly the content-type in Apache 2.0,
+      making it ready to works with mod_deflate and AddOutputFilterByType 
+      [Henri Gomez]
+      
+Changes with JK2 2.0.2:
+    * Fix the bug 14293. Thanks to Martin Kraemer for his help.
+      [Jean-Frederic Clere]
+    * Don't send initial chunk for chunked encoding, fix #14282
+      [Costin Manolache]
+    * Fix the POST data on JNI
+      [Mladen Turk]
+    * Remove the deprecated message for path
+      [Costin Manolache]
+    * Add the regular expressions to uriMap. The regex uris are differentiated
+      to normal one by starting with dollar ($) sign.
+      [Mladen Turk]
+    * Add the max_connections to the wajp13 worker.
+      [Mladen Turk]    
+    * Add the hostMap cache
+      [Mladen Turk] 
+    * Allow the lb:name scheme inside the [channel.xxx]
+      [Mladen Turk]
+    * Duplicate all global directives on each vhost that has inheritGlobals set.
+      Directives   are   created   using   createBean   only   if   not   found.
+      Beside directives, the webapps are duplicated to.
+      [Mladen Turk]
+      
+Changes with JK2 2.0.1:
+
+    * Tentative fix for 12346.
+      If an unrecoverable error happens ( for example when the client hits  stop
+      the server can't send more data   since the connection is lost )  we  need
+      to forcefully break the ajp13 connection, since tomcat can't know this and
+      will continue to send data.
+
+      This behavior is a result of the optimizations made for the 'normal' case,
+      i.e.  the fact  that tomcat  doesn't wait  for confirmation  when  sending
+      chunks of data.  Adding the roundtrip  will have big  negative performance
+      hit - and it's better to deal with the error cases.
+
+      Note that  the alternative  ( and  what seems  to happen  for apache  ) is
+      to ignore  the server  errors and  continue to  receive chunks  and ignore
+      them.  That  can save  the  ajp  connection -   with the  price  of having
+      tomcat  send useless data. For a  large file that  may be a  bad solution,
+      and  tomcat will not  be notified that the client had problems ( which may
+      be a usefull info ).
+
+      If  this  doesn't  fix  the  problem  -  please  send  me  logs  with  ajp
+      debug enabled. I can't reproduce it ( or run IIS ), but this is clearly  a
+      bug. [Costin Manolache] 
+
+    * Fix the logger_file      
+      Change the default logger to the jk2.log (was mod_jk.log)
+      Fix the ${serverRoot} replaceProperties.
+      Do not close log file if it is stderr.
+      [Mladen Turk]
+      
+    * Fix the  apr_socket default timeout  value, and the  send/recv that caused
+      wrong header readings. [Mladen Turk]
+      
+    * Fix and rewrite the hostMap. It was  a real mess. There was also a bug  in    
+      the  code  that  caused  host  mapping to  be  sensitive  to the  order of
+      directives in the config. [Mladen Turk]
+      
+    * Introduce the  timeout option for  the load balancer.  The timeout if  set    
+      will force the lb to cycle  through workers if all are in  the error_state
+      for the specified amount of  seconds. This  is useful for  situations when
+      the  TC is  overloaded and   refuses new  connections. The  lb  will  wait
+      and  after timeout will report 500 to the client. [Mladen Turk]
diff --git a/connectors/jk/native2/INSTALL.txt b/connectors/jk/native2/INSTALL.txt
new file mode 100644
index 0000000..e6cac83
--- /dev/null
+++ b/connectors/jk/native2/INSTALL.txt
@@ -0,0 +1,91 @@
+  APACHE INSTALLATION OVERVIEW
+
+  For complete documentation, see
+  http://jakarta.apache.org/tomcat/tomcat-4.1-doc/jk2/index.html
+  
+*** Quick Start - Apache 2 ***
+
+  In the following example Apache2 is installed in 
+  /usr/local/apache2 and the commands are executed in the
+  jakarta-tomcat-connectors/jk/native2 directory. 
+
+  $ ./configure --with-apxs2=PREFIX/bin/apxs
+  $ make
+  $ cd ../build/jk2/apache2
+  $ PREFIX/bin/apxs -n jk2 -i mod_jk2.so
+
+     NOTES: * Replace PREFIX with the filesystem path under which 
+              Apache should be installed.  A typical installation
+              might use "/usr/local/apache2" for PREFIX (without the
+              quotes).
+
+  Add the following to httpd:
+  LoadModule jk2_module modules/mod_jk2.so
+
+  Follow "Quick Start Apache-2.0 and Apache-1.3" below for the workers2.properties file.
+
+*** Quick Start Apache-2.0 and Apache-1.3 ***
+  Create a workers2.properties in conf (where httpd.conf is localised).
+  Put something like the following in the file:
+  [channel.socket:localhost:8009]
+  port=8009
+  host=127.0.0.1
+ 
+  [ajp13:localhost:8009]
+  channel=channel.socket:localhost:8009
+ 
+  [uri:/examples/*]
+  worker=ajp13:localhost:8009
+ 
+- Restart Apache.
+  Use apachectl for example PREFIX/bin/apachectl graceful
+  Access to http://localhost/examples/ should bring a Tomcat index page. 
+
+*** Quick Start - Apache 1.3 ***
+
+  Download apr and apr-util
+  wget http://www.apache.org/dist/apr/apr-0.9.4.tar.gz
+  wget http://www.apache.org/dist/apr/apr-util-0.9.4.tar.gz
+
+  NOTE: * Use a mirror to find the best location of apr see
+          http://www.apache.org/dyn/closer.cgi/apr/
+          (For me it gives http://www.apache.de/dist/apr/apr-0.9.4.tar.gz and
+           So I have done:
+           wget http://www.apache.de/dist/apr/apr-0.9.4.tar.gz
+           wget http://www.apache.de/dist/apr/apr-util-0.9.4.tar.gz
+          )
+
+  Extract the apr and apr-util sources:
+  gtar xvf apr-0.9.4.tar.gz
+  gtar xvf apr-util-0.9.4.tar.gz
+
+  Build and install mod_jk2:
+  $ ./configure --with-apxs=PREFIX/bin/apxs \
+                --with-apr=/absolute/path/to/apr-0.9.4 \
+                --with-apr-util=/absolute/path/to/apr-util-0.9.4
+  $ make
+  $ cd ../build/jk2/apache13
+  $ PREFIX/bin/apxs -n jk2 -i mod_jk2.so
+
+  Add the following to httpd.conf:
+  LoadModule jk2_module libexec/mod_jk2.so
+  AddModule mod_jk2.c 
+
+  Follow "Quick Start Apache-2.0 and Apache-1.3" above for the workers2.properties file.
+
+***  Quick Start - IIS ***
+  
+  Use the provided install4iss.js script file.
+  Put the isapi_redirector2.dll and install4iis.js to the path
+  <where you installed tomcat>/bin .
+  Copy the workers2.properties file to the path
+  <where you installed tomcat>/conf .
+  Now open the command prompt and cd to the tomcat bin directory.
+  
+  C:\Tomcat\bin> cscript install4iis.js
+  
+  The provided script will install the isapi filter and create the
+  virtual directory named /jakarta with execute permission.
+
+  Restart the WWW service and you are ready to use the isapi_redirector2.
+
diff --git a/connectors/jk/native2/Makefile.in b/connectors/jk/native2/Makefile.in
new file mode 100644
index 0000000..2a32695
--- /dev/null
+++ b/connectors/jk/native2/Makefile.in
@@ -0,0 +1,70 @@
+all: build
+
+build: @APR_BUILD@ jk2-build
+
+clean: @APR_CLEAN@ jk2-clean 
+
+build-apxs: @APR_BUILD@ jk2-build-apxs
+
+clean-apxs: @APR_CLEAN@ jk2-clean-apxs
+
+install-apxs: jk2-install-apxs
+
+jk2-build:
+	list='@WEBSERVERS@'; \
+	for i in $$list; do \
+		echo "Making $$target in $$i"; \
+		if test "$$i" != "."; then \
+		(cd $$i && $(MAKE)) || exit 1; \
+	fi; \
+	done;
+
+jk2-build-apxs:
+	list='@WEBSERVERS@'; \
+	for i in $$list; do \
+		echo "Making $$target in $$i"; \
+		if test "$$i" != "."; then \
+		(cd $$i && $(MAKE) -f Makefile.apxs) || exit 1; \
+	fi; \
+	done;
+
+jk2-install-apxs:
+	list='@WEBSERVERS@'; \
+	for i in $$list; do \
+		echo "Making $$target in $$i"; \
+		if test "$$i" != "."; then \
+		(cd $$i && $(MAKE) -f Makefile.apxs install) || exit 1; \
+	fi; \
+	done;
+
+jk2-clean:
+	list='@WEBSERVERS@'; \
+	for i in $$list; do \
+		echo "Making $$target in $$i"; \
+		if test "$$i" != "."; then \
+		(cd $$i && $(MAKE) clean) || exit 1; \
+	fi; \
+	done;
+
+jk2-clean-apxs:
+	list='@WEBSERVERS@'; \
+	for i in $$list; do \
+		echo "Making $$target in $$i"; \
+		if test "$$i" != "."; then \
+		(cd $$i && $(MAKE) -f Makefile.apxs clean) || exit 1; \
+	fi; \
+	done;
+
+apr-build:
+	( cd @APR_DIR@ && make && cd @APR_UTIL_DIR@ && make )
+	( cd @APR_DIR@ && make install && cd @APR_UTIL_DIR@ && make install )
+
+apr-clean:
+	( cd @APR_DIR@ && make clean && cd @APR_UTIL_DIR@ && make clean )
+
+apidocs: common/*.h
+	mkdir -p ./docs/api
+	../../scandoc/scandoc.pl -i ../../scandoc/template.pl -p \
+	./docs/api/ -dproject="mod_jk 2 Library" common/*.h common/*.c include/*.h
+
+
diff --git a/connectors/jk/native2/README.txt b/connectors/jk/native2/README.txt
new file mode 100644
index 0000000..c5da2b5
--- /dev/null
+++ b/connectors/jk/native2/README.txt
@@ -0,0 +1,63 @@
+README for JK2
+--------------
+
+JK2 is a refactoring of  JK and is much more  powerfull.  Even if it works  with
+Apache 1.3, JK2 has been developed with Apache 2.0 in mind, and is better suited
+for multi-threaded  servers like  IIS, NES/iPlanet.  It can  also be  embeded in
+other applications and used from java.
+
+JK2 improves  the modularity  and has  a better  separation between protocol and
+physical layer. As such JK2 support  fast unix-socket, and could be extended  to
+support others communications channels. It is better suited for JNI and may  use
+(in a future version) JDK 1.4  NIO. There is additional support for  monitoring,
+similar with  JMX in  java. A  module similar  with mod_status  is provided, and
+additional adapters  can be  used to  interface and  provide status  and runtime
+configuration.  The  configuration  has been  changed  to  follow the  component
+models. Multiple configuration sources can be  supported ( in additon to file  )
+providing better  integration with  the embeding  application. The  config layer
+uses the management layer APIs and  it can support persistence for changes  done
+via runtime configuration. 
+
+
+How to obtain the JK2 and Apache Portable Runtime sources:
+----------------------------------------------------------
+
+NOTE: If you downloaded a source distribution from our website or a mirror  (the
+file is called jakarta-tomcat-connectors-jk2-X.X.X.src.tar.gz) you don't need to
+obtain any other file. Please follow this chapter only if you want to obtain the
+latest CVS version of the sources.
+
+Check out the module sources from CVS using the following commands:
+
+    cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic login
+    (Logging in to anoncvs@cvs.apache.org)
+    CVS password: anoncvs
+    cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic \
+        checkout jakarta-tomcat-connectors
+
+Once  CVS downloads  the jakarta-tomcat-connectors  module sources,  we need  to
+download the  APR (Apache  Portable Runtime)  sources. To  do this  simply use a
+release version of APR:
+
+    - You can download it from http://www.apache.org/dist/apr/.
+    - The file is called apr-X.X.X.tar.gz
+
+You will also need the APR-UTIL (a companion library to APR). To do this  simply
+use a release version of APR:
+
+    - You can download it from http://www.apache.org/dist/apr/.
+    - The file is called apr-util-X.X.X.tar.gz
+
+When the APR sources are in place,  we need to create the configure scripts.  It
+is done by running the command:
+
+    ./buildconf.sh
+
+
+WARNING:
+---------
+
+The JK2  should be  considered initial-release  quality code.   It has  not been
+subjected to the  same stresses on  its stability and  security that the  mod_jk
+releases  have  enjoyed,  so  there is  a  greater  possibility  of undiscovered
+vulnerabilities to stability or security.
diff --git a/connectors/jk/native2/STATUS.txt b/connectors/jk/native2/STATUS.txt
new file mode 100644
index 0000000..ad5f428
--- /dev/null
+++ b/connectors/jk/native2/STATUS.txt
@@ -0,0 +1,26 @@
+JAKARTA TOMCAT CONNECTORS 2 (JK2) STATUS:			-*-text-*-
+Last modified at [$Date$]
+
+Release:
+
+    2.0.5   : in progress
+    2.0.4   : released March 29, 2004
+    2.0.3   : not released
+    2.0.2   : released November 27, 2002
+    2.0.1   : released October 6, 2002 
+    2.0.0   : released September 30, 2002 
+    
+RELEASE SHOWSTOPPERS:
+
+    
+    
+ 
+RELEASE NON-SHOWSTOPPERS BUT WOULD BE REAL NICE TO WRAP THESE UP:
+    
+    * Make the experimental service channel between connector and TC, that
+      will allow asynchronous communication, and will be used for various
+      management messages.
+
+STUFF FOR 2.1:
+
+    * Use the APR for as a System abstraction layer through the code.
\ No newline at end of file
diff --git a/connectors/jk/native2/build.xml b/connectors/jk/native2/build.xml
new file mode 100644
index 0000000..8066b46
--- /dev/null
+++ b/connectors/jk/native2/build.xml
@@ -0,0 +1,689 @@
+<?xml version="1.0" ?>
+
+<project name="jk_native2" default="main" basedir=".">
+
+  <description>Build the native component of jk.</description>
+  
+  <property environment="env" />
+
+  <property file="${user.home}/.ant.properties" />
+  <property file="${user.home}/build.properties" />
+  <property file="${user.home}/build.properties.${os.arch}.${os.name}" />
+  <property file="../build.properties.${os.arch}.${os.name}" />
+  <property file="../build.properties" />
+
+  <property name="arch.prefix" value="" />
+
+  <!-- ========== Local paths, overriden in build.properties ========== -->
+
+  <property name="netscape.home" location="${iplanet.home}/plugins" />
+  <property name="apache2.home" location="${env.APACHE2_HOME}" />
+  <!-- That's the default RedHat package -->
+  <property name="apache13.home" location="/usr" />
+  
+  <!-- Uncomment if you want to use APR in apache1.3 
+  <property name="APACHE13_APR" value="true" />
+    -->
+
+  <property name="apache2.include" value="${apache2.home}/include" />
+  <property name="apr.include" location="${apache2.include}" />
+ 
+  <!-- ========== Build options ========== -->
+
+  <property name="so.debug" value="true" />
+  <property name="so.optimize" value="false" />
+  <property name="so.profile" value="false" />
+
+  <!-- ========== local properties ========== -->
+ 
+  <!-- Base dir for jk sources -->
+  <property name="jk.src" location=".." />
+  <property name="jk.build" location="../build" />
+
+  <property name="native.dir" location="${jk.src}/native2" />
+
+  <property name="build.dir" location="${jk.build}/jk2" />
+  <property name="cpptask.jar" location="${jk.src}/../lib/cpptasks.jar" />
+
+  <path id="jkant" >
+    <pathelement location="${jk.build}/lib/jkant.jar"/>
+    <pathelement location="${cpptask.jar}"/>
+  </path>
+
+  <!-- ==================== Targets ==================== -->
+
+  <target name="main" depends="init,apache20,apache13,jni,iis,aolserver">
+  </target>
+
+  <target name="all" depends="jkant,main">
+  </target>
+
+  <target name="init.taskdef" >
+    <taskdef resource="META-INF/ant.tasks" 
+	     classpathref="jkant" />
+
+  </target>
+
+  <target name="guess.os" >
+    <echo message="build.properties ${os.arch}.${os.name}" />
+    <!-- What OS ( it'll determine the includes ) -->
+    <condition property="linux">
+       <equals arg1="${os.name}" arg2="Linux"/>
+    </condition>
+    <condition property="solaris">
+       <equals arg1="${os.name}" arg2="SunOS"/>
+    </condition>
+    <condition property="win32">
+      <os family="windows"/>
+    </condition>
+    <condition property="hpux">
+      <equals arg1="${os.name}" arg2="HP-UX"/>
+    </condition>
+    <!-- I believe they are using cross-compilation, so checking the os.name
+         doesn't help. We'll check if the NDK is installed instead -->
+    <condition property="netware">
+      <available file="novellndk.home" />
+    </condition>
+    <echo message="Linux:${linux} Win32:${win32} Netware:${netware} Solaris:${solaris} HPUX:${hpux}" />
+  </target>
+
+
+  <target name="guess.server" >
+    <!-- What servers do we have ? -->
+    <available property="apache2.detect" 
+               file="${apache2.home}" />
+    <condition property="apache13.detect" >
+       <or>
+        <available file="${apache13.home}/bin/htpasswd" />
+        <available file="${apache13.home}/bin/htpasswd.exe" />
+       </or>
+    </condition>
+    <available property="iis.detect" 
+               file="${mssdk.home}" />
+    <available property="iplanet.detect" 
+               file="${iplanet.home}" />
+    <available property="aolserver.detect" 
+               file="${aolserver.home}" />
+    <condition property="build.jni" >
+       <or>
+        <isset property="iis.detect"  />
+        <isset property="apache2.detect"  />
+       </or>
+    </condition>
+    <echo message="Apache2 ${apache2.home} ${apache2.detect}" />
+    <echo message="Apache13 ${apache13.home} ${apache13.detect}" />
+    <echo message="IIS ${iis.home} ${iis.detect}" />
+    <echo message="Iplanet ${iplanet.home} ${iplanet.detect}" />
+    <echo message="AOLserver ${aolserver.home} ${aolserver.detect}" />
+    <echo message="JNI ${build.jni}" />
+    <echo message="" />
+  </target>
+
+  <!-- Platform-specific intialization -->
+  <target name="init.os" depends="init.win32,init.netware" />
+
+  <target name="init.win32.properties" if="win32" >
+    <!-- Override it in build.properties if using windows and a 
+    different location. I think that's the defaul location for msdev -->
+    <property name="mssdk.home" 
+              location="c:/Program Files/Microsoft Visual Studio/VC98"/>
+
+    <property name="build.compiler.base" location="${mssdk.home}" />
+    <property name="build.compiler.cc" location="${mssdk.home}/bin/cl" />
+    <property name="build.compiler.ld" location="${mssdk.home}/bin/link" />
+    <echo message="MSDEV compiler: ${mssdk.home}" />
+    <uptodate property="mc.uptodate"  
+              targetfile="${build.dir}/jk_logger_win32_message.res">
+      <srcfiles dir="common" includes="*.mc"/>
+    </uptodate>
+  </target>
+
+  <target name="init.win32.mc" unless="mc.uptodate" if="win32">
+    <exec executable="${mssdk.home}/bin/mc" dir="common" >
+      <arg value="jk_logger_win32_message.mc" />
+    </exec>
+    <exec executable="${mssdk.home}/bin/rc" dir="common" >
+      <arg value="-r" />
+      <arg value="-fo"/>
+      <arg value="${build.dir}/jk_logger_win32_message.res"/>
+      <arg value="jk_logger_win32_message.rc" />
+    </exec>
+  </target>
+
+  <target name="init.win32" depends="init.win32.properties,init.win32.mc" if="win32" />
+
+  <target name="init.netware" if="netware" >
+    <property name="mw.home" location="d:/tools/mw/6.0" />
+    <property name="novellndk.home" location="d:/tools/novell/ndk/nwsdk" />
+
+    <property name="build.compiler.base" location="${mw.home}" />
+    <property name="build.compiler.cc" location="${build.compiler.base}/bin/mwccnlm" />
+    <property name="build.compiler.ld" location="${build.compiler.base}/bin/mwldnlm" />
+    <property name="novelllibc.dir" location="${novellndk.home}/libc" />
+    <echo message="MWCC compiler: ${mw.home}" />
+    <echo message="NDK: ${novellndk.home}" />
+  </target>
+
+
+  <target name="init" depends="init.taskdef,guess.os,init.os,guess.server" >
+    <mkdir dir="${build.dir}" />
+    <!-- set this to "" if you don't want the prefix. This will use the 
+          prefix in the target name, to allow multiple platforms to build in 
+          the same tree -->
+  </target>
+
+  <target name="jkant" >
+    <ant dir="${jk.src}" target="jkant"/>
+  </target>
+
+  <target name="apache20-cpptask" depends="init" if="apache2.detect">
+    <property name="ant.reuse.loader"  value="true" />
+
+    <taskdef resource="cpptasks.tasks" classpathref="jkant" />
+    <typedef resource="cpptasks.types" classpathref="jkant" />
+      
+    <property name="apr.include" location="${apache2.home}/include" />
+    <mkdir dir="${build.dir}/apache2${arch.prefix}" />
+    
+    <defineset id="jk2-defs">
+           <define name="_REENTRANT" />
+           <define name="CHUNK_SIZE" value="4096" />
+           <define name="USE_APACHE_MD5" />
+           <define name="HAS_APR" />
+           <define name="HAVE_JNI" />
+
+           <define name="HPUX11" if="hpux" />
+           <define name="WIN32" if="win32" />
+           <define name="_WINDOWS" if="win32" />
+           <define name="_MBCS" if="win32" />
+           <define name="_USRDLL" if="win32" />
+           <define name="MOD_JK2_EXPORTS" if="win32" />
+    </defineset>
+
+    <cc outtype="shared"
+        subsystem="console"
+        runtime="dynamic"
+        objdir="${build.dir}/apache2${arch.prefix}" 
+        outfile="${build.dir}/apache2${arch.prefix}/mod_jk2.so" >
+
+        <compiler name="msvc" if="use.msvc" >
+        </compiler>
+
+        <compiler name="gcc"  
+                  warnings="diagnostic" 
+                  debug="true" />
+        
+        <!-- Do we need this ? 
+        <linker name="gcc"  
+                debug="true">
+        </linker>
+        -->
+        
+        <fileset dir="." >
+          <include name="server/apache2/*.c" />
+	  <include name="common/*.c" />
+	  <include name="jni/*.c" />
+        </fileset>
+        
+        <includepath location="${native.dir}/common" />
+        <includepath location="${native.dir}/include" />
+
+        <sysincludepath location="${apache2.home}/include" />
+        <sysincludepath location="${apache2.home}/include/httpd" />
+	<sysincludepath location="${apr.include}" />
+	<sysincludepath location="${apr-util.include}" />
+
+	<sysincludepath location="${java.home}/../include" />
+        <sysincludepath location="${java.home}/../include/linux" if="linux" />
+        <sysincludepath location="${java.home}/../include/hp-ux" if="hpux" />        
+        <sysincludepath location="${java.home}/../include/win32" if="win32" />        
+        <sysincludepath location="${java.home}/../include/solaris" if="solaris" />        
+        <sysincludepath location="&quot;${mssdk.home}/include&quot;" if="win32"/>
+        
+        <defineset refid="jk2-defs"/>
+
+        <syslibset libs="libhttpd" if="win32" />
+        <syslibset libs="libapr" if="win32" />
+        <syslibset libs="libaprutil" if="win32" />
+        <!--
+        <libset libs="libhttpd.lib" if="win32" />
+        <libset libs="libapr.lib" if="win32" />
+        <libset libs="libaprutil.lib" if="win32" />
+         -->
+        <libset libs="advapi32.lib" if="win32" />
+        <libset libs="wsock32.lib" if="win32" />
+
+        <linkerArg value="/libpath:&quot;${apr.lib}&quot;" if="win32" />
+        <linkerArg value="/libpath:&quot;${apr-util.lib}&quot;" if="win32" />
+        <linkerArg value="/libpath:&quot;${apache2.home}/lib&quot;" if="win32" />
+        <linkerArg value="/libpath:&quot;${mssdk.home}/lib&quot;" if="win32" />
+        <linkerArg value="/libpath:&quot;${mssdk.home}/../Common/MSDev98/bin&quot;" if="win32" />
+    </cc>
+    
+    <!-- Missing in cc:
+          - profile: can be implemented with flags
+          - depends: can be implemented using ant built-ins
+          - Netware/MWCC: probably easy to port.
+         Bugs: 
+          - libtool doesn't seem to work with multiple files ( easy )
+          - can't specify mod_jk.so as name ( will generate libmod_jk.so )
+          - Classpath problems - can't use types if the jar is not in the CP. Workaround with
+            a special <cc-init> task.
+          - 'history.xml' seems to create major problems ( compile-related ? )
+     -->
+  </target>
+  
+  <target name="apache20" depends="init" if="apache2.detect">
+    <mkdir dir="${build.dir}/apache2${arch.prefix}" />
+    <so sofile="mod_jk2" 
+	buildDir="${build.dir}/apache2${arch.prefix}"
+	optimize="${so.optimize}"
+	debug="${so.debug}"
+	taskDebug="0"
+	profile="${so.profile}"	>
+      <def name="_REENTRANT" />
+      <def name="CHUNK_SIZE" value="4096" 
+	   info="Read/Write buffer size" />
+      <def name="USE_APACHE_MD5" 
+	   info="Use the MD5 implementation that is part of apache2" />
+      <def name="HAS_APR"
+	   info="Allow APR specific extensions" />
+      <def name="HAVE_JNI" 
+	   info="Jni worker" />
+      <def name="HPUX11" if="hpux" />
+      <def name="WIN32" if="win32" />
+      <def name="_WINDOWS" if="win32" />
+      <def name="_MBCS" if="win32" />
+      <def name="_USRDLL" if="win32" />
+      <def name="MOD_JK2_EXPORTS" if="win32" />
+      <src dir=".">
+	<include name="server/apache2/*.c" />
+	<include name="common/*.c" />
+	<include name="jni/*.c" />
+      </src>
+      <includes>
+	<include name="${native.dir}/common" />
+	<include name="${apache2.include}" />
+	<include name="${apr.include}" />
+	<include name="${apr-util.include}" />
+	<include name="${native.dir}/include" />
+	<include name="${java.home}/../include" />
+        <include name="${java.home}/../include/linux" if="linux" />
+        <include name="${java.home}/../include/hp-ux" if="hpux" />        
+        <include name="${java.home}/../include/win32" if="win32" />        
+        <include name="${java.home}/../include/solaris" if="solaris" />        
+        <include name="&quot;${mssdk.home}/include&quot;" if="win32"/>
+      </includes>
+      <depends>
+	<fileset dir="${native.dir}/common" includes="*.h" />
+	<fileset dir="${native.dir}/include" includes="*.h" />
+      </depends>
+      <resource fileName="../jk_logger_win32_message.res" if="win32" />
+      <import fileName="libhttpd.lib" if="win32" />
+      <import fileName="libapr.lib" if="win32" />
+      <import fileName="libaprutil.lib" if="win32" />
+      <import fileName="advapi32.lib" if="win32" />
+      <import fileName="wsock32.lib" if="win32" />
+      <import fileName="shell32.lib" if="win32" />
+      <import fileName="kernel32.lib" if="win32" />
+
+      <linkOpt value="/libpath:&quot;${apr.lib}&quot;" if="win32" />
+      <linkOpt value="/libpath:&quot;${apr-util.lib}&quot;" if="win32" />
+      <linkOpt value="/libpath:&quot;${apache2.home}/lib&quot;" if="win32" />
+      <linkOpt value="/libpath:&quot;${mssdk.home}/lib&quot;" if="win32" />
+      <linkOpt value="/libpath:&quot;${mssdk.home}/../Common/MSDev98/bin&quot;" if="win32" />
+    </so>
+  </target>
+  
+  <target name="aolserver" description="Build nsjk2 module for AOLserver" depends="init" if="aolserver.detect">
+    <property name="apr.include" location="${apache2.home}/include" />
+    <mkdir dir="${build.dir}/aolserver${arch.prefix}" />
+
+    <so sofile="nsjk2"
+	buildDir="${build.dir}/aolserver${arch.prefix}"
+	optimize="${so.optimize}"
+	debug="${so.debug}"
+	taskDebug="0"
+	profile="${so.profile}"	>
+      <def name="_REENTRANT" />
+      <def name="CHUNK_SIZE" value="4096" 
+	   info="Read/Write buffer size" />
+
+	<!-- Use MD5 from APU
+      <def name="USE_APACHE_MD5" 
+	   info="Use the MD5 implementation that is part of apache2" />
+	-->
+
+      <def name="HAS_APR"
+	   info="Allow APR specific extensions" />
+      <def name="HAVE_JNI" 
+	   info="Jni worker" />
+      <def name="HPUX11" if="hpux" />
+      <def name="WIN32" if="win32" />
+      <def name="_WINDOWS" if="win32" />
+      <def name="_MBCS" if="win32" />
+      <def name="_USRDLL" if="win32" />
+      <def name="MOD_JK2_EXPORTS" if="win32" />
+      <src dir=".">
+	<include name="server/aolserver/*.c" />
+	<include name="common/*.c" />
+	<include name="jni/*.c" />
+      </src>
+      <includes>
+	<include name="${native.dir}/common" />
+	<include name="${aolserver.home}/include" />
+	<include name="${aolserver.home}" />
+	<include name="${apr.include}" />
+	<include name="${apr-util.include}" />
+	<include name="${native.dir}/include" />
+	<include name="${java.home}/../include" />
+        <include name="${java.home}/../include/linux" if="linux" />
+        <include name="${java.home}/../include/hp-ux" if="hpux" />        
+        <include name="${java.home}/../include/win32" if="win32" />        
+        <include name="${java.home}/../include/solaris" if="solaris" />        
+        <include name="&quot;${mssdk.home}/include&quot;" if="win32"/>
+      </includes>
+      <depends>
+	<fileset dir="${native.dir}/common" includes="*.h" />
+	<fileset dir="${native.dir}/include" includes="*.h" />
+      </depends>
+      <resource fileName="../jk_logger_win32_message.res" if="win32" />
+      <import fileName="libhttpd.lib" if="win32" />
+      <import fileName="libapr.lib" if="win32" />
+      <import fileName="libaprutil.lib" if="win32" />
+      <import fileName="advapi32.lib" if="win32" />
+      <import fileName="wsock32.lib" if="win32" />
+      <import fileName="shell32.lib" if="win32" />
+      <import fileName="kernel32.lib" if="win32" />
+
+      <linkOpt value="/libpath:&quot;${apr.lib}&quot;" if="win32" />
+      <linkOpt value="/libpath:&quot;${apr-util.lib}&quot;" if="win32" />
+      <linkOpt value="/libpath:&quot;${apache2.home}/lib&quot;" if="win32" />
+      <linkOpt value="/libpath:&quot;${mssdk.home}/lib&quot;" if="win32" />
+      <linkOpt value="/libpath:&quot;${mssdk.home}/../Common/MSDev98/bin&quot;" if="win32" />
+      <linkOpt value="-L${apr.lib}" />
+      <linkOpt value="-lapr-0" />
+      <linkOpt value="-laprutil-0" />
+      <linkOpt value="-lexpat" />
+    </so>
+  </target>
+  
+  <target name="jni" depends="init" if="build.jni">
+    <mkdir dir="${build.dir}/jni${arch.prefix}" />
+    <!-- can be overriden -->
+    <property name="apr.home" location="${apache2.home}" />
+    <property name="apr.include" location="${apr.home}/include" />
+    <property name="apr.lib" location="${apr.home}/lib" />
+
+    <so sofile="libjkjni" 
+	buildDir="${build.dir}/jni${arch.prefix}" 
+	optimize="${so.optimize}"
+	debug="${so.debug}"
+	profile="${so.profile}" >
+      <src dir="${native.dir}">
+	<include name="jni/*.c" />
+	<include name="common/*.c" />
+        <exclude name="common/jk_nwmain.c" unless="netware" />
+      </src>
+      <includes>
+	<include name="${native.dir}/common" />
+	<include name="${native.dir}/jni" />
+	<include name="${native.dir}/include" />
+	<include name="${java.home}/../include" />
+	<include name="${build.compiler.base}/include" />
+	<include name="${apr.include}" />
+	<include name="${apr.include}/httpd" />
+        
+        <!-- Platform specific includes -->
+	<include name="${novellndk.home}/include/nlm" if="netware" />
+	<include name="${novellndk.home}/include" if="netware" />
+        <include name="${java.home}/../include/netware" if="netware" />
+        
+        <include name="${java.home}/../include/win32" if="win32" />
+        <include name="${java.home}/../include/solaris" if="solaris" />        
+        <include name="${java.home}/../include/linux" if="linux" />
+        <include name="${java.home}/../include/hp-ux" if="hpux" />        
+      </includes>
+      <depends>
+	<fileset dir="${native.dir}/common" includes="*.h" />
+	<fileset dir="${native.dir}/include" includes="*.h" />
+      </depends>
+      <resource fileName="../jk_logger_win32_message.res" if="win32"/>
+
+      <!-- Platform-specific tags. We should have a "readhat" test too -->
+      <linkOpt value="-lcrypt" if="linux" />
+      <linkOpt value="-L${apr.lib}" if="linux" />
+      <linkOpt value="-lapr-0" if="linux" />
+
+      <linkOpt value="-L${apr.lib}" if="solaris" />
+      <linkOpt value="-lapr-0" if="solaris" />
+
+      <linkOpt value="-L${apr.lib}" if="hpux" />
+      <linkOpt value="-lapr-0" if="hpux" />
+
+      <altSoFile value="jni_conn" if="netware" />
+      
+      <def name="N_PLAT_NLM" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="NETWARE" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="XP_NETWARE" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="HAS_APR" info="JNI is now based on APR" />
+      <def name="_REENTRANT" />
+      <def name="HAVE_JNI" />
+           
+      <import fileName="${novellndk.home}/imports/clib.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/lib0.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/nlmlib.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/threads.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/socklib.imp" if="netware" />
+      <export fileName="${native.dir}/jni/jk_jnicb.exp" if="netware" />
+      <linkOpt value="-desc &quot;JNI Natives for Tomcat&quot;" if="netware" />
+      <linkOpt value="-screenname &quot;System Console&quot;" if="netware" />
+      <linkOpt value="-nlmversion 1,20,2" if="netware" />
+      <linkOpt value="-threadname &quot;JK_JNI Thread&quot;" if="netware" />
+      <linkOpt value="-stacksize 64000" if="netware" />
+      
+      <def name="HPUX11" if="hpux" />
+      <def name="WIN32" if="win32" />
+      <def name="NDEBUG" if="win32.release" />
+      <def name="_DEBUG" if="win32.debug" />
+      <def name="_WINDOWS" if="win32" />
+      <def name="_MBCS" if="win32" />
+      <def name="_USRDLL" if="win32" />
+      <def name="JNI_CONNECT_EXPORTS" if="win32" />
+      <def name="HAVE_SIGNAL" unless="win32" />
+
+      <import fileName="libhttpd.lib" if="win32" />
+      <import fileName="libapr.lib" if="win32" />
+      <import fileName="libaprutil.lib" if="win32" />
+      <import fileName="advapi32.lib" if="win32" />
+      <import fileName="wsock32.lib" if="win32" />
+
+      <linkOpt value="/libpath:&quot;${apr.lib}&quot;" if="win32" />
+      <linkOpt value="/libpath:&quot;${apr-util.lib}&quot;" if="win32" />
+      <linkOpt value="/libpath:&quot;${apache2.home}/lib&quot;" if="win32" />
+      <linkOpt value="/libpath:&quot;${mssdk.home}/lib&quot;" if="win32" />
+      <linkOpt value="/libpath:&quot;${mssdk.home}/../Common/MSDev98/bin&quot;" if="win32" />
+    </so>
+  </target>
+
+
+  <target name="apache13" depends="init" if="apache13.detect">
+    <mkdir dir="${jk.build}/jk2/apache13${arch.prefix}" />
+    <so sofile="mod_jk2" 
+	buildDir="${jk.build}/jk2/apache13${arch.prefix}"
+	optimize="${so.optimize}"
+	debug="${so.debug}"
+	profile="${so.profile}">
+
+      <src dir=".">
+	<include name="server/apache13/*.c" />
+	<include name="common/*.c" />
+        <exclude name="jk_nwmain.c" unless="netware" />
+      </src>
+      <includes>
+	<include name="${native.dir}/include" />
+	<include name="${native.dir}/common" />
+	<include name="${build.compiler.base}/include" />
+	<include name="${apache13.home}/include" />
+        <!-- Redhat package -->
+	<include name="${apache13.home}/include/apache" />
+	<include name="${native.dir}/include" />
+	<include name="${java.home}/../include" />
+
+        <!-- Platform specific includes -->
+	<include name="${apache13.home}/os/netware" if="netware" />
+	<include name="${novellndk.home}/include/nlm" if="netware" />
+	<include name="${novellndk.home}/include" if="netware" />
+	<include name="${novellndk.home}/include/winsock" if="netware" />
+        <include name="${java.home}/../include/netware" if="netware" />
+                 
+	<include name="${apache13.home}/os/win32" if="win32" />
+        <include name="${java.home}/../include/win32" if="win32" />
+        <include name="${native.dir}/common" if="win32" />
+        <include name="${java.home}/../include/solaris" if="solaris" />        
+        <include name="${java.home}/../include/hp-ux" if="hpux" />        
+        <include name="${java.home}/../include/linux" if="linux" />
+      </includes>
+      <depends>
+	<fileset dir="${native.dir}/common" includes="*.h" />
+      </depends>
+
+      <resource fileName="../jk_logger_win32_message.res" if="win32"/>
+
+      <def name="EAPI" info="Building with EAPI support " />
+      <def name="_REENTRANT" />
+      <def name="CHUNK_SIZE" value="4096" />
+
+      <!-- Platform-specific tags -->
+      <def name="N_PLAT_NLM" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="NETWARE" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="XP_NETWARE" if="netware"
+	   info="Building for NetWare platform" />
+      <def name="USE_SPRINTF" if="netware"
+	   info="Use the sprintf function to build strings" />
+           
+      <def name="HAVE_MMAP" unless="win32" />
+      <def name="WIN32" if="win32" />
+      <def name="_WINDOWS" if="win32" />
+      <def name="_MBCS" if="win32" />
+      <def name="_USRDLL" if="win32" />
+      <def name="MOD_JK_EXPORTS" if="win32" />
+      <def name="HPUX11" if="hpux" />
+
+      
+      <nlmmodule value="apache" if="netware" />
+      <import fileName="${novellndk.home}/imports/clib.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/lib0.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/nlmlib.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/threads.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/socklib.imp" if="netware" />
+      <import fileName="${novellndk.home}/imports/ws2nlm.imp" if="netware" />
+      <import fileName="${apache13.home}/os/netware/ApacheCore.imp" if="netware" />
+      <export symbol="jk2_module" if="netware" />
+      <linkOpt value="-desc &quot;Apache 1.3 plugin for Tomcat&quot;" if="netware" />
+      <linkOpt value="-screenname &quot;System Console&quot;" if="netware" />
+      <linkOpt value="-nlmversion 1,20,2" if="netware" />
+      <linkOpt value="-threadname &quot;mod_jk Thread&quot;" if="netware" />
+      <linkOpt value="-stacksize 64000" if="netware" />
+      
+      <export symbol="jk2_module" if="win32" />
+      <import fileName="ApacheCore.lib" if="win32" />
+      <import fileName="wsock32.lib" if="win32" />
+      <import fileName="advapi32.lib" if="win32" />
+      <linkOpt value="/libpath:&quot;${build.compiler.base}/lib&quot;" 
+	       if="win32" />
+      <linkOpt value="/libpath:&quot;${apache13.home}/libexec&quot;" 
+	        if="win32" />
+    </so>
+  </target>
+
+  <target name="iis" depends="init" if="iis.detect">
+    <mkdir dir="${build.dir}/isapi" />
+    <so sofile="isapi_redirector2" 
+        buildDir="${build.dir}/isapi"
+        optimize="${so.optimize}"
+        debug="${so.debug}"
+        profile="${so.profile}">
+        
+      <src dir=".">
+        <include name="server/isapi/*.c" />
+        <include name="common/*.c" />
+        <exclude name="jk_nwmain.c" unless="netware" />
+	<include name="jni/*.c" />
+        <exclude name="jk_apr.c" unless="netware" />
+      </src>
+      <includes>
+        <include name="${java.home}/../include" />
+        <include name="${apr.include}/../include" />
+	<include name="${native.dir}/include" />
+        <include name="${java.home}/../include/win32" />
+        <include name="${native.dir}/common" />
+        <include name="${native.dir}/server/isapi" />
+        <include name="${build.compiler.base}/include" />
+        <!--include name="&quot;${build.compiler.base}/include&quot;" /-->
+        <include name="&quot;${mssdk.home}/include&quot;"/>
+      </includes>
+      <depends>
+        <fileset dir="${native.dir}/common" includes="*.h" />
+      </depends>
+      <resource fileName="../jk_logger_win32_message.res"/>
+
+      <!-- Platform-specific tags -->
+      <def name="WIN32" />
+      <def name="_WINDOWS"  />
+      <def name="NDEBUG" if="win32.release" />
+      <def name="_DEBUG" if="win32.debug" />
+      <def name="_MBCS" />
+      <def name="_USRDLL" />
+      <def name="ISAPI_EXPORTS" />
+      <def name="HAS_APR" />
+      <def name="HAVE_JNI" 
+	   info="Jni worker" />
+
+      <import fileName="advapi32.lib" />
+      <import fileName="wsock32.lib" />
+      <import fileName="libapr.lib" if="win32" />
+      <import fileName="libaprutil.lib" if="win32" />
+      <export symbol="HttpFilterProc"/>
+      <export symbol="GetFilterVersion"/>
+      <export symbol="GetExtensionVersion"/>
+      <export symbol="HttpExtensionProc"/>
+      <export symbol="TerminateFilter"/>
+      <export symbol="TerminateExtension"/>
+      <linkOpt value="/libpath:&quot;${mssdk.home}/lib&quot;" if="win32" />
+      <linkOpt value="/libpath:&quot;${mssdk.home}/../Common/MSDev98/bin&quot;" if="win32" />
+      <linkOpt value="/libpath:&quot;${apr.lib}&quot;" if="win32" />
+    </so>
+  </target>
+
+  <target name="clean" >
+    <delete >
+      <fileset dir="${build.dir}">
+	<include name="**/*.o"/>
+	<include name="**/*.so"/>
+	<include name="**/*.lo"/>
+	<include name="**/*.la"/>
+	<include name="**/*.a"/>
+	<include name="**/.libs"/>
+	<include name="**/*.lib"/>
+	<include name="**/*.nlm"/>
+	<include name="**/*.map"/>
+	<include name="**/*.sym"/>
+	<include name="**/*.NCV"/>
+	<include name="**/*.exp"/>
+	<include name="**/*.pdb"/>
+	<include name="**/*.opt"/>
+	<include name="**/*.def"/>
+	<include name="**/*.obj"/>
+	<include name="**/*.dll"/>
+	<include name="**/*.res"/>
+      </fileset>
+    </delete>
+  </target>
+
+</project>
diff --git a/connectors/jk/native2/buildconf.sh b/connectors/jk/native2/buildconf.sh
new file mode 100755
index 0000000..b5058c4
--- /dev/null
+++ b/connectors/jk/native2/buildconf.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+echo "rm autom4te.cache"
+rm -rf autom4te.cache
+
+echo "libtoolize --force --automake --copy"
+libtoolize --force --automake --copy
+echo "aclocal"
+aclocal
+echo "automake --copy --add-missing"
+automake --copy --add-missing
+echo "autoconf"
+autoconf
+
+echo "rm autom4te.cache"
+rm -rf autom4te.cache
diff --git a/connectors/jk/native2/common/jk_channel.c b/connectors/jk/native2/common/jk_channel.c
new file mode 100644
index 0000000..60b20aa
--- /dev/null
+++ b/connectors/jk/native2/common/jk_channel.c
@@ -0,0 +1,116 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Common methods to all channels.
+ * 
+ * @author: Costin Manolache
+ */
+
+#include "jk_map.h"
+#include "jk_env.h"
+#include "jk_channel.h"
+#include "jk_global.h"
+
+#include <string.h>
+#include "jk_registry.h"
+
+/** Common attributes for all channels
+ */
+int JK_METHOD jk2_channel_setAttribute(jk_env_t *env,
+                                       jk_bean_t *mbean,
+                                       char *name, void *valueP)
+{
+    jk_channel_t *ch = (jk_channel_t *)mbean->object;
+    char *value = valueP;
+
+    if (strcmp("debug", name) == 0) {
+        ch->mbean->debug = atoi(value);
+    }
+    else if (strcmp("disabled", name) == 0) {
+        ch->mbean->disabled = atoi(value);
+        if (ch->worker != NULL)
+            ch->worker->mbean->disabled = ch->mbean->disabled;
+    }
+    else {
+        if (ch->worker != NULL) {
+            return ch->worker->mbean->setAttribute(env, ch->worker->mbean,
+                                                   name, valueP);
+        }
+        return JK_ERR;
+    }
+    return JK_OK;
+}
+
+
+
+/** Called by java ( local or remote ). 
+ */
+int JK_METHOD jk2_channel_invoke(jk_env_t *env, jk_bean_t *bean,
+                                 jk_endpoint_t *ep, int code, jk_msg_t *msg,
+                                 int raw)
+{
+    jk_channel_t *ch = (jk_channel_t *)bean->object;
+    int rc = JK_OK;
+
+    if (ch->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG, "ch.%d() \n", code);
+
+    switch (code) {
+    case CH_OPEN:{
+            if (ch->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG, "ch.open()\n");
+            if (ch->open != NULL)
+                rc = ch->open(env, ch, ep);
+            return rc;
+        }
+    case CH_CLOSE:{
+            if (ch->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG, "ch.close()\n");
+            if (ch->close != NULL)
+                rc = ch->close(env, ch, ep);
+            return rc;
+        }
+    case CH_READ:{
+            if (ch->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG, "ch.recv()\n");
+            if (ch->recv != NULL)
+                rc = ch->recv(env, ch, ep, msg);
+            if (rc == JK_OK)
+                return JK_INVOKE_WITH_RESPONSE;
+            return rc;
+        }
+    case CH_WRITE:{
+            if (ch->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG, "ch.send()\n");
+            if (ch->serverSide)
+                msg->serverSide = JK_TRUE;
+            if (ch->send != NULL)
+                rc = ch->send(env, ch, ep, msg);
+            return rc;
+        }
+    case CH_HASINPUT:{
+            if (ch->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG, "ch.hasinput()\n");
+            if (ch->serverSide)
+                msg->serverSide = JK_TRUE;
+            if (ch->hasinput != NULL)
+                rc = ch->hasinput(env, ch, ep, 1000);   /* Well we should handle timeout better isn't it ? */
+            return rc;
+        }
+    }                           /* switch */
+    return JK_ERR;
+}
diff --git a/connectors/jk/native2/common/jk_channel_apr_socket.c b/connectors/jk/native2/common/jk_channel_apr_socket.c
new file mode 100644
index 0000000..0a3acfa
--- /dev/null
+++ b/connectors/jk/native2/common/jk_channel_apr_socket.c
@@ -0,0 +1,538 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Channel using APR sockets.
+ *
+ * @author:  Gal Shachor <shachor@il.ibm.com>                           
+ * @author: Costin Manolache
+ * @author: Jean-Frederic Clere <jfrederic.clere@fujitsu-siemens.com>
+ */
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_env.h"
+#include "jk_channel.h"
+#include "jk_global.h"
+#include "jk_registry.h"
+
+/** Information specific for the socket channel
+ */
+struct jk_channel_apr_private
+{
+    int ndelay;
+    apr_sockaddr_t *addr;
+    char *host;
+    apr_port_t port;
+    int keepalive;
+    int timeout;
+};
+
+typedef struct jk_channel_apr_private jk_channel_apr_private_t;
+
+/*
+  We use the _privateInt field directly. Long term we can define our own
+  jk_channel_apr_t structure and use the _private field, etc - but we 
+  just need to store an int.
+
+  XXX We could also use properties or 'notes'
+*/
+
+static int JK_METHOD jk2_channel_apr_resolve(jk_env_t *env, char *host,
+                                             apr_port_t port,
+                                             jk_channel_apr_private_t * rc);
+
+static int JK_METHOD jk2_channel_apr_close(jk_env_t *env, jk_channel_t *_this,
+                                           jk_endpoint_t *endpoint);
+
+
+static char *jk2_channel_apr_socket_getAttributeInfo[] =
+    { "host", "port", "keepalive", "timeout", "nodelay", "graceful",
+    "debug", "disabled", NULL
+};
+static char *jk2_channel_apr_socket_setAttributeInfo[] =
+    { "host", "port", "keepalive", "timeout", "nodelay", "graceful",
+    "debug", "disabled", NULL
+};
+
+static int JK_METHOD jk2_channel_apr_setProperty(jk_env_t *env,
+                                                 jk_bean_t *mbean,
+                                                 char *name, void *valueP)
+{
+    jk_channel_t *ch = (jk_channel_t *)mbean->object;
+    char *value = valueP;
+    jk_channel_apr_private_t *socketInfo =
+        (jk_channel_apr_private_t *) (ch->_privatePtr);
+
+    if (strcmp("host", name) == 0) {
+        socketInfo->host = value;
+    }
+    else if (strcmp("port", name) == 0) {
+        socketInfo->port = atoi(value);
+    }
+    else if (strcmp("keepalive", name) == 0) {
+        socketInfo->keepalive = atoi(value);
+    }
+    else if (strcmp("timeout", name) == 0) {
+        socketInfo->timeout = atoi(value);
+    }
+    else if (strcmp("nodelay", name) == 0) {
+        socketInfo->ndelay = atoi(value);
+    }
+    else {
+        return jk2_channel_setAttribute(env, mbean, name, valueP);
+    }
+    return JK_OK;
+}
+
+static void *JK_METHOD jk2_channel_apr_socket_getAttribute(jk_env_t *env,
+                                                           jk_bean_t *bean,
+                                                           char *name)
+{
+    jk_channel_t *ch = (jk_channel_t *)bean->object;
+    jk_channel_apr_private_t *socketInfo =
+        (jk_channel_apr_private_t *) (ch->_privatePtr);
+
+    if (strcmp(name, "name") == 0) {
+        return bean->name;
+    }
+    else if (strcmp("host", name) == 0) {
+        return socketInfo->host;
+    }
+    else if (strcmp("port", name) == 0) {
+        return jk2_env_itoa(env, socketInfo->port);
+    }
+    else if (strcmp("nodelay", name) == 0) {
+        return jk2_env_itoa(env, socketInfo->ndelay);
+    }
+    else if (strcmp("keepalive", name) == 0) {
+        return jk2_env_itoa(env, socketInfo->keepalive);
+    }
+    else if (strcmp("timeout", name) == 0) {
+        return jk2_env_itoa(env, socketInfo->timeout);
+    }
+    else if (strcmp("graceful", name) == 0) {
+        return jk2_env_itoa(env, ch->worker->graceful);
+    }
+    else if (strcmp("debug", name) == 0) {
+        return jk2_env_itoa(env, ch->mbean->debug);
+    }
+    else if (strcmp("disabled", name) == 0) {
+        return jk2_env_itoa(env, ch->mbean->disabled);
+    }
+    return NULL;
+}
+
+
+/** resolve the host IP ( jk_resolve ) and initialize the channel.
+ */
+static int JK_METHOD jk2_channel_apr_init(jk_env_t *env, jk_bean_t *chB)
+{
+    jk_channel_t *ch = chB->object;
+    jk_channel_apr_private_t *socketInfo =
+        (jk_channel_apr_private_t *) (ch->_privatePtr);
+    int rc;
+
+    if (socketInfo->host == NULL) {
+        char *localName = ch->mbean->localName;
+
+        char *portIdx = strchr(localName, ':');
+
+        if (portIdx == NULL || portIdx[1] == '\0') {
+            socketInfo->port = AJP13_DEF_PORT;
+        }
+        else {
+            portIdx++;
+            socketInfo->port = atoi(portIdx);
+        }
+
+        if (socketInfo->host == NULL) {
+            socketInfo->host =
+                ch->mbean->pool->calloc(env, ch->mbean->pool,
+                                        strlen(localName) + 1);
+            if (portIdx == NULL) {
+                strcpy(socketInfo->host, localName);
+            }
+            else {
+                strncpy(socketInfo->host, localName, portIdx - localName - 1);
+            }
+        }
+    }
+
+    if (socketInfo->port <= 0)
+        socketInfo->port = AJP13_DEF_PORT;
+
+    if (socketInfo->host == NULL)
+        socketInfo->host = AJP13_DEF_HOST;
+
+    rc = jk2_channel_apr_resolve(env, socketInfo->host, socketInfo->port,
+                                 socketInfo);
+    if (rc != JK_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, "jk2_channel_apr_init: "
+                      "can't resolve %s:%d errno=%d\n", socketInfo->host,
+                      socketInfo->port, errno);
+    }
+
+    return rc;
+}
+
+/*
+ * Wait input event on socket for timeout ms
+ */
+static int JK_METHOD jk2_channel_apr_hasinput(jk_env_t *env,
+                                              jk_channel_t *ch,
+                                              jk_endpoint_t *endpoint,
+                                              int timeout)
+{
+    /*
+     * Should implements the APR select/poll for socket here
+     */
+    return (JK_TRUE);
+}
+
+
+/** private: resolve the address on init
+ */
+static int JK_METHOD jk2_channel_apr_resolve(jk_env_t *env,
+                                             char *host, apr_port_t port,
+                                             jk_channel_apr_private_t * rc)
+{
+    int err;
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "channelApr.resolve(): create AF_NET  %s %d\n", host, port);
+    err = apr_sockaddr_info_get(&rc->addr, host, APR_UNSPEC, port, 0,
+                                (apr_pool_t *) env->globalPool->_private);
+    if (err != APR_SUCCESS) {
+        return err;
+    }
+    return JK_OK;
+}
+
+
+/** connect to Tomcat (jk_open_socket)
+ */
+static int JK_METHOD jk2_channel_apr_open(jk_env_t *env,
+                                          jk_channel_t *ch,
+                                          jk_endpoint_t *endpoint)
+{
+    jk_channel_apr_private_t *socketInfo =
+        (jk_channel_apr_private_t *) (ch->_privatePtr);
+
+    apr_sockaddr_t *remote_sa = socketInfo->addr;
+    int ndelay = socketInfo->ndelay;
+    int keepalive = socketInfo->keepalive;
+
+    apr_socket_t *sock;
+    apr_status_t ret;
+    apr_int32_t timeout =
+        (apr_int32_t) (socketInfo->timeout * APR_USEC_PER_SEC);
+    char msg[128];
+    int connected = 0;
+
+    while (remote_sa && !connected) {
+        if ((ret = apr_socket_create(&sock, remote_sa->family, SOCK_STREAM,
+#if (APR_MAJOR_VERSION > 0)
+                                     APR_PROTO_TCP,
+#endif
+                                     (apr_pool_t *) env->globalPool->
+                                     _private))
+            != APR_SUCCESS) {
+            if (remote_sa->next) {
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "channelApr.open(): error %d creating socket to %s\n",
+                              ret, socketInfo->host);
+            }
+            else {
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "channelApr.open(): error %d creating socket to %s\n",
+                              ret, socketInfo->host);
+            }
+            remote_sa = remote_sa->next;
+            continue;
+        }
+        /* store the channel information */
+        endpoint->channelData = sock;
+
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "channelApr.open(): create tcp socket %d\n", sock);
+
+        /* the default timeout (0) will set the socket to blocking with
+           infinite timeouts.
+         */
+
+        if (timeout <= 0)
+            apr_socket_timeout_set(sock, -1);
+        else
+            apr_socket_timeout_set(sock, timeout);
+
+        /* make the connection out of the socket */
+        do {
+            ret = apr_socket_connect(sock, remote_sa);
+        } while (APR_STATUS_IS_EINTR(ret));
+
+        /* if an error occurred, loop round and try again */
+        if (ret != APR_SUCCESS) {
+            apr_socket_close(sock);
+            if (remote_sa->next) {
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "channelApr.open() attempt to connect to %pI (%s) failed %d\n",
+                              remote_sa, socketInfo->host, ret);
+            }
+            else {
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "channelApr.open() attempt to connect to %pI (%s) failed %d\n",
+                              remote_sa, socketInfo->host, ret);
+            }
+            remote_sa = remote_sa->next;
+            continue;
+        }
+        connected = 1;
+    }
+
+    if (!connected) {
+        apr_socket_close(sock);
+        return JK_ERR;
+    }
+    /* enable the use of keep-alive packets on TCP connection */
+    if (keepalive) {
+        int set = 1;
+        if ((ret =
+             apr_socket_opt_set(sock, APR_SO_KEEPALIVE,
+                                set)) != APR_SUCCESS) {
+            apr_socket_close(sock);
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "channelApr.open() keepalive failed %d %s\n",
+                          ret, apr_strerror(ret, msg, sizeof(msg)));
+            return JK_ERR;
+        }
+    }
+
+    /* Disable the Nagle algorithm if ndelay is set */
+    if (ndelay) {
+        int set = 1;
+        if ((ret =
+             apr_socket_opt_set(sock, APR_TCP_NODELAY, set)) != APR_SUCCESS) {
+            apr_socket_close(sock);
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "channelApr.open() nodelay failed %d %s\n",
+                          ret, apr_strerror(ret, msg, sizeof(msg)));
+            return JK_ERR;
+        }
+    }
+
+    if (ch->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "channelApr.open(), sock = %d\n", sock);
+
+    return JK_OK;
+}
+
+
+/** close the socket  ( was: jk2_close_socket )
+*/
+static int JK_METHOD jk2_channel_apr_close(jk_env_t *env, jk_channel_t *ch,
+                                           jk_endpoint_t *endpoint)
+{
+    apr_socket_t *sd;
+    apr_status_t rc;
+
+    sd = endpoint->channelData;
+    if (sd == NULL)
+        return JK_ERR;
+
+    endpoint->channelData = NULL;       /* XXX check it. */
+    endpoint->sd = -1;
+
+    /* nothing else to clean, the socket_data was allocated ouf of
+     *  endpoint's pool
+     */
+    rc = apr_socket_close(sd);
+    return rc;
+}
+
+
+/** send a long message
+ * @param sd  opened socket.
+ * @param b   buffer containing the data.
+ * @param len length to send.
+ * @return    -2: send returned 0 ? what this that ?
+ *            -3: send failed.
+ *            >0: total size send.
+ * @bug       this fails on Unixes if len is too big for the underlying
+ *             protocol.
+ * @was: jk_tcp_socket_sendfull
+ */
+static int JK_METHOD jk2_channel_apr_send(jk_env_t *env, jk_channel_t *ch,
+                                          jk_endpoint_t *endpoint,
+                                          jk_msg_t *msg)
+{
+    char *b;
+    int len;
+    apr_socket_t *sock;
+    apr_status_t stat;
+    apr_size_t length;
+    char data[128];
+
+    sock = endpoint->channelData;
+
+    if (sock == NULL)
+        return JK_ERR;
+
+    msg->end(env, msg);
+    len = msg->len;
+    b = msg->buf;
+
+    length = (apr_size_t) len;
+    do {
+        apr_size_t written = length;
+
+        stat = apr_socket_send(sock, b, &written);
+        if (stat != APR_SUCCESS) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "jk2_channel_apr_send send failed %d %s\n",
+                          stat, apr_strerror(stat, data, sizeof(data)));
+            return -3;          /* -2 is not possible... */
+        }
+        length -= written;
+        b += written;
+    } while (length);
+
+    return JK_OK;
+}
+
+
+/** receive len bytes.
+ * @param sd  opened socket.
+ * @param b   buffer to store the data.
+ * @param len length to receive.
+ * @return    -1: receive failed or connection closed.
+ *            >0: length of the received data.
+ * Was: tcp_socket_recvfull
+ */
+static int JK_METHOD jk2_channel_apr_readN(jk_env_t *env,
+                                           jk_channel_t *ch,
+                                           jk_endpoint_t *endpoint,
+                                           char *b, int len)
+{
+    apr_socket_t *sock;
+    apr_size_t length;
+    apr_status_t stat;
+    int rdlen;
+
+    sock = endpoint->channelData;
+
+    if (sock == NULL)
+        return JK_ERR;
+
+    rdlen = 0;
+    length = (apr_size_t) len;
+    while (rdlen < len) {
+
+        stat = apr_socket_recv(sock, b + rdlen, &length);
+
+        if (stat == APR_EOF)
+            return -1;          /* socket closed. */
+        else if (APR_STATUS_IS_EAGAIN(stat))
+            continue;
+        else if (stat != APR_SUCCESS)
+            return -1;          /* any error. */
+        rdlen += length;
+        length = (apr_size_t) (len - rdlen);
+    }
+    return rdlen;
+}
+
+/** receive len bytes.
+ * @param sd  opened socket.
+ * @param b   buffer to store the data.
+ * @param len length to receive.
+ * @return    -1: receive failed or connection closed.
+ *            >0: length of the received data.
+ * Was: tcp_socket_recvfull
+ */
+static int JK_METHOD jk2_channel_apr_recv(jk_env_t *env, jk_channel_t *ch,
+                                          jk_endpoint_t *endpoint,
+                                          jk_msg_t *msg)
+{
+    int hlen = msg->headerLength;
+    int blen;
+    int rc;
+
+
+    jk2_channel_apr_readN(env, ch, endpoint, msg->buf, hlen);
+
+    blen = msg->checkHeader(env, msg, endpoint);
+    if (blen < 0) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "channelApr.receive(): Bad header\n");
+        return JK_ERR;
+    }
+
+    rc = jk2_channel_apr_readN(env, ch, endpoint, msg->buf + hlen, blen);
+
+    if (rc < 0) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "channelApr.receive(): Error receiving message body %d %d\n",
+                      rc, errno);
+        return JK_ERR;
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "channelApr.receive(): Received len=%d type=%d\n",
+                  blen, (int)msg->buf[hlen]);
+    return JK_OK;
+
+}
+
+
+int JK_METHOD jk2_channel_apr_socket_factory(jk_env_t *env,
+                                             jk_pool_t *pool,
+                                             jk_bean_t *result,
+                                             const char *type,
+                                             const char *name)
+{
+    jk_channel_t *ch;
+
+    ch = (jk_channel_t *)pool->calloc(env, pool, sizeof(jk_channel_t));
+
+    ch->_privatePtr = (jk_channel_apr_private_t *)
+        pool->calloc(env, pool, sizeof(jk_channel_apr_private_t));
+
+    ch->recv = jk2_channel_apr_recv;
+    ch->send = jk2_channel_apr_send;
+    ch->open = jk2_channel_apr_open;
+    ch->close = jk2_channel_apr_close;
+    ch->hasinput = jk2_channel_apr_hasinput;
+
+    ch->is_stream = JK_TRUE;
+
+    result->setAttribute = jk2_channel_apr_setProperty;
+    result->getAttribute = jk2_channel_apr_socket_getAttribute;
+    result->init = jk2_channel_apr_init;
+    result->getAttributeInfo = jk2_channel_apr_socket_getAttributeInfo;
+    result->multiValueInfo = NULL;
+    result->setAttributeInfo = jk2_channel_apr_socket_setAttributeInfo;
+
+    ch->mbean = result;
+    result->object = ch;
+
+
+    ch->workerEnv = env->getByName(env, "workerEnv");
+    ch->workerEnv->addChannel(env, ch->workerEnv, ch);
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_channel_jni.c b/connectors/jk/native2/common/jk_channel_jni.c
new file mode 100644
index 0000000..569e0e9
--- /dev/null
+++ b/connectors/jk/native2/common/jk_channel_jni.c
@@ -0,0 +1,668 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ *  Channel using jni calls ( assuming in-process tomcat ).
+ *
+ * @author:  Gal Shachor <shachor@il.ibm.com>                           
+ * @author: Costin Manolache
+ */
+
+#include "jk_workerEnv.h"
+#include "jk_env.h"
+#include "jk_bean.h"
+
+#ifdef HAVE_JNI
+
+#include "jk_map.h"
+#include "jk_env.h"
+#include "jk_channel.h"
+#include "jk_global.h"
+
+#include <string.h>
+#include "jk_registry.h"
+#include <jni.h>
+
+/* default only, is configurable now */
+
+#define JAVA_BRIDGE_CLASS_NAME ("org/apache/jk/apr/AprImpl")
+
+#define JNI_TOMCAT_STARTED 2
+extern int jk_jni_status_code;
+
+/** Information specific for the socket channel
+ */
+typedef struct
+{
+    jk_vm_t *vm;
+
+    char *className;
+    jclass jniBridge;
+
+    jmethodID writeMethod;
+    int status;
+} jk_channel_jni_private_t;
+
+typedef struct
+{
+    JNIEnv *jniEnv;
+
+    int len;
+    jbyteArray jarray;
+    char *carray;
+    int arrayLen;
+
+    jobject jniJavaContext;
+/*     jobject msgJ; */
+} jk_ch_jni_ep_private_t;
+
+
+/*
+   Duplicate string and convert it to ASCII on EBDIC based systems
+   Needed for at least AS/400 and BS2000 but what about other EBDIC systems ?
+   Implement as macro cause:   
+   we don't need to duplicate the strings if they are const on non EBDIC systems 
+*/
+
+#if defined(AS400) || defined(_OSD_POSIX)
+#define SSTRDUP_ASCII(e, s) ((e)->tmpPool->pstrdup2ascii(e, (e)->tmpPool, s))
+#else
+#define SSTRDUP_ASCII(e, s) (s)
+#endif
+
+static int JK_METHOD jk2_channel_jni_init(jk_env_t *env, jk_bean_t *jniWB)
+{
+    jk_channel_t *jniW = jniWB->object;
+    jk_workerEnv_t *wEnv = jniW->workerEnv;
+
+    if (wEnv->childId != 0) {
+        if (jniW->worker != NULL)
+            jniW->worker->mbean->disabled = JK_TRUE;
+        return JK_ERR;
+    }
+    if (wEnv->vm == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "channel_jni.init() no VM found\n");
+        if (jniW->worker != NULL) {
+            jniW->worker->mbean->disabled = JK_TRUE;
+        }
+        return JK_ERR;
+    }
+    return JK_OK;
+}
+
+/*
+ * Wait input event for timeout ms
+ */
+static int JK_METHOD jk2_channel_jni_hasinput(jk_env_t *env,
+                                              jk_channel_t *ch,
+                                              jk_endpoint_t *endpoint,
+                                              int timeout)
+{
+    /*
+     * No delay in such case
+     */
+    return (JK_TRUE);
+}
+
+
+/** Assume the jni-worker or someone else started
+ *  tomcat and initialized what is needed.
+ */
+static int JK_METHOD jk2_channel_jni_open(jk_env_t *env,
+                                          jk_channel_t *_this,
+                                          jk_endpoint_t *endpoint)
+{
+    jk_workerEnv_t *we = endpoint->worker->workerEnv;
+    JNIEnv *jniEnv;
+    jk_ch_jni_ep_private_t *epData;
+    jmethodID jmethod;
+    jobject jobj;
+    jstring jstr;
+
+    jk_channel_jni_private_t *jniCh = _this->_privatePtr;
+
+    if (endpoint->channelData != NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "channel_jni.open() already open, nothing else to do\n");
+        return JK_OK;
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO, "channel_jni.open():  \n");
+
+    /* It is useless to continue if the channel worker 
+       does not exist.
+     */
+    if (_this->worker == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "channel_jni.open()  NullPointerException, no channel worker found\n");
+        return JK_ERR;
+    }
+
+    jniCh->vm = (jk_vm_t *) we->vm;
+    if (jniCh->vm == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "channel_jni.open() no VM found\n");
+        _this->worker->mbean->disabled = JK_TRUE;
+        return JK_ERR;
+    }
+
+    jniEnv = (JNIEnv *) jniCh->vm->attach(env, jniCh->vm);
+    if (jniEnv == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "channel_jni.open() can't attach\n");
+        _this->worker->mbean->disabled = JK_TRUE;
+        return JK_ERR;
+    }
+    /* Create the buffers used by the write method. We allocate a
+       byte[] and jbyte[] - I have no idea what's more expensive,
+       to copy a buffer or to 'pin' the jbyte[] for copying.
+
+       This will be tuned if needed, for now it seems the easiest
+       solution
+     */
+    epData = (jk_ch_jni_ep_private_t *)
+        endpoint->mbean->pool->calloc(env, endpoint->mbean->pool,
+                                      sizeof(jk_ch_jni_ep_private_t));
+
+    endpoint->channelData = epData;
+
+    /* AS400/BS2000 need EBCDIC to ASCII conversion for JNI */
+    jniCh->jniBridge =
+        (*jniEnv)->FindClass(jniEnv, SSTRDUP_ASCII(env, jniCh->className));
+
+    if (jniCh->jniBridge == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "channel_jni.open() can't find %s\n", jniCh->className);
+        _this->worker->mbean->disabled = JK_TRUE;
+        return JK_ERR;
+    }
+
+    jniCh->jniBridge = (*jniEnv)->NewGlobalRef(jniEnv, jniCh->jniBridge);
+
+    if (jniCh->jniBridge == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "channel_jni.open() Unable to allocate globalref for %s\n",
+                      jniCh->className);
+        _this->worker->mbean->disabled = JK_TRUE;
+        return JK_ERR;
+    }
+
+    /* Interface to the callback mechansim. The idea is simple ( is it ? ) - we
+       use a similar pattern with java, trying to do as little as possible
+       in C and pass minimal information to allow this.
+
+       The pattern used for callback works for our message forwarding but also for
+       other things - like singnals, etc
+     */
+
+    /* AS400/BS2000 need EBCDIC to ASCII conversion for JNI */
+    jmethod = (*jniEnv)->GetStaticMethodID(jniEnv, jniCh->jniBridge,
+                                           SSTRDUP_ASCII(env,
+                                                         "createJavaContext"),
+                                           SSTRDUP_ASCII(env,
+                                                         "(Ljava/lang/String;J)Ljava/lang/Object;"));
+
+    if (jmethod == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "channel_jni.open() can't find createJavaContext\n");
+        _this->worker->mbean->disabled = JK_TRUE;
+
+        if ((*jniEnv)->ExceptionCheck(jniEnv)) {
+            (*jniEnv)->ExceptionClear(jniEnv);
+        }
+        return JK_ERR;
+    }
+
+    /* AS400/BS2000 need EBCDIC to ASCII conversion for JNI */
+    jstr = (*jniEnv)->NewStringUTF(jniEnv, SSTRDUP_ASCII(env, "channelJni"));
+
+    jobj = (*jniEnv)->CallStaticObjectMethod(jniEnv, jniCh->jniBridge,
+                                             jmethod,
+                                             jstr,
+                                             (jlong) (long)(void *)endpoint->
+                                             mbean);
+
+    if (jobj == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "channel_jni.open() Can't create java context\n");
+        epData->jniJavaContext = NULL;
+        _this->worker->mbean->disabled = JK_TRUE;
+        if ((*jniEnv)->ExceptionCheck(jniEnv)) {
+            (*jniEnv)->ExceptionClear(jniEnv);
+        }
+        return JK_ERR;
+    }
+    epData->jniJavaContext = (*jniEnv)->NewGlobalRef(jniEnv, jobj);
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "channel_jni.open() Got ep %#lx %#lx\n", jobj,
+                  epData->jniJavaContext);
+
+    /* XXX Destroy them in close */
+
+    /* AS400/BS2000 need EBCDIC to ASCII conversion for JNI */
+    jmethod = (*jniEnv)->GetStaticMethodID(jniEnv, jniCh->jniBridge,
+                                           SSTRDUP_ASCII(env, "getBuffer"),
+                                           SSTRDUP_ASCII(env,
+                                                         "(Ljava/lang/Object;I)[B"));
+    if (jmethod == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "channel_jni.open() can't find getBuffer\n");
+        _this->worker->mbean->disabled = JK_TRUE;
+        return JK_ERR;
+    }
+
+    epData->jarray =
+        (*jniEnv)->CallStaticObjectMethod(jniEnv, jniCh->jniBridge, jmethod,
+                                          epData->jniJavaContext, 0);
+
+    epData->jarray = (*jniEnv)->NewGlobalRef(jniEnv, epData->jarray);
+
+    epData->arrayLen = (*jniEnv)->GetArrayLength(jniEnv, epData->jarray);
+
+    /* XXX > ajp buffer size. Don't know how to fragment or reallocate
+       yet */
+    epData->carray =
+        (char *)endpoint->mbean->pool->calloc(env, endpoint->mbean->pool,
+                                              epData->arrayLen);
+
+    /* AS400/BS2000 need EBCDIC to ASCII conversion for JNI */
+    jniCh->writeMethod =
+        (*jniEnv)->GetStaticMethodID(jniEnv, jniCh->jniBridge,
+                                     SSTRDUP_ASCII(env, "jniInvoke"),
+                                     SSTRDUP_ASCII(env,
+                                                   "(JLjava/lang/Object;)I"));
+
+    if (jniCh->writeMethod == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_EMERG,
+                      "channel_jni.open() can't find jniInvoke\n");
+
+        _this->worker->mbean->disabled = JK_TRUE;
+        return JK_ERR;
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "channel_jni.open() found write method, open ok\n");
+
+    _this->worker->mbean->disabled = JK_FALSE;
+
+    /* Don't detach ( XXX Need to find out when the thread is
+     *  closing in order for this to work )
+     */
+    /*     jniCh->vm->detach( env, jniCh->vm ); */
+    return JK_OK;
+}
+
+
+/** 
+ */
+static int JK_METHOD jk2_channel_jni_close(jk_env_t *env, jk_channel_t *_this,
+                                           jk_endpoint_t *endpoint)
+{
+    jk_ch_jni_ep_private_t *epData;
+    JNIEnv *jniEnv;
+    jk_channel_jni_private_t *jniCh = _this->_privatePtr;
+    epData = (jk_ch_jni_ep_private_t *) endpoint->channelData;
+
+    if (epData == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "channel_jni.close() no channel data\n");
+        return JK_ERR;
+    }
+    jniEnv = (JNIEnv *) jniCh->vm->attach(env, jniCh->vm);
+
+    if (jniEnv == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "channel_jni.close() can't attach\n");
+        return JK_ERR;
+    }
+    if (epData->jarray != NULL) {
+        (*jniEnv)->DeleteGlobalRef(jniEnv, epData->jarray);
+    }
+    if (epData->jniJavaContext != NULL) {
+        (*jniEnv)->DeleteGlobalRef(jniEnv, epData->jniJavaContext);
+    }
+
+    jniCh->vm->detach(env, jniCh->vm);
+    env->l->jkLog(env, env->l, JK_LOG_INFO, "channel_jni.close() ok\n");
+
+    endpoint->channelData = NULL;
+    return JK_OK;
+
+}
+
+/** send a long message
+ * @param sd  opened socket.
+ * @param b   buffer containing the data.
+ * @param len length to send.
+ * @return    -2: send returned 0 ? what this that ?
+ *            -3: send failed.
+ *            >0: total size send.
+ * @bug       this fails on Unixes if len is too big for the underlying
+ *             protocol.
+ * @was: jk_tcp_socket_sendfull
+ */
+static int JK_METHOD jk2_channel_jni_send(jk_env_t *env, jk_channel_t *_this,
+                                          jk_endpoint_t *endpoint,
+                                          jk_msg_t *msg)
+{
+/*    int sd; */
+    int sent = 0;
+    char *b;
+    int len;
+    jbyte *nbuf;
+    jbyteArray jbuf;
+    int jlen = 0;
+    jboolean iscopy = 0;
+    JNIEnv *jniEnv;
+    jk_channel_jni_private_t *jniCh = _this->_privatePtr;
+    jk_ch_jni_ep_private_t *epData =
+        (jk_ch_jni_ep_private_t *) endpoint->channelData;
+
+    if (_this->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG, "channel_jni.send() %#lx\n",
+                      epData);
+
+    if (epData == NULL) {
+        jk2_channel_jni_open(env, _this, endpoint);
+        epData = (jk_ch_jni_ep_private_t *) endpoint->channelData;
+    }
+    if (epData == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "channel_jni.send() error opening channel\n");
+        return JK_ERR;
+    }
+    if (epData->jniJavaContext == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "channel_jni.send() no java context\n");
+        jk2_channel_jni_close(env, _this, endpoint);
+        return JK_ERR;
+    }
+
+    msg->end(env, msg);
+    len = msg->len;
+    b = msg->buf;
+
+    if (_this->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "channel_jni.send() (1) %#lx\n", epData);
+
+    jniEnv = NULL;              /* epData->jniEnv; */
+    jbuf = epData->jarray;
+
+    if (jniCh->writeMethod == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_EMERG,
+                      "channel_jni.send() no write method\n");
+        return JK_ERR;
+    }
+    if (jniEnv == NULL) {
+        /* Try first getEnv, then attach */
+        jniEnv = (JNIEnv *) jniCh->vm->attach(env, jniCh->vm);
+        if (jniEnv == NULL) {
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "channel_jni.send() can't attach\n");
+            return JK_ERR;
+        }
+    }
+
+    if (_this->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "channel_jni.send() getting byte array \n");
+
+    /* Copy the data in the ( recycled ) jbuf, then call the
+     *  write method. XXX We could try 'pining' if the vm supports
+     *  it, this is a looong lived object.
+     */
+#ifdef JK_JNI_CRITICAL
+    nbuf = (*jniEnv)->GetPrimitiveArrayCritical(jniEnv, jbuf, &iscopy);
+#else
+    nbuf = (*jniEnv)->GetByteArrayElements(jniEnv, jbuf, &iscopy);
+#endif
+    if (iscopy)
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "channelJni.send() get java bytes iscopy %d\n", iscopy);
+
+    if (nbuf == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "channelJni.send() Can't get java bytes");
+        return JK_ERR;
+    }
+
+    memcpy(nbuf, b, len);
+
+#ifdef JK_JNI_CRITICAL
+    (*jniEnv)->ReleasePrimitiveArrayCritical(jniEnv, jbuf, nbuf, 0);
+#else
+    (*jniEnv)->ReleaseByteArrayElements(jniEnv, jbuf, nbuf, 0);
+#endif
+    if (_this->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "channel_jni.send() before send %#lx\n",
+                      (void *)(long)epData->jniJavaContext);
+
+    sent = (*jniEnv)->CallStaticIntMethod(jniEnv,
+                                          jniCh->jniBridge,
+                                          jniCh->writeMethod,
+                                          (jlong) (long)(void *)env,
+                                          epData->jniJavaContext);
+    if (_this->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "channel_jni.send() result %d\n", sent);
+    return JK_OK;
+}
+
+
+/**
+ * Not used - we have a single thread, there is no 'blocking' - the
+ * java side will send messages by calling a native method, which will
+ * receive and dispatch.
+ */
+static int JK_METHOD jk2_channel_jni_recv(jk_env_t *env, jk_channel_t *_this,
+                                          jk_endpoint_t *endpoint,
+                                          jk_msg_t *msg)
+{
+/*    jbyte *nbuf; */
+/*    jbyteArray jbuf; */
+/*    int jlen; */
+/*    jboolean iscommit;     */
+    jk_channel_jni_private_t *jniCh = _this->_privatePtr;
+
+    env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                  "channelJni.recv() method not supported for JNI channel\n");
+    return -1;
+
+    /* Old workaround:
+
+       nbuf=(jbyte *)endpoint->currentData;
+
+       if(nbuf==NULL ) {
+       env->l->jkLog(env, env->l, JK_LOG_INFO,
+       "channelJni.recv() no jbyte[] was received\n");
+       return -1;
+       }
+
+       env->l->jkLog(env, env->l, JK_LOG_INFO,
+       "channelJni.recv() receiving %d\n", len);
+
+       memcpy( b, nbuf + endpoint->currentOffset, len );
+       endpoint->currentOffset += len;
+
+       return len;
+     */
+}
+
+
+/** Called before request processing, to initialize resources.
+    All following calls will be in the same thread.
+*/
+int JK_METHOD jk2_channel_jni_beforeRequest(struct jk_env *env,
+                                            jk_channel_t *_this,
+                                            struct jk_worker *worker,
+                                            struct jk_endpoint *endpoint,
+                                            struct jk_ws_service *r)
+{
+    jk_workerEnv_t *we = worker->workerEnv;
+
+    if (worker->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "service() attaching to vm\n");
+
+    return JK_OK;
+}
+
+/** Called after request processing. Used to be worker.done()
+ */
+int JK_METHOD jk2_channel_jni_afterRequest(struct jk_env *env,
+                                           jk_channel_t *_this,
+                                           struct jk_worker *worker,
+                                           struct jk_endpoint *endpoint,
+                                           struct jk_ws_service *r)
+{
+    jk_workerEnv_t *we = worker->workerEnv;
+
+    if (we == NULL || we->vm == NULL) {
+        return JK_OK;
+    }
+    /* 
+     * In case of not having the endpoint detach the jvm.
+     * XXX Remove calling this function from ajp13 worker?
+     */
+    if (endpoint == NULL)
+        we->vm->detach(env, we->vm);
+    if (worker->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "channelJni.afterRequest() ok\n");
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_channel_jni_setProperty(jk_env_t *env,
+                                                 jk_bean_t *mbean,
+                                                 char *name, void *valueP)
+{
+    jk_channel_t *ch = (jk_channel_t *)mbean->object;
+    char *value = valueP;
+    jk_channel_jni_private_t *jniInfo =
+        (jk_channel_jni_private_t *) (ch->_privatePtr);
+
+    if (strcmp("class", name) == 0) {
+        jniInfo->className = value;
+    }
+    /* TODO: apache protocol hooks
+       else if( strcmp( "xxxx", name ) == 0 ) {
+       jniInfo->xxxx=value;
+       } 
+     */
+    else {
+        return jk2_channel_setAttribute(env, mbean, name, valueP);
+    }
+    return JK_OK;
+}
+
+/** Called by java. Will take the msg and dispatch it to workerEnv, as if it would
+ *  be if received via socket
+ */
+int JK_METHOD jk2_channel_jni_invoke(jk_env_t *env, jk_bean_t *bean,
+                                     jk_endpoint_t *ep, int code,
+                                     jk_msg_t *msg, int raw)
+{
+    jk_channel_t *ch = (jk_channel_t *)bean->object;
+    int rc = JK_OK;
+
+    if (ch->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG, "ch.%d() \n", code);
+
+    code = (int)msg->getByte(env, msg);
+
+    if (ch->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "channelJni.java2cInvoke() %d\n", code);
+
+    return ep->worker->workerEnv->dispatch(env, ep->worker->workerEnv,
+                                           ep->currentRequest, ep, code,
+                                           ep->reply);
+}
+
+static int JK_METHOD jk2_channel_jni_status(jk_env_t *env,
+                                            struct jk_worker *worker,
+                                            jk_channel_t *_this)
+{
+
+    jk_channel_jni_private_t *jniCh = _this->_privatePtr;
+    if (jniCh->status != JNI_TOMCAT_STARTED) {
+        jniCh->status = jk_jni_status_code;
+        if (jniCh->status != JNI_TOMCAT_STARTED)
+            return JK_ERR;
+    }
+    return JK_OK;
+}
+
+
+int JK_METHOD jk2_channel_jni_factory(jk_env_t *env, jk_pool_t *pool,
+                                      jk_bean_t *result,
+                                      const char *type, const char *name)
+{
+    jk_channel_t *ch = result->object;
+    jk_workerEnv_t *wEnv;
+    jk_channel_jni_private_t *jniPrivate;
+
+
+    wEnv = env->getByName(env, "workerEnv");
+    ch = (jk_channel_t *)pool->calloc(env, pool, sizeof(jk_channel_t));
+
+    ch->recv = jk2_channel_jni_recv;
+    ch->send = jk2_channel_jni_send;
+    ch->open = jk2_channel_jni_open;
+    ch->close = jk2_channel_jni_close;
+    ch->hasinput = jk2_channel_jni_hasinput;
+
+    ch->beforeRequest = jk2_channel_jni_beforeRequest;
+    ch->afterRequest = jk2_channel_jni_afterRequest;
+    ch->status = jk2_channel_jni_status;
+
+    ch->_privatePtr = jniPrivate =
+        (jk_channel_jni_private_t *) pool->calloc(env, pool,
+                                                  sizeof
+                                                  (jk_channel_jni_private_t));
+
+    jniPrivate->className = JAVA_BRIDGE_CLASS_NAME;
+    ch->is_stream = JK_FALSE;
+
+    /* No special attribute */
+    result->setAttribute = jk2_channel_jni_setProperty;
+    ch->mbean = result;
+    result->object = ch;
+    result->init = jk2_channel_jni_init;
+
+    ch->workerEnv = wEnv;
+    wEnv->addChannel(env, wEnv, ch);
+
+    result->invoke = jk2_channel_jni_invoke;
+
+    return JK_OK;
+}
+
+#else
+
+int JK_METHOD jk2_channel_jni_factory(jk_env_t *env, jk_pool_t *pool,
+                                      jk_bean_t *result,
+                                      const char *type, const char *name)
+{
+    result->disabled = 1;
+    return JK_OK;
+}
+
+#endif
diff --git a/connectors/jk/native2/common/jk_channel_un.c b/connectors/jk/native2/common/jk_channel_un.c
new file mode 100644
index 0000000..d27b7b9
--- /dev/null
+++ b/connectors/jk/native2/common/jk_channel_un.c
@@ -0,0 +1,526 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Channel using 'plain' TCP sockets or UNIX sockets.
+ * Based on jk_sockbuf. It uses a an APR-based mechanism.
+ * The UNIX sockets are not yet in APR (the code has to been written).
+ * 
+ * Properties:
+ *  - host/filename
+ *  - port
+ *  - ndelay (Where the hell we set it?)
+ *
+ * This channel should 'live' as much as the workerenv. It is stateless.
+ * It allocates memory for endpoint private data ( using endpoint's pool ).
+ *
+ * @author:  Gal Shachor <shachor@il.ibm.com>                           
+ * @author: Costin Manolache
+ * @author: Jean-Frederic Clere <jfrederic.clere@fujitsu-siemens.com>
+ */
+
+#include "jk_map.h"
+#include "jk_env.h"
+#include "jk_channel.h"
+#include "jk_global.h"
+
+#include <string.h>
+#include <fcntl.h>
+#include "jk_registry.h"
+
+#ifdef HAVE_UNIXSOCKETS
+
+/** Information specific for the socket channel
+ */
+typedef struct jk_channel_un_private
+{
+    int ndelay;
+    struct sockaddr_un unix_addr;
+    char *file;
+
+    int l_onoff;                /* Nonzero to linger on close.  */
+    int l_linger;               /* Time to linger.  */
+
+    int backlog;
+
+    int listenSocket;
+} jk_channel_un_private_t;
+
+#ifndef SUN_LEN
+/* actual length of an initialized sockaddr_un */
+#define SUN_LEN(su) \
+        (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
+#endif
+
+static int JK_METHOD jk2_channel_un_close(jk_env_t *env, jk_channel_t *ch,
+                                          jk_endpoint_t *endpoint);
+
+static char *jk2_channel_un_multiValueInfo[] = { "group", NULL };
+static char *jk2_channel_un_setAttributeInfo[] =
+    { "file", "soLinger", "listen",
+    "level", NULL
+};
+static char *jk2_channel_un_getAttributeInfo[] = { "file", "soLinger",
+    "listen", NULL
+};
+
+static int JK_METHOD jk2_channel_un_setAttribute(jk_env_t *env,
+                                                 jk_bean_t *mbean,
+                                                 char *name, void *valueP)
+{
+    jk_channel_t *ch = (jk_channel_t *)mbean->object;
+    char *value = valueP;
+    jk_channel_un_private_t *socketInfo =
+        (jk_channel_un_private_t *) (ch->_privatePtr);
+
+    if (strcmp("file", name) == 0) {
+        socketInfo->file = value;
+    }
+    else if (strcmp("soLinger", name) == 0) {
+        socketInfo->l_linger = atoi(value);
+    }
+    else if (strcmp("listen", name) == 0) {
+        socketInfo->backlog = atoi(value);
+        ch->serverSide = JK_TRUE;
+    }
+    else {
+        return jk2_channel_setAttribute(env, mbean, name, valueP);
+    }
+    return JK_OK;
+}
+
+static void *JK_METHOD jk2_channel_un_getAttribute(jk_env_t *env,
+                                                   jk_bean_t *mbean,
+                                                   char *name)
+{
+    jk_channel_t *ch = (jk_channel_t *)mbean->object;
+    jk_channel_un_private_t *socketInfo =
+        (jk_channel_un_private_t *) (ch->_privatePtr);
+
+    if (strcmp("file", name) == 0) {
+        return socketInfo->file;
+    }
+    else if (strcmp("soLinger", name) == 0) {
+        return jk2_env_itoa(env, socketInfo->l_linger);
+    }
+    else if (strcmp("listen", name) == 0) {
+        return jk2_env_itoa(env, socketInfo->backlog);
+    }
+    return NULL;
+}
+
+/** resolve the host IP ( jk_resolve ) and initialize the channel.
+ */
+static int JK_METHOD jk2_channel_un_init(jk_env_t *env, jk_bean_t *chB)
+{
+    jk_channel_t *ch = chB->object;
+    jk_channel_un_private_t *socketInfo =
+        (jk_channel_un_private_t *) (ch->_privatePtr);
+    int rc = JK_OK;
+    int omask;
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO, "channelUn.init(): init \n");
+    if (socketInfo->file == NULL) {
+        char *localName = ch->mbean->localName;
+        jk_config_t *cfg = ch->workerEnv->config;
+
+        /* Set the 'name' property
+         */
+        localName =
+            jk2_config_replaceProperties(env, cfg->map, cfg->map->pool,
+                                         localName);
+
+        /*   env->l->jkLog(env, env->l, JK_LOG_INFO, */
+        /*                 "channelUn.init(): use name %s\n", localName ); */
+
+        if (localName[0] == '/') {
+            ch->mbean->setAttribute(env, ch->mbean, "file", localName);
+        }
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "channelUn.init(): extracted file from name %s\n",
+                      socketInfo->file);
+    }
+
+    if (socketInfo->file != NULL && socketInfo->file[0] == '/') {
+        memset(&socketInfo->unix_addr, 0, sizeof(struct sockaddr_un));
+        socketInfo->unix_addr.sun_family = AF_UNIX;
+        strcpy(socketInfo->unix_addr.sun_path, socketInfo->file);
+
+        if (ch->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "channelUn.init(): create AF_UNIX  %s\n",
+                          socketInfo->file);
+    }
+    else {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, "channelUn.init(): "
+                      "can't init %s errno=%d\n", socketInfo->file, errno);
+    }
+
+    if (ch->serverSide == JK_TRUE) {
+
+        socketInfo->listenSocket = socket(AF_UNIX, SOCK_STREAM, 0);
+        if (socketInfo->listenSocket < 0) {
+            return JK_ERR;
+        }
+
+        omask = umask(0117);    /* so that only Apache can use socket */
+
+        rc = bind(socketInfo->listenSocket,
+                  (struct sockaddr *)&socketInfo->unix_addr,
+                  SUN_LEN(&(socketInfo->unix_addr)));
+
+        umask(omask);           /* can't fail, so can't clobber errno */
+
+        if (rc < 0)
+            return -errno;
+
+        listen(socketInfo->listenSocket, socketInfo->backlog);
+
+        if (ch->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "Unix socket listening on %d \n",
+                          socketInfo->listenSocket);
+        /*
+           {
+           struct linger {
+           int   l_onoff;    
+           int   l_linger;   
+           } lin;
+           int rc;
+
+           lin.l_onoff = l_onoff;
+           lin.l_linger = l_linger;
+           rc=setsockopt(sd, SOL_SOCKET, SO_LINGER, &lin, sizeof(lin));
+           if( rc < 0) {
+           return -errno;
+           }
+           }
+         */
+
+    }
+
+    return rc;
+}
+
+
+/*
+ * Wait input event on socket for timeout ms
+ */
+static int JK_METHOD jk2_channel_un_hasinput(jk_env_t *env,
+                                             jk_channel_t *ch,
+                                             jk_endpoint_t *endpoint,
+                                             int timeout)
+{
+    /*
+     * Should implements the select/poll for UN here
+     */
+    return (JK_TRUE);
+}
+
+/** connect to Tomcat (jk_open_socket)
+ */
+static int JK_METHOD jk2_channel_un_open(jk_env_t *env,
+                                         jk_channel_t *ch,
+                                         jk_endpoint_t *endpoint)
+{
+    jk_channel_un_private_t *socketInfo =
+        (jk_channel_un_private_t *) (ch->_privatePtr);
+    int unixsock;
+    struct sockaddr_un client;
+    int clientlen;
+
+    if (ch->serverSide) {
+        while (1) {
+            clientlen = sizeof(client);
+
+            unixsock =
+                accept(socketInfo->listenSocket, (struct sockaddr *)&client,
+                       &clientlen);
+
+            if (ch->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "channelUn.open(): accept  %d %d\n", unixsock,
+                              errno);
+
+            /* XXX Should we return EINTR ? This would allow us to stop
+             */
+            if (unixsock < 0) {
+                if (errno == EINTR) {
+                    if (ch->mbean->debug > 0)
+                        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                      "channelUn.open(): accept EINTR  %d %d\n",
+                                      unixsock, errno);
+                    continue;
+                }
+                else {
+                    env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                  "channelUn.open(): accept error  %d %d %s\n",
+                                  unixsock, errno, strerror(errno));
+                    return -errno;
+                }
+            }
+            break;
+        }
+    }
+    else {
+        unixsock = socket(AF_UNIX, SOCK_STREAM, 0);
+        if (unixsock < 0) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "channelUn.open(): can't create socket %d %s\n",
+                          errno, strerror(errno));
+            return JK_ERR;
+        }
+
+        if (ch->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "channelUn.open(): create unix socket %s %d\n",
+                          socketInfo->file, unixsock);
+
+        if (connect(unixsock, (struct sockaddr *)&(socketInfo->unix_addr),
+                    sizeof(struct sockaddr_un)) < 0) {
+            close(unixsock);
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "channelUn.connect() connect failed %d %s\n",
+                          errno, strerror(errno));
+            return JK_ERR;
+        }
+    }
+
+#if defined(F_SETFD) && defined(FD_CLOEXEC)
+    /* Protect the socket so that it will not be inherited by child processes */
+    fcntl(unixsock, F_SETFD, FD_CLOEXEC);
+#endif
+
+    if (ch->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "channelUn.open(): connect unix socket %d %s\n",
+                      unixsock, socketInfo->file);
+    /* store the channel information */
+    endpoint->sd = unixsock;
+    return JK_OK;
+}
+
+/** close the socket  ( was: jk2_close_socket )
+*/
+static int JK_METHOD jk2_channel_un_close(jk_env_t *env, jk_channel_t *ch,
+                                          jk_endpoint_t *endpoint)
+{
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "channelUn.close(): close unix socket %d \n", endpoint->sd);
+    close(endpoint->sd);
+    endpoint->sd = -1;
+    return JK_OK;
+}
+
+
+/** send a long message
+ * @param sd  opened socket.
+ * @param b   buffer containing the data.
+ * @param len length to send.
+ * @return    -2: send returned 0 ? what this that ?
+ *            -3: send failed.
+ *            >0: total size send.
+ * @bug       this fails on Unixes if len is too big for the underlying
+ *             protocol.
+ * @was: jk_tcp_socket_sendfull
+ */
+static int JK_METHOD jk2_channel_un_send(jk_env_t *env, jk_channel_t *ch,
+                                         jk_endpoint_t *endpoint,
+                                         jk_msg_t *msg)
+{
+    unsigned char *b;
+    int len;
+    int sent = 0;
+    int this_time;
+    int unixsock;
+
+    msg->end(env, msg);
+    len = msg->len;
+    b = msg->buf;
+
+    unixsock = endpoint->sd;
+    if (unixsock < 0) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "channel.apr:send() not connected %d\n", unixsock);
+        return JK_ERR;
+    }
+
+    while (sent < len) {
+        errno = 0;
+        this_time = write(unixsock, (char *)b + sent, len - sent);
+
+        if (ch->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "channel.apr:send() write() %d %d %s\n", this_time,
+                          errno, strerror(errno));
+        if (0 == this_time) {
+            return -2;
+        }
+        if (this_time < 0) {
+            return -3;
+        }
+        sent += this_time;
+    }
+    /*     return sent; */
+    return JK_OK;
+}
+
+
+/** receive len bytes.
+ * @param sd  opened socket.
+ * @param b   buffer to store the data.
+ * @param len length to receive
+ * @return    -1: receive failed or connection closed.
+ *            >0: length of the received data.
+ * Was: tcp_socket_recvfull
+ */
+static int JK_METHOD jk2_channel_un_readN(jk_env_t *env,
+                                          jk_channel_t *ch,
+                                          jk_endpoint_t *endpoint,
+                                          unsigned char *b, int len)
+{
+    int sd;
+    int rdlen;
+
+    sd = endpoint->sd;
+
+    if (sd < 0) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "channel.apr:readN() not connected %d\n", sd);
+        return -3;
+    }
+
+    rdlen = 0;
+
+    while (rdlen < len) {
+        int this_time = recv(sd, (char *)b + rdlen,
+                             len - rdlen, 0);
+        if (this_time < 0) {
+            if (EAGAIN == errno) {
+                continue;
+            }
+            return -2;
+        }
+        if (0 == this_time) {
+            return -1;
+        }
+        rdlen += this_time;
+    }
+    return rdlen;
+}
+
+
+/** receive len bytes.
+ * @param sd  opened socket.
+ * @param b   buffer to store the data.
+ * @param len length to receive.
+ * @return    -1: receive failed or connection closed.
+ *            >0: length of the received data.
+ * Was: tcp_socket_recvfull
+ */
+static int JK_METHOD jk2_channel_un_recv(jk_env_t *env, jk_channel_t *ch,
+                                         jk_endpoint_t *endpoint,
+                                         jk_msg_t *msg)
+{
+    int hlen = msg->headerLength;
+    int blen;
+    int rc = JK_OK;
+
+
+    blen = jk2_channel_un_readN(env, ch, endpoint, msg->buf, hlen);
+    if (blen <= 0) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "channelUn.receive(): error receiving %d %d %s %#lx %d\n",
+                      blen, errno, strerror(errno), endpoint, endpoint->sd);
+        return JK_ERR;
+    }
+
+    blen = msg->checkHeader(env, msg, endpoint);
+    if (blen < 0) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "channelUn.receive(): Bad header\n");
+        return JK_ERR;
+    }
+
+    rc = jk2_channel_un_readN(env, ch, endpoint, msg->buf + hlen, blen);
+
+    if (rc < 0) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "channelUn.receive(): Error receiving message body %d %d\n",
+                      rc, errno);
+        return JK_ERR;
+    }
+
+    if (ch->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "channelUn.receive(): Received len=%d type=%d\n",
+                      blen, (int)msg->buf[hlen]);
+    return JK_OK;
+
+}
+
+
+int JK_METHOD jk2_channel_un_factory(jk_env_t *env,
+                                     jk_pool_t *pool,
+                                     jk_bean_t *result,
+                                     const char *type, const char *name)
+{
+    jk_channel_t *ch;
+
+    ch = (jk_channel_t *)pool->calloc(env, pool, sizeof(jk_channel_t));
+
+    ch->_privatePtr = (jk_channel_un_private_t *)
+        pool->calloc(env, pool, sizeof(jk_channel_un_private_t));
+
+    ch->recv = jk2_channel_un_recv;
+    ch->send = jk2_channel_un_send;
+    ch->open = jk2_channel_un_open;
+    ch->close = jk2_channel_un_close;
+    ch->hasinput = jk2_channel_un_hasinput;
+    ch->is_stream = JK_TRUE;
+    ch->serverSide = JK_FALSE;
+
+    result->setAttribute = jk2_channel_un_setAttribute;
+    result->getAttribute = jk2_channel_un_getAttribute;
+    result->multiValueInfo = jk2_channel_un_multiValueInfo;
+    result->setAttributeInfo = jk2_channel_un_setAttributeInfo;
+    result->getAttributeInfo = jk2_channel_un_getAttributeInfo;
+    result->invoke = jk2_channel_invoke;
+
+    ch->mbean = result;
+    result->object = ch;
+    result->init = jk2_channel_un_init;
+
+    ch->workerEnv = env->getByName(env, "workerEnv");
+    ch->workerEnv->addChannel(env, ch->workerEnv, ch);
+
+    return JK_OK;
+}
+
+#else
+
+int JK_METHOD jk2_channel_un_factory(jk_env_t *env,
+                                     jk_pool_t *pool,
+                                     jk_bean_t *result,
+                                     const char *type, const char *name)
+{
+    env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                  "channelUn.factory(): Support for unix sockets is disabled, "
+                  "you need to set HAVE_UNIXSOCKETS at compile time\n");
+    result->disabled = 1;
+    return JK_FALSE;
+}
+#endif
diff --git a/connectors/jk/native2/common/jk_config.c b/connectors/jk/native2/common/jk_config.c
new file mode 100644
index 0000000..b976288
--- /dev/null
+++ b/connectors/jk/native2/common/jk_config.c
@@ -0,0 +1,494 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Common methods for processing config data. Independent of the config
+ * storage ( this is a sort of base class ).
+ *
+ * Config is read from the storage as a map, with 'bean names' as keys
+ * and a map of attributes as values.
+ *
+ * We'll process the map creating new objects and calling the setters, similar
+ * with what we do on the java side ( see ant or tomcat  ).
+ *
+ * There is also support for update, based on a version number. New objects
+ * or existing objects having a 'ver' attribute will have the setter methods
+ * called with the new values. XXX a reconfig method may be needed to notify.
+ * Backends that support notifications may update directly the changed attributes,
+ * but all components must support the simpler store ( where some attributes
+ * may be set multiple times, since we can't detect individual att changes ).
+ *
+ * Note: The current code assumes a backend that is capable of storing
+ * multi-valued attributes. This makes things hard to port to registry and
+ * some other repositories. 
+ *
+ * @author: Gal Shachor <shachor@il.ibm.com>                           
+ * @author: Costin Manolache
+ */
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_config.h"
+
+#define LENGTH_OF_LINE    (1024)
+
+/** Interpret the 'name' as [OBJECT].[PROPERTY].
+    If the [OBJECT] is not found, construct it using
+    the prefix ( i.e. we'll search for a factory that matches
+    name - XXX make it longest match ? ).
+
+    Then set the property on the object that is found or
+    constructed.
+
+    No replacement or saving is done on the val - this is
+    a private method
+*/
+static int jk2_config_processBeanPropertyString(jk_env_t *env,
+                                                jk_config_t *cfg,
+                                                char *propertyString,
+                                                char **objName,
+                                                char **propertyName)
+{
+    char *lastDot;
+    char *lastDot1;
+
+    propertyString = cfg->pool->pstrdup(env, cfg->pool, propertyString);
+
+    lastDot = strrchr(propertyString, (int)'.');
+    lastDot1 = strrchr(propertyString, (int)':');
+
+    if (lastDot1 == NULL)
+        lastDot1 = lastDot;
+
+    if (lastDot == NULL || lastDot < lastDot1)
+        lastDot = lastDot1;
+
+    if (lastDot == NULL || *lastDot == '\0')
+        return JK_ERR;
+
+    *lastDot = '\0';
+    lastDot++;
+
+    *objName = propertyString;
+    *propertyName = lastDot;
+
+    /*     fprintf(stderr, "ProcessBeanProperty string %s %s\n", *objName, *propertyName); */
+
+    return JK_OK;
+}
+
+
+/** Set a property on a bean. Call this when you know the bean.
+    The name and values will be saved in the config tables.
+    
+    @param mbean coresponds to the object beeing configured
+    @param name the property to be set ( can't have '.' or ':' in it )
+    @param val the value, $(property) will be replaced.
+ */
+int jk2_config_setProperty(jk_env_t *env, jk_config_t *cfg,
+                           jk_bean_t *mbean, char *name, char *val)
+{
+    char *pname;
+    int multiValue = JK_FALSE;
+
+    if (mbean == cfg->mbean) {
+        pname = name;
+    }
+    else {
+        /* Make substitution work for ${OBJ_NAME.PROP} */
+        pname = cfg->pool->calloc(env, cfg->pool,
+                                  strlen(name) + strlen(mbean->name) + 4);
+        strcpy(pname, mbean->name);
+        strcat(pname, ".");
+        strcat(pname, name);
+    }
+
+    name = cfg->pool->pstrdup(env, cfg->pool, name);
+    val = cfg->pool->pstrdup(env, cfg->pool, val);
+
+    if (strlen(name) && *name == '$') {
+        cfg->map->put(env, cfg->map, name + 1, val, NULL);
+        return JK_OK;
+    }
+
+    /** Save it on the config. XXX no support for deleting yet */
+    /* The _original_ value. Will be saved with $() in it */
+    if (mbean->settings == NULL)
+        jk2_map_default_create(env, &mbean->settings, cfg->pool);
+
+    if (mbean->multiValueInfo != NULL) {
+        int i;
+        for (i = 0; i < 64; i++) {
+            if (mbean->multiValueInfo[i] == NULL)
+                break;
+            if (strcmp(name, mbean->multiValueInfo[i]) == 0) {
+                multiValue = JK_TRUE;
+                break;
+            }
+        }
+    }
+
+    if (multiValue) {
+        mbean->settings->add(env, mbean->settings, name, val);
+    }
+    else {
+        mbean->settings->put(env, mbean->settings, name, val, NULL);
+    }
+
+    /* Call the 'active' setter
+     */
+    val = jk2_config_replaceProperties(env, cfg->map, cfg->map->pool, val);
+
+    /* fprintf( stderr, "config.setProperty2 %s %s %s \n", mbean->name, name, val ); */
+
+    /** Used for future replacements
+     */
+    if (multiValue) {
+        cfg->map->add(env, cfg->map, pname, val);
+    }
+    else {
+        cfg->map->put(env, cfg->map, pname, val, NULL);
+    }
+
+    if (cfg->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "config: set %s / %s / %#lx / %s = %s\n", mbean->name,
+                      name, mbean, pname, val);
+
+    if (strcmp(name, "name") == 0) {
+        return JK_OK;
+    }
+    if (strcmp(name, "ver") == 0) {
+        mbean->ver = atol(val);
+        return JK_OK;
+    }
+    if (strcmp(name, "debug") == 0) {
+        mbean->debug = atoi(val);
+        if (mbean->setAttribute) {
+            mbean->setAttribute(env, mbean, name, val);
+        }
+        return JK_OK;
+    }
+    if (strcmp(name, "disabled") == 0) {
+        /* int oldDisabled=mbean->disabled; */
+
+        mbean->disabled = atoi(val);
+        if (mbean->setAttribute) {
+            mbean->setAttribute(env, mbean, name, val);
+        }
+
+        /* State change ... - it needs to be handled at the end */
+        /*         if( oldDisabled != mbean->disabled ) { */
+        /*         } */
+        return JK_OK;
+    }
+    if (strcmp(name, "info") == 0) {
+        /* do nothing, this is a comment */
+        return JK_OK;
+    }
+
+    if ((mbean == cfg->mbean) &&
+        (strcmp(name, "file") == 0) && cfg->file != NULL) {
+        /* 'file' property on ourself, avoid rec.
+         */
+        if (cfg->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "config.setAttribute() ignore %s %s %s\n",
+                          mbean->name, name, val);
+
+        return JK_OK;
+    }
+
+    if (mbean->setAttribute) {
+        int rc = mbean->setAttribute(env, mbean, name, val);
+        if (rc != JK_OK) {
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "config.setAttribute() Error setting %s %s %s\n",
+                          mbean->name, name, val);
+        }
+        if (cfg->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "config.setAttribute() %d setting %s %s %s\n",
+                          cfg->mbean->debug, mbean->name, name, val);
+        return rc;
+    }
+    return JK_ERR;
+}
+
+int jk2_config_setPropertyString(jk_env_t *env, jk_config_t *cfg,
+                                 char *name, char *value)
+{
+    jk_bean_t *mbean;
+    int status;
+    char *objName = NULL;
+    char *propName = NULL;
+
+    /* fprintf( stderr, "setPropertyString %s %s \n", name, value ); */
+
+    status =
+        jk2_config_processBeanPropertyString(env, cfg, name, &objName,
+                                             &propName);
+    if (status != JK_OK) {
+        /* Unknown properties ends up in our config, as 'unclaimed' or global */
+        cfg->setProperty(env, cfg, cfg->mbean, name, value);
+        return status;
+    }
+
+    /** Replace properties in the object name */
+    objName =
+        jk2_config_replaceProperties(env, cfg->map, cfg->map->pool, objName);
+
+    mbean = env->getBean(env, objName);
+    if (mbean == NULL) {
+        mbean = env->createBean(env, cfg->pool, objName);
+    }
+
+    if (mbean == NULL) {
+        /* Can't create it, save the value in our map */
+        cfg->setProperty(env, cfg, cfg->mbean, name, value);
+        return JK_ERR;
+    }
+
+    if (mbean->settings == NULL)
+        jk2_map_default_create(env, &mbean->settings, cfg->pool);
+
+    return cfg->setProperty(env, cfg, mbean, propName, value);
+}
+
+
+
+/**
+ *  Replace $(property) in value.
+ * 
+ */
+char *jk2_config_replaceProperties(jk_env_t *env, jk_map_t *m,
+                                   struct jk_pool *resultPool, char *value)
+{
+    char *rc;
+    char *env_start;
+    int rec = 0;
+    int didReplace = JK_FALSE;
+    rc = value;
+    env_start = value;
+
+    while (env_start = strstr(env_start, "${")) {
+        char *env_end = strstr(env_start, "}");
+        if (rec++ > 20)
+            return rc;
+        if (env_end) {
+            char env_name[LENGTH_OF_LINE + 1] = "";
+            char *env_value;
+
+            strncpy(env_name, env_start + 2, (env_end - env_start) - 2);
+
+            env_value = m->get(env, m, env_name);
+            if (env_value == NULL) {
+                env_value = getenv(env_name);
+            }
+            /* fprintf(stderr, "XXXjk_map %s %s \n", env_name, env_value ); */
+
+            if (env_value != NULL) {
+                int offset = 0;
+                /* tmp allocations in tmpPool */
+                char *new_value = env->tmpPool->calloc(env, env->tmpPool,
+                                                       (strlen(rc) +
+                                                        strlen(env_value)));
+                if (!new_value) {
+                    break;
+                }
+                if (env_start == rc) {
+                    new_value[0] = '\0';
+                }
+                else {
+                    strncpy(new_value, rc, env_start - rc);
+                }
+                /* fprintf(stderr, "XXX %s %s %s\n", new_value, env_value, env_end + 1 ); */
+
+                strcat(new_value, env_value);
+                strcat(new_value, env_end + 1);
+                offset = env_start - rc + strlen(env_value);
+                rc = new_value;
+                /* Avoid recursive subst */
+                env_start = rc + offset;
+                didReplace = JK_TRUE;
+            }
+            else {
+                env_start = env_end;
+            }
+        }
+        else {
+            break;
+        }
+    }
+
+    if (didReplace && resultPool != NULL && resultPool != env->tmpPool) {
+        /* Make sure the result is allocated in the right mempool.
+           tmpPool will be reset for each request.
+         */
+        rc = resultPool->pstrdup(env, resultPool, rc);
+    }
+
+    return rc;
+}
+
+/* -------------------- Reconfiguration -------------------- */
+
+/** cfgData has component names as keys and a map of attributes as value.
+ *  We'll create the beans and call the setters.
+ *  If this is not firstTime, we create new componens and modify those
+ *  with a lower 'ver'.
+ *
+ *  Note that _no_ object can be ever destroyed. You can 'disable' them,
+ *  but _never_ remove/destroy it. We work in a multithreaded environment,
+ *  and any removal may have disastrous consequences. Using critical
+ *  sections would drastically affect the performance.
+ */
+int jk2_config_processConfigData(jk_env_t *env, jk_config_t *cfg,
+                                 int firstTime)
+{
+    int i;
+    int rc = 0;
+
+    /* Set the config
+     */
+    for (i = 0; i < cfg->cfgData->size(env, cfg->cfgData); i++) {
+        char *name = cfg->cfgData->nameAt(env, cfg->cfgData, i);
+        rc = cfg->processNode(env, cfg, name, firstTime);
+    }
+
+    /* Init/stop components that need that. FirstTime will be handled by workerEnv, since
+       some components don't support dynamic config and need a specific order.
+     */
+    if (!firstTime) {
+        for (i = 0; i < env->_objects->size(env, env->_objects); i++) {
+            char *name = env->_objects->nameAt(env, env->_objects, i);
+            jk_bean_t *mbean = env->_objects->valueAt(env, env->_objects, i);
+
+            if (mbean == NULL)
+                continue;
+
+            /* New state ( == not initialized ) and disabled==0,
+               try to reinit */
+            if (mbean->state == JK_STATE_NEW && mbean->disabled == 0) {
+                int initOk = JK_OK;
+
+                if (mbean->init != NULL) {
+                    initOk = mbean->init(env, mbean);
+                    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                                  "config.update(): Starting %s %d\n", name,
+                                  initOk);
+                }
+                if (initOk == JK_OK) {
+                    mbean->state = JK_STATE_INIT;
+                }
+            }
+
+            /* Initialized state - and the config changed to disabled */
+            if (mbean->state == JK_STATE_INIT && mbean->disabled != 0) {
+                int initOk = JK_OK;
+
+                /* Stop */
+                if (mbean->destroy) {
+                    initOk = mbean->destroy(env, mbean);
+                    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                                  "config.update(): Stopping %s %d\n", name,
+                                  initOk);
+                }
+                if (initOk) {
+                    mbean->state = JK_STATE_NEW;
+                }
+            }
+        }
+    }
+    return rc;
+}
+
+/** This method will process one mbean configuration or reconfiguration.
+    The special case for firstTime will be eventually removed - it is needed because some
+    components may still depend on a specific startup order.
+
+    The goal is for each component to follow JMX patterns - you should be able to add / remove
+    jk components at runtime in any order.
+*/
+int jk2_config_processNode(jk_env_t *env, jk_config_t *cfg, char *name,
+                           int firstTime)
+{
+    int j;
+
+    jk_map_t *prefNode = cfg->cfgData->get(env, cfg->cfgData, name);
+    jk_bean_t *bean;
+    long ver = 0;
+    char *verString;
+    int newBean = 0;
+
+    if (cfg->mbean->debug > 5)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "config.setConfig():  process %s\n", name);
+
+    bean = env->getBean(env, name);
+    if (bean == NULL) {
+        if (cfg->mbean->debug > 0) {
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "config.setConfig():  Creating %s\n", name);
+        }
+        bean = env->createBean(env, cfg->pool, name);
+        newBean = 1;
+    }
+
+    if (bean == NULL) {
+        /* Can't create it, save the value in our map */
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "config.update(): Can't create %s\n", name);
+        return JK_ERR;
+    }
+
+    /* Don't call setters on objects that have the same ver.
+       This is just a workaround for components that are not reconfigurable.
+     */
+    verString = prefNode->get(env, prefNode, "ver");
+    if (!firstTime) {
+        /* No ver option - assume it didn't change */
+        if (verString == NULL && !newBean) {
+            return JK_OK;
+        }
+        if (verString != NULL) {
+            ver = atol(verString);
+
+            if (ver == bean->ver && !newBean) {
+                /* Object didn't change and is not new
+                 */
+                return JK_OK;
+            }
+        }
+    }
+
+    if (!firstTime)
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "config.update(): Updating %s in %d\n", name, getpid());
+
+    for (j = 0; j < prefNode->size(env, prefNode); j++) {
+        char *pname = prefNode->nameAt(env, prefNode, j);
+        char *pvalue = prefNode->valueAt(env, prefNode, j);
+
+        cfg->setProperty(env, cfg, bean, pname, pvalue);
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "config.update(): done %s\n", name);
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_config_file.c b/connectors/jk/native2/common/jk_config_file.c
new file mode 100644
index 0000000..ea00854
--- /dev/null
+++ b/connectors/jk/native2/common/jk_config_file.c
@@ -0,0 +1,404 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Description: File based configuration.
+ *
+ * @author: Gal Shachor <shachor@il.ibm.com>                           
+ * @author: Costin Manolache
+ */
+
+#ifdef AS400
+#include "apr_xlate.h"
+#endif
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_config.h"
+
+#define LENGTH_OF_LINE    (1024)
+
+/* 
+ */
+static int jk2_config_file_saveConfig(jk_env_t *env,
+                                      jk_config_t *cfg, char *workerFile)
+{
+    FILE *fp;
+    int i, j;
+
+    if (workerFile == NULL)
+        workerFile = cfg->file;
+
+    if (workerFile == NULL)
+        return JK_ERR;
+
+#ifdef AS400
+    fp = fopen(workerFile, "w, o_ccsid=0");
+#else
+    fp = fopen(workerFile, "w");
+#endif
+
+    if (fp == NULL)
+        return JK_ERR;
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "config.save(): Saving %s\n", workerFile);
+
+    /* We'll save only the objects/properties that were set
+       via config, and to the original 'string'. That keeps the config
+       small ( withou redundant ) and close to the original. Comments
+       will be saved/loaded later.
+     */
+    for (i = 0; i < env->_objects->size(env, env->_objects); i++) {
+        char *name = env->_objects->nameAt(env, env->_objects, i);
+        jk_bean_t *mbean = env->_objects->valueAt(env, env->_objects, i);
+
+        if (mbean == NULL || mbean->settings == NULL)
+            continue;
+
+        if (strcmp(name, mbean->name) != 0) {
+            /* It's an alias. */
+            continue;
+        }
+        fprintf(fp, "[%s]\n", mbean->name);
+
+        for (j = 0; j < mbean->settings->size(env, mbean->settings); j++) {
+            char *pname = mbean->settings->nameAt(env, mbean->settings, j);
+            /* Don't save redundant information */
+            if (strcmp(pname, "name") != 0) {
+                fprintf(fp, "%s=%s\n",
+                        pname,
+                        mbean->settings->valueAt(env, mbean->settings, j));
+            }
+        }
+        fprintf(fp, "\n");
+    }
+
+    fclose(fp);
+
+    return JK_OK;
+}
+
+/* ==================== */
+/*  Reading / parsing. 
+ */
+
+static void jk2_trim_prp_comment(char *prp)
+{
+#ifdef AS400
+    char *comment;
+    /* lots of lines that translate a '#' realtime deleted   */
+    comment = strchr(prp, *APR_NUMBERSIGN);
+#else
+    char *comment = strchr(prp, '#');
+#endif
+    if (comment) {
+        *comment = '\0';
+    }
+}
+
+static int jk2_trim(char *s)
+{
+    int i;
+
+    for (i = strlen(s) - 1; (i >= 0) && isspace(s[i]); i--);
+
+    s[i + 1] = '\0';
+
+    for (i = 0; ('\0' != s[i]) && isspace(s[i]); i++);
+
+    if (i > 0) {
+        strcpy(s, &s[i]);
+    }
+
+    return strlen(s);
+}
+
+
+
+int jk2_config_file_parseProperty(jk_env_t *env, jk_map_t *m, char **section,
+                                  char *prp)
+{
+    int rc = JK_ERR;
+    char *v;
+    jk_map_t *prefNode = NULL;
+
+    jk2_trim_prp_comment(prp);
+
+    if (jk2_trim(prp) == 0)
+        return JK_OK;
+
+    /* Support windows-style 'sections' - for cleaner config
+     */
+    if ((prp[0] == '[')) {
+        v = strchr(prp, ']');
+        *v = '\0';
+        jk2_trim(v);
+        prp++;
+
+        *section = m->pool->pstrdup(env, m->pool, prp);
+
+        jk2_map_default_create(env, &prefNode, m->pool);
+
+        m->add(env, m, *section, prefNode);
+
+        return JK_OK;
+    }
+
+    v = strchr(prp, '=');
+    if (v == NULL)
+        return JK_OK;
+
+    *v = '\0';
+    v++;
+
+    if (strlen(v) == 0 || strlen(prp) == 0)
+        return JK_OK;
+
+    if (*section != NULL) {
+        prefNode = m->get(env, m, *section);
+    }
+    else {
+        prefNode = m;
+    }
+
+    if (prefNode == NULL)
+        return JK_ERR;
+
+    /* fprintf(stderr, "Adding [%s] %s=%s\n", cfg->section, prp, v ); */
+    prefNode->add(env, prefNode, m->pool->pstrdup(env, m->pool, prp),
+                  m->pool->pstrdup(env, m->pool, v));
+
+    return JK_OK;
+}
+
+
+
+/** Read the config file
+ */
+int jk2_config_file_read(jk_env_t *env, jk_map_t *m, const char *file)
+{
+    FILE *fp;
+    char buf[LENGTH_OF_LINE + 1];
+    char *prp;
+    char *section = NULL;
+    if (m == NULL || file == NULL)
+        return JK_ERR;
+
+#ifdef AS400
+    fp = fopen(file, "r, o_ccsid=0");
+#else
+    fp = fopen(file, "r");
+#endif
+
+    if (fp == NULL)
+        return JK_ERR;
+
+    while (NULL != (prp = fgets(buf, LENGTH_OF_LINE, fp))) {
+        jk2_config_file_parseProperty(env, m, &section, prp);
+    }
+
+    fclose(fp);
+    return JK_OK;
+}
+
+
+static int jk2_config_file_readFile(jk_env_t *env,
+                                    jk_config_t *cfg,
+                                    int *didReload, int firstTime)
+{
+    int rc;
+    struct stat statbuf;
+
+    if (didReload != NULL)
+        *didReload = JK_FALSE;
+
+    if (cfg->file == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "config.update(): No config file\n");
+        return JK_ERR;
+    }
+
+    rc = stat(cfg->file, &statbuf);
+    if (rc == -1) {
+        /* Don't report it as an error - it's a common case */
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "config.update(): Can't find config file %s\n",
+                      cfg->file);
+        return JK_OK;
+    }
+
+    if (!firstTime && statbuf.st_mtime < cfg->mtime) {
+        if (cfg->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "config.update(): No reload needed %s %ld %ld\n",
+                          cfg->file, cfg->mtime, statbuf.st_mtime);
+        return JK_OK;
+    }
+
+    if (cfg->cs == NULL) {
+        jk_bean_t *jkb =
+            env->createBean2(env, cfg->mbean->pool, "threadMutex", NULL);
+        if (jkb != NULL && jkb->object != NULL) {
+            cfg->cs = jkb->object;
+            jkb->init(env, jkb);
+        }
+    }
+
+    if (cfg->cs != NULL)
+        cfg->cs->lock(env, cfg->cs);
+
+    /* Check if another thread has updated the config */
+
+    rc = stat(cfg->file, &statbuf);
+    if (rc == -1) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "config.update(): Can't find config file %s",
+                      cfg->file);
+        if (cfg->cs != NULL)
+            cfg->cs->unLock(env, cfg->cs);
+        return JK_ERR;
+    }
+
+    if (!firstTime && statbuf.st_mtime <= cfg->mtime) {
+        if (cfg->cs != NULL)
+            cfg->cs->unLock(env, cfg->cs);
+        return JK_OK;
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "cfg.update() Updating config %s %d %d\n",
+                  cfg->file, cfg->mtime, statbuf.st_mtime);
+
+    /** The map will be lost - all objects must be saved !
+     */
+    jk2_map_default_create(env, &cfg->cfgData, env->tmpPool);
+
+    rc = jk2_config_file_read(env, cfg->cfgData, cfg->file);
+
+    if (rc == JK_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "config.setConfig():  Reading properties %s %d\n",
+                      cfg->file, cfg->cfgData->size(env, cfg->cfgData));
+    }
+    else {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "config.setConfig(): Error reading properties %s\n",
+                      cfg->file);
+        if (cfg->cs != NULL)
+            cfg->cs->unLock(env, cfg->cs);
+        return JK_ERR;
+    }
+
+    rc = jk2_config_processConfigData(env, cfg, firstTime);
+
+    if (didReload != NULL)
+        *didReload = JK_TRUE;
+
+    cfg->mtime = statbuf.st_mtime;
+
+    if (cfg->cs != NULL)
+        cfg->cs->unLock(env, cfg->cs);
+
+    return rc;
+}
+
+
+static int jk2_config_file_update(jk_env_t *env,
+                                  jk_config_t *cfg, int *didReload)
+{
+    return jk2_config_file_readFile(env, cfg, didReload, JK_FALSE);
+}
+
+/** Set a property for this config object
+ */
+static int JK_METHOD jk2_config_file_setAttribute(struct jk_env *env,
+                                                  struct jk_bean *mbean,
+                                                  char *name, void *valueP)
+{
+    jk_config_t *cfg = mbean->object;
+    char *value = valueP;
+
+    if (strcmp(name, "file") == 0) {
+        cfg->file = value;
+        return jk2_config_file_readFile(env, cfg, NULL, JK_TRUE);
+    }
+    else if (strcmp(name, "debugEnv") == 0) {
+        env->debug = atoi(value);
+    }
+    else if (strcmp(name, "save") == 0) {
+        return jk2_config_file_saveConfig(env, cfg, cfg->file);
+    }
+    else {
+        return JK_ERR;
+    }
+    return JK_OK;
+}
+
+/** Get a property for this config object
+ */
+static void *JK_METHOD jk2_config_file_getAttribute(struct jk_env *env,
+                                                    struct jk_bean *mbean,
+                                                    char *name)
+{
+    jk_config_t *cfg = mbean->object;
+
+    if (strcmp(name, "file") == 0) {
+        return cfg->file;
+    }
+    else if (strcmp(name, "ver") == 0) {
+        return 0;
+    }
+    else {
+        return "";
+    }
+}
+
+static char *myGetAttInfo[] = { "ver", "file", NULL };
+static char *mySetAttInfo[] = { "ver", "file", "save", NULL };
+
+int JK_METHOD jk2_config_file_factory(jk_env_t *env, jk_pool_t *pool,
+                                      jk_bean_t *result,
+                                      const char *type, const char *name)
+{
+    jk_config_t *_this;
+
+    _this = (jk_config_t *)pool->alloc(env, pool, sizeof(jk_config_t));
+    if (_this == NULL)
+        return JK_ERR;
+
+    _this->pool = pool;
+    result->getAttributeInfo = myGetAttInfo;
+    result->setAttributeInfo = mySetAttInfo;
+    _this->ver = 0;
+
+    _this->setPropertyString = jk2_config_setPropertyString;
+    _this->setProperty = jk2_config_setProperty;
+    _this->processNode = jk2_config_processNode;
+
+    _this->cs = NULL;
+
+    _this->update = jk2_config_file_update;
+    _this->save = jk2_config_file_saveConfig;
+
+    result->object = _this;
+    result->setAttribute = jk2_config_file_setAttribute;
+    result->getAttribute = jk2_config_file_getAttribute;
+    _this->mbean = result;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_endpoint.c b/connectors/jk/native2/common/jk_endpoint.c
new file mode 100644
index 0000000..e35b83d
--- /dev/null
+++ b/connectors/jk/native2/common/jk_endpoint.c
@@ -0,0 +1,160 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Endpoint represents a connection between the jk server and client.
+ *  ( tomcat and apache )
+ *
+ * @author Costin Manolache
+ */
+
+#include "jk_global.h"
+#include "jk_pool.h"
+#include "jk_channel.h"
+#include "jk_msg.h"
+#include "jk_logger.h"
+#include "jk_handler.h"
+#include "jk_service.h"
+#include "jk_env.h"
+#include "jk_objCache.h"
+#include "jk_registry.h"
+
+
+static int JK_METHOD jk2_endpoint_init(jk_env_t *env, jk_bean_t *bean)
+{
+    jk_endpoint_t *ep = (jk_endpoint_t *)bean->object;
+    jk_stat_t *stats;
+    jk_workerEnv_t *wEnv = ep->workerEnv;
+
+    /* alloc it inside the shm if possible */
+    if (wEnv->epStat == NULL) {
+        if (wEnv->shm != NULL && wEnv->childId >= 0) {
+            char shmName[128];
+            apr_snprintf(shmName, 128, "epStat.%d", wEnv->childId);
+
+            wEnv->epStat =
+                wEnv->shm->createSlot(env, wEnv->shm, shmName, 8096);
+            if (wEnv->epStat == NULL) {
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "workerEnv.init() create slot %s failed\n",
+                              shmName);
+                /* If epStat is NULL - no statistics will be collected, but the server should still work.
+                 */
+                /*return JK_ERR; */
+            }
+            else {
+                wEnv->epStat->structCnt = 0;
+                env->l->jkLog(env, env->l, JK_LOG_INFO,
+                              "workerEnv.init() create slot %s\n", shmName);
+            }
+        }
+    }
+
+    if (wEnv->epStat != NULL && wEnv->childId >= 0) {
+        jk_stat_t *statArray = (jk_stat_t *)wEnv->epStat->data;
+        stats = &statArray[ep->mbean->id];
+        ep->workerEnv->epStat->structSize = sizeof(jk_stat_t);
+        ep->workerEnv->epStat->structCnt = ep->mbean->id + 1;
+        if (ep->worker != NULL && ep->worker->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "SHM stats %d %#lx %#lx %s %s childId=%d\n",
+                          ep->mbean->id, ep->workerEnv->epStat->data, stats,
+                          ep->mbean->localName, ep->mbean->name,
+                          ep->workerEnv->childId);
+    }
+    else {
+        stats =
+            (jk_stat_t *)ep->mbean->pool->calloc(env, ep->mbean->pool,
+                                                 sizeof(jk_stat_t));
+        if (ep->worker != NULL && ep->worker->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "Local stats %d %#lx %d\n", ep->mbean->id,
+                          ep->workerEnv->epStat, ep->workerEnv->childId);
+    }
+
+    ep->stats = stats;
+
+    ep->stats->reqCnt = 0;
+    ep->stats->errCnt = 0;
+    ep->stats->maxTime = 0;
+    ep->stats->totalTime = 0;
+
+    bean->state = JK_STATE_INIT;
+
+    return JK_OK;
+}
+
+static char *getAttInfo[] = { "id", NULL };
+
+static void *JK_METHOD jk2_endpoint_getAttribute(jk_env_t *env,
+                                                 jk_bean_t *bean, char *name)
+{
+    jk_endpoint_t *ep = (jk_endpoint_t *)bean->object;
+
+    if (strcmp(name, "id") == 0) {
+        return "1";
+    }
+    else if (strcmp("inheritGlobals", name) == 0) {
+        return "";
+    }
+    return NULL;
+}
+
+
+
+int JK_METHOD
+jk2_endpoint_factory(jk_env_t *env, jk_pool_t *pool,
+                     jk_bean_t *result, const char *type, const char *name)
+{
+    jk_endpoint_t *e = (jk_endpoint_t *)pool->calloc(env, pool,
+                                                     sizeof(jk_endpoint_t));
+    int epId;
+
+    if (e == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "endpoint.factory() OutOfMemoryException\n");
+        return JK_ERR;
+    }
+
+    /* Init message storage areas. Protocol specific.
+     */
+    e->request = jk2_msg_ajp_create(env, pool, 0);
+    e->reply = jk2_msg_ajp_create(env, pool, 0);
+    e->post = jk2_msg_ajp_create(env, pool, 0);
+
+    e->readBuf = pool->alloc(env, pool, 8096);
+    e->bufPos = 0;
+
+    result->init = jk2_endpoint_init;
+
+    e->sd = -1;
+    e->recoverable = JK_TRUE;
+    e->cPool = pool->create(env, pool, HUGE_POOL_SIZE);
+    e->stats = NULL;
+    e->channelData = NULL;
+    e->currentRequest = NULL;
+    e->worker = NULL;
+    epId = atoi(result->localName);
+
+    result->object = e;
+    result->getAttributeInfo = getAttInfo;
+    result->getAttribute = jk2_endpoint_getAttribute;
+    e->mbean = result;
+
+    e->workerEnv = env->getByName(env, "workerEnv");
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_env.c b/connectors/jk/native2/common/jk_env.c
new file mode 100644
index 0000000..207897f
--- /dev/null
+++ b/connectors/jk/native2/common/jk_env.c
@@ -0,0 +1,526 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "jk_global.h"
+#include "jk_env.h"
+#include "jk_objCache.h"
+
+jk_env_t *jk_env_globalEnv;
+void *jkGlobalAprPool = NULL;
+
+/* Private methods 
+*/
+static void jk2_env_initEnv(jk_env_t *env, char *id);
+
+/* We should have one env per thread to avoid sync problems. 
+   The env provides access to tmp pools, exception 
+*/
+
+/* -------------------- Env management -------------------- */
+
+static void *JK_METHOD jk2_env_getAprPool(jk_env_t *env)
+{
+    /* We don't want to have to recreate the scoreboard after
+     * restarts, so we'll create a global pool and never clean it.
+     */
+    if (jkGlobalAprPool == NULL) {
+        int rc;
+
+        rc = apr_pool_create((apr_pool_t **) & jkGlobalAprPool, NULL);
+        if (rc != APR_SUCCESS || jkGlobalAprPool == NULL) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "Unable to create global apr pool\n");
+            return NULL;
+        }
+    }
+    return jkGlobalAprPool;
+}
+
+void JK_METHOD jk2_env_setAprPool(jk_env_t *env, void *aprPool)
+{
+    jkGlobalAprPool = aprPool;
+}
+
+/** Public method, creates/get the global env
+ */
+jk_env_t *JK_METHOD jk2_env_getEnv(char *id, jk_pool_t *pool)
+{
+    if (jk_env_globalEnv == NULL) {
+        if (pool == NULL)
+            return NULL;
+        jk_env_globalEnv =
+            (jk_env_t *)pool->calloc(NULL, pool, sizeof(jk_env_t));
+        jk_env_globalEnv->globalPool = pool;
+        jk2_env_initEnv((jk_env_t *)jk_env_globalEnv, id);
+
+        /* fprintf( stderr, "env: top level env %#lx\n", jk_env_globalEnv); */
+    }
+    return jk_env_globalEnv;
+}
+
+
+/** Get a local env - either a new one or a recycled one
+ *  XXX Try TLD too 
+ */
+static jk_env_t *JK_METHOD jk2_env_get(jk_env_t *parentEnv)
+{
+    jk_env_t *env =
+        (jk_env_t *)parentEnv->envCache->get(parentEnv, parentEnv->envCache);
+    if (env == NULL) {
+        jk_pool_t *parentPool = parentEnv->globalPool;
+        env =
+            (jk_env_t *)parentPool->calloc(parentEnv, parentPool,
+                                           sizeof(jk_env_t));
+
+        env->tmpPool =
+            parentPool->create(parentEnv, parentPool, HUGE_POOL_SIZE);
+
+        env->registerFactory = parentEnv->registerFactory;
+        env->getByName = parentEnv->getByName;
+        env->getByName2 = parentEnv->getByName2;
+        env->getBean2 = parentEnv->getBean2;
+        env->getBean = parentEnv->getBean;
+        env->alias = parentEnv->alias;
+        env->createBean2 = parentEnv->createBean2;
+        env->createBean = parentEnv->createBean;
+        env->getEnv = parentEnv->getEnv;
+        env->releaseEnv = parentEnv->releaseEnv;
+        env->jkClearException = parentEnv->jkClearException;
+        env->jkException = parentEnv->jkException;
+        env->getAprPool = parentEnv->getAprPool;
+        env->setAprPool = parentEnv->setAprPool;
+
+        env->_registry = parentEnv->_registry;
+        env->_objects = parentEnv->_objects;
+        env->l = parentEnv->l;
+        env->globalPool = parentEnv->globalPool;
+        env->envCache = parentEnv->envCache;
+        env->debug = parentEnv->debug;
+        env->soName = parentEnv->soName;
+
+        if (env->debug > 0) {
+            if (env->l == NULL)
+                fprintf(stderr, "env:Create child env %#lx %#lx\n", parentEnv,
+                        env);
+            else
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "env:Create child env %#lx %#lx\n", parentEnv,
+                              env);
+        }
+    }
+    return env;
+}
+
+/** Release the env ( clean and recycle )
+ */
+static int JK_METHOD jk2_env_recycleEnv(jk_env_t *env)
+{
+    env->tmpPool->reset(env, env->tmpPool);
+    env->jkClearException(env);
+    return JK_OK;
+}
+
+/** Release the env ( clean and recycle )
+ */
+static int JK_METHOD jk2_env_put(jk_env_t *parent, jk_env_t *chld)
+{
+    jk2_env_recycleEnv(chld);
+    return parent->envCache->put(parent, parent->envCache, chld);
+}
+
+/* -------------------- Object management -------------------- */
+
+/** Create a jk component, using only the name.
+ *  Now things are simpler - the 'type' is the prefix, separated by ':' - no
+ *  guessing involved.
+ */
+static jk_bean_t *jk2_env_createBean(jk_env_t *env, jk_pool_t *pool,
+                                     char *objName)
+{
+    char *type = NULL;
+/*    void *obj; */
+    char *localName;
+
+    localName = strchr(objName, ':');
+    if (localName == NULL) {
+        type = objName;
+    }
+    else {
+        /* Funny pointer arithmetic. I hope I got it right */
+        type =
+            env->tmpPool->calloc(env, env->tmpPool, localName - objName + 2);
+        strncpy(type, objName, localName - objName);
+        localName++;
+    }
+
+    return env->createBean2(env, pool, type, localName);
+}
+
+/** Create a component using type and local part ( pre-cooked ).
+ */
+static jk_bean_t *jk2_env_createBean2(jk_env_t *env, jk_pool_t *pool,
+                                      char *type, char *localName)
+{
+    jk_env_objectFactory_t fac;
+    jk_bean_t *result = NULL;
+    jk_pool_t *workerPool;
+    char *name;
+    int i;
+
+    if (localName != NULL)
+        result = env->getBean2(env, type, localName);
+
+    if (result != NULL)
+        return result;
+
+    if (pool == NULL) {
+        pool = env->globalPool;
+    }
+
+    if (type == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "env.createBean2(): NullPointerException\n");
+        return NULL;
+    }
+
+    if (strcmp("disabled", type) == 0) {
+        return NULL;
+    }
+
+    /* if( localName!=NULL && strncmp( localName, type, strlen( type )) == 0 ) { */
+    /* Common error, make it 'localName' */
+    /*         if( strcmp( type, localName ) == 0 ) { */
+    /*             localName=""; */
+    /*         } else { */
+    /*             localName= localName + strlen(type) + 1; */
+    /*         } */
+    /*     } */
+
+    if (env->debug > 0) {
+        if (env->l != NULL) {
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "env.createBean2(): Create [%s] %s\n", type,
+                          localName);
+        }
+        else {
+            fprintf(stderr, "env.createBean2(): Create [%s] %s\n", type,
+                    localName);
+        }
+    }
+
+    fac =
+        (jk_env_objectFactory_t) env->_registry->get(env, env->_registry,
+                                                     type);
+
+    if (fac == NULL) {
+        if (env->l) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "env.createBean2(): Error getting factory for [%s] %s\n",
+                          type, localName);
+        }
+        else {
+            fprintf(stderr, "Error getting factory for %s \n", type);
+        }
+        return NULL;
+    }
+
+    workerPool = pool->create(env, pool, HUGE_POOL_SIZE);
+
+
+    /** Generate a unique name if none is specified
+     */
+    if (localName == NULL) {
+        localName = workerPool->calloc(env, workerPool, 10);
+        sprintf((char *)localName, "%d", jk_env_globalEnv->id++);
+    }
+
+    name =
+        workerPool->calloc(env, workerPool,
+                           strlen(type) + strlen(localName) + 2);
+    strcpy(name, type);
+    strcat(name, ":");
+    strcat(name, localName);
+
+    result =
+        (jk_bean_t *)workerPool->calloc(env, workerPool, sizeof(jk_bean_t));
+    result->pool = workerPool;
+    result->type = workerPool->pstrdup(env, workerPool, type);
+    result->name = workerPool->pstrdup(env, workerPool, name);
+    result->localName = workerPool->pstrdup(env, workerPool, localName);
+    result->debug = 0;
+    result->state = JK_STATE_NEW;
+    result->disabled = JK_FALSE;
+    result->settings = NULL;
+    result->getAttributeInfo = NULL;
+    result->setAttributeInfo = NULL;
+
+    fac(env, workerPool, result, result->type, result->name);
+
+    if (result->object == NULL) {
+        if (env->l) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "env.createBean2(): Factory error creating %s ( %s, %s)\n",
+                          name, type, localName);
+        }
+        else {
+            fprintf(stderr,
+                    "env.createBean2(): Factory error creating %s ( %s, %s)\n",
+                    name, type, localName);
+        }
+        return NULL;
+    }
+
+    if (env->debug > 0) {
+        if (env->l == NULL)
+            fprintf(stderr, "env.createBean2(): register %s %#lx\n",
+                    result->name, result->object);
+        else
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "env.createBean2(): register %s %#lx\n",
+                          result->name, result->object);
+    }
+
+    jk_env_globalEnv->_objects->put(env, jk_env_globalEnv->_objects,
+                                    result->name, result, NULL);
+
+    for (i =
+         jk_env_globalEnv->_objects->size(env,
+                                          jk_env_globalEnv->_objects) - 1;
+         i >= 0; i--) {
+        if (jk_env_globalEnv->_objects->
+            valueAt(env, jk_env_globalEnv->_objects, i) == result) {
+            result->objId = i;
+            break;
+        }
+    }
+
+    if (strcmp(localName, "") == 0) {
+        /* "" for local name is used as 'default'. Allow "type" as an alias for "type:"
+         */
+        jk_env_globalEnv->_objects->put(env, jk_env_globalEnv->_objects,
+                                        result->type, result, NULL);
+    }
+
+    return result;
+}
+
+/** Define an alias, for simpler config / less typing
+ */
+static void JK_METHOD jk2_env_alias(jk_env_t *env, const char *name,
+                                    const char *alias)
+{
+    jk_bean_t *jkb = env->getBean(env, name);
+
+    if (jkb == NULL) {
+        if (env->l == NULL) {
+            if (env->debug > 0)
+                fprintf(stderr, "env.alias(): Not found %s\n", name);
+        }
+        else {
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "env.alias(): Not found %s\n", name);
+        }
+        return;
+    }
+
+    if (env->debug > 0) {
+        if (env->l == NULL)
+            fprintf(stderr, "env.alias(): alias %s %s\n", name, alias);
+        else
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "env.alias(): alias %s %s\n", name, alias);
+    }
+
+    jk_env_globalEnv->_objects->put(env, jk_env_globalEnv->_objects, alias,
+                                    jkb, NULL);
+}
+
+/** Get the object by name. Returns the real object, not the wrapper
+ */
+static void *JK_METHOD jk2_env_getByName(jk_env_t *env, const char *name)
+{
+    jk_bean_t *result = env->getBean(env, name);
+
+    if (result == NULL) {
+        if (env->debug > 0) {
+            if (env->l == NULL)
+                fprintf(stderr, "env.getByName(): Can't find %#lx %s\n", env,
+                        name);
+            else
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "env.getByName(): Can't find %#lx %s\n", env,
+                              name);
+        }
+        return NULL;
+    }
+
+    if (env->debug > 0) {
+        if (env->l == NULL)
+            fprintf(stderr, "env.getByName(): Get by name %s %#lx\n", name,
+                    result->object);
+        else
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "env.getByName(): Get by name %s %#lx\n", name,
+                          result->object);
+    }
+    return result->object;
+}
+
+static void *JK_METHOD jk2_env_getByName2(jk_env_t *env, const char *type,
+                                          const char *localName)
+{
+    jk_bean_t *result = env->getBean2(env, type, localName);
+    if (result == NULL)
+        return NULL;
+    return result->object;
+}
+
+/** Get the wrapper for the named object
+ */
+static jk_bean_t *JK_METHOD jk2_env_getBean(jk_env_t *env, const char *name)
+{
+    if (name == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "env.getBean(): NullPointerException\n");
+        return NULL;
+    }
+
+    return (jk_bean_t *)env->_objects->get(env, env->_objects, name);
+}
+
+static jk_bean_t *JK_METHOD jk2_env_getBean2(jk_env_t *env, const char *type,
+                                             const char *localName)
+{
+    char *name;
+    if (type == NULL || localName == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "env.getBean2(): NullPointerException\n");
+        return NULL;
+    }
+
+    name =
+        env->tmpPool->calloc(env, env->tmpPool,
+                             strlen(type) + strlen(localName) + 2);
+    strcpy(name, type);
+    strcat(name, ":");
+    strcat(name, localName);
+
+    return (jk_bean_t *)env->_objects->get(env, env->_objects, name);
+}
+
+/** Register the type and the factory
+ */
+static void JK_METHOD jk2_env_registerFactory(jk_env_t *env,
+                                              const char *type,
+                                              jk_env_objectFactory_t fact)
+{
+    if (type == NULL || fact == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "env.registerFactory(): NullPointerException\n");
+        return;
+    }
+    env->_registry->put(env, env->_registry, (char *)type, (void *)fact,
+                        NULL);
+}
+
+/* -------------------- Exceptions -------------------- */
+
+/* Exceptions.
+ */
+static void JK_METHOD jkThrow(jk_env_t *env,
+                              const char *file, int line,
+                              const char *type, const char *fmt, ...)
+{
+    va_list args;
+/*    char *buf; */
+
+    va_start(args, fmt);
+    env->l->jkVLog(env, env->l, file, line, JK_LOG_ERROR_LEVEL, fmt, args);
+    va_end(args);
+
+}
+
+/** re-throw the exception and record the current pos.
+ *  in the stack trace
+ *  XXX Not implemented/not used
+ */
+static void JK_METHOD jkReThrow(jk_env_t *env, const char *file, int line)
+{
+    /* Nothing yet. It should record the file/line for stack trace */
+}
+
+/* Last exception that occured
+ */
+static jk_exception_t *JK_METHOD jk_env_jkException(jk_env_t *env)
+{
+    return env->lastException;
+}
+
+/** Clear the exception state
+ */
+static void JK_METHOD jk_env_jkClearException(jk_env_t *env)
+{
+    env->lastException = NULL;
+}
+
+/* -------------------- Util -------------------- */
+
+/* It should be in some util class */
+
+char *JK_METHOD jk2_env_itoa(jk_env_t *env, int i)
+{
+    char *buf = env->tmpPool->calloc(env, env->tmpPool, 20);
+    sprintf(buf, "%d", i);
+    return buf;
+}
+
+/* -------------------- Init method -------------------- */
+
+
+static void jk2_env_initEnv(jk_env_t *env, char *id)
+{
+
+    env->registerFactory = jk2_env_registerFactory;
+    env->getByName = jk2_env_getByName;
+    env->getByName2 = jk2_env_getByName2;
+    env->getBean = jk2_env_getBean;
+    env->getBean2 = jk2_env_getBean2;
+    env->createBean2 = jk2_env_createBean2;
+    env->createBean = jk2_env_createBean;
+    env->alias = jk2_env_alias;
+    env->getEnv = jk2_env_get;
+    env->recycleEnv = jk2_env_recycleEnv;
+    env->releaseEnv = jk2_env_put;
+    env->debug = 0;
+    env->jkClearException = jk_env_jkClearException;
+    env->jkException = jk_env_jkException;
+    env->getAprPool = jk2_env_getAprPool;
+    env->setAprPool = jk2_env_setAprPool;
+
+    env->id = 0;
+
+    jk2_map_default_create(env, &env->_registry, env->globalPool);
+    jk2_map_default_create(env, &env->_objects, env->globalPool);
+
+    env->tmpPool =
+        env->globalPool->create(env, env->globalPool, HUGE_POOL_SIZE);
+
+    jk2_registry_init(env);
+
+    env->envCache = jk2_objCache_create(env, env->globalPool);
+    env->envCache->init(env, env->envCache, 64);
+    env->envCache->maxSize = -1;
+}
diff --git a/connectors/jk/native2/common/jk_handler_logon.c b/connectors/jk/native2/common/jk_handler_logon.c
new file mode 100644
index 0000000..0c8a0ea
--- /dev/null
+++ b/connectors/jk/native2/common/jk_handler_logon.c
@@ -0,0 +1,223 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Description: AJP14 Login handler
+ * Author:      Henri Gomez <hgomez@apache.org>
+ * Version:     $Revision$                                          
+ */
+
+#include "jk_global.h"
+#include "jk_pool.h"
+#include "jk_msg.h"
+#include "jk_md5.h"
+#include "jk_logger.h"
+#include "jk_service.h"
+#include "jk_env.h"
+#include "jk_handler.h"
+#include "jk_registry.h"
+
+/* Private definitions */
+
+/*
+ * Third Login Phase (web server -> servlet engine), md5 of seed + secret is sent
+ */
+#define AJP14_LOGCOMP_CMD	(unsigned char)0x12
+
+/* web-server want context info after login */
+#define AJP14_CONTEXT_INFO_NEG      0x80000000
+
+/* web-server want context updates */
+#define AJP14_CONTEXT_UPDATE_NEG    0x40000000
+
+/* communication could use AJP14 */
+#define AJP14_PROTO_SUPPORT_AJP14_NEG   0x00010000
+
+#define AJP14_ENTROPY_SEED_LEN		32      /* we're using MD5 => 32 chars */
+#define AJP14_COMPUTED_KEY_LEN		32      /* we're using MD5 also */
+
+/*
+ * Decode the Login Command
+ *
+ * +-------------------------+---------------------------+
+ * | LOGIN SEED CMD (1 byte) | MD5 of entropy (String) |
+ * +-------------------------+---------------------------+
+ *
+ * Build the reply: 
+ *   byte    LOGCOMP
+ *   String  MD5 of random + Secret key
+ *   long    negotiation
+ *   String  serverName
+ *
+ */
+static int JK_METHOD jk2_handler_login(jk_env_t *env, void *target,
+                                       jk_endpoint_t *ae, jk_msg_t *msg)
+{
+    int rc;
+    char *entropy;
+    char computedKey[AJP14_COMPUTED_KEY_LEN];
+    char *secret = ae->worker->secret;
+    long negociation;
+
+    entropy = msg->getString(env, msg);
+    if (entropy == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "Error ajp14_unmarshal_login_seed - can't get seed\n");
+        return JK_HANDLER_FATAL;
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "handle.logseed() received entropy %s\n", entropy);
+
+    jk2_md5((const unsigned char *)entropy,
+            (const unsigned char *)secret, computedKey);
+
+    env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                  "Into ajp14_compute_md5 (%s/%s) -> (%s)\n",
+                  entropy, secret, computedKey);
+
+    msg->reset(env, msg);
+
+    env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                  "Into ajp14_marshal_login_comp_into_msgb\n");
+
+    rc = msg->appendByte(env, msg, AJP14_LOGCOMP_CMD);
+    if (rc != JK_OK)
+        return JK_HANDLER_FATAL;
+
+    /* COMPUTED-SEED */
+    rc = msg->appendString(env, msg, (const char *)computedKey);
+    if (rc != JK_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "handler.loginSecret() error serializing computed secret\n");
+        return JK_HANDLER_FATAL;
+    }
+    negociation = (AJP14_CONTEXT_INFO_NEG | AJP14_PROTO_SUPPORT_AJP14_NEG);
+    msg->appendLong(env, msg, negociation);
+
+    rc = msg->appendString(env, msg, ae->worker->workerEnv->server_name);
+
+    if (rc != JK_OK)
+        return JK_HANDLER_FATAL;
+
+    return JK_HANDLER_RESPONSE;
+}
+
+
+/*
+ * Decode the LogOk Command. After that we're done, the connection is
+ * perfect and ready.
+ *
+ * +--------------------+------------------------+---------------------------
+ * | LOGOK CMD (1 byte) | NEGOCIED DATA (32bits) | SERVLET ENGINE INFO(CString)
+ * +--------------------+------------------------+---------------------------
+ *
+ */
+static int JK_METHOD jk2_handler_logok(jk_env_t *env, void *target,
+                                       jk_endpoint_t *ae, jk_msg_t *msg)
+{
+    unsigned long nego;
+    char *sname;
+/*    int rc; */
+
+    nego = msg->getLong(env, msg);
+
+    if (nego == 0xFFFFFFFF) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "handler.logok()  can't get negociated data\n");
+        return JK_HANDLER_FATAL;
+    }
+
+    sname = (char *)msg->getString(env, msg);
+
+    if (!sname) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "handler.logok() Error getting servlet engine name\n");
+        return JK_HANDLER_FATAL;
+    }
+
+    /* take care of removing previously allocated data */
+    /* XXXXXXXXX NEED A SUB POOL !!!! */
+    /*
+       if (ae->servletContainerName == NULL || 
+       strcmp( sname, ae->servletContainerName) != 0 )  {
+       ae->servletContainerName=
+       (char *)ae->pool->pstrdup( env, ae->pool,sname );
+       }
+
+       env->l->jkLog(env, env->l, JK_LOG_INFO,
+       "handler.logok() Successfully connected to %s\n",
+       ae->servletContainerName);
+     */
+    return JK_HANDLER_LAST;
+}
+
+
+/*
+ * Decode the Log Nok Command 
+ *
+ * +---------------------+-----------------------+
+ * | LOGNOK CMD (1 byte) | FAILURE CODE (32bits) |
+ * +---------------------+-----------------------+
+ *
+ */
+static int JK_METHOD jk2_handler_lognok(jk_env_t *env, void *target,
+                                        jk_endpoint_t *ae, jk_msg_t *msg)
+{
+    unsigned long status;
+
+    status = msg->getLong(env, msg);
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "handler.logonFailure() code %08lx", status);
+
+    return JK_HANDLER_FATAL;
+}
+
+int JK_METHOD jk2_handler_logon_init(jk_env_t *env, jk_handler_t * _this,
+                                     jk_workerEnv_t *wEnv)
+{
+    wEnv->registerHandler(env, wEnv, "handler.logon",
+                          "login", JK_HANDLE_LOGON_SEED,
+                          jk2_handler_login, NULL);
+
+    wEnv->registerHandler(env, wEnv, "handler.logon",
+                          "logOk", JK_HANDLE_LOGON_OK,
+                          jk2_handler_logok, NULL);
+
+    wEnv->registerHandler(env, wEnv, "handler.logon",
+                          "logNok", JK_HANDLE_LOGON_ERR,
+                          jk2_handler_lognok, NULL);
+    return JK_OK;
+}
+
+
+/** Register handlers
+ */
+int JK_METHOD jk2_handler_logon_factory(jk_env_t *env, jk_pool_t *pool,
+                                        jk_bean_t *result,
+                                        const char *type, const char *name)
+{
+    jk_handler_t *h;
+
+    h = (jk_handler_t *) pool->calloc(env, pool, sizeof(jk_handler_t));
+
+    h->init = jk2_handler_logon_init;
+
+    result->object = h;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_handler_response.c b/connectors/jk/native2/common/jk_handler_response.c
new file mode 100644
index 0000000..753e945
--- /dev/null
+++ b/connectors/jk/native2/common/jk_handler_response.c
@@ -0,0 +1,293 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "jk_global.h"
+#include "jk_service.h"
+#include "jk_msg.h"
+#include "jk_env.h"
+#include "jk_requtil.h"
+#include "jk_env.h"
+#include "jk_handler.h"
+#include "jk_endpoint.h"
+
+#include "jk_registry.h"
+
+
+/** SEND_HEADERS handler
+   AJPV13_RESPONSE/AJPV14_RESPONSE:=
+    response_prefix (2)
+    status          (short)
+    status_msg      (short)
+    num_headers     (short)
+    num_headers*(res_header_name header_value)
+    *body_chunk
+    terminator      boolean <! -- recycle connection or not  -->
+
+req_header_name := 
+    sc_req_header_name | (string)
+
+res_header_name := 
+    sc_res_header_name | (string)
+
+header_value :=
+    (string)
+
+body_chunk :=
+    length  (short)
+    body    length*(var binary)
+
+ */
+static int JK_METHOD jk2_handler_startResponse(jk_env_t *env, void *target,
+                                               jk_endpoint_t *ae,
+                                               jk_msg_t *msg)
+{
+    jk_ws_service_t *s = target;
+    int err = JK_ERR;
+    int i;
+    jk_pool_t *pool = s->pool;
+    int headerCount;
+    int debug = 1;
+
+    if (s->uriEnv != NULL)
+        debug = s->uriEnv->mbean->debug;
+
+    s->status = msg->getInt(env, msg);
+    s->msg = (char *)msg->getString(env, msg);
+    if (s->msg) {
+        jk_xlate_from_ascii(s->msg, strlen(s->msg));
+        /* Do we want this ? Probably not needed, but safer ! */
+        s->msg = ae->cPool->pstrdup(env, ae->cPool, s->msg);
+    }
+    headerCount = msg->getInt(env, msg);
+
+    /* XXX assert msg->headers_out is set - the server adapter should know what
+       kind of map to use ! */
+
+    for (i = 0; i < headerCount; i++) {
+        char *nameS;
+        char *valueS;
+        unsigned short name = msg->peekInt(env, msg);
+
+        if ((name & 0XFF00) == 0XA000) {
+            msg->getInt(env, msg);
+            name = name & 0X00FF;
+            if (name <= SC_RES_HEADERS_NUM) {
+                nameS = (char *)jk2_requtil_getHeaderById(env, name);
+            }
+            else {
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "handler.response() Invalid header id (%d)\n",
+                              name);
+                return JK_HANDLER_FATAL;
+            }
+        }
+        else {
+            nameS = (char *)msg->getString(env, msg);
+            if (nameS == NULL) {
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "handler.response() Null header name \n");
+                return JK_HANDLER_FATAL;
+            }
+            jk_xlate_from_ascii(nameS, strlen(nameS));
+        }
+
+        valueS = (char *)msg->getString(env, msg);
+        if (valueS == NULL) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "Error ajp_unmarshal_response - Null header value\n");
+            return JK_HANDLER_FATAL;
+        }
+
+        jk_xlate_from_ascii(valueS, strlen(valueS));
+
+        if (debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "handler.response() Header[%d] [%s] = [%s]\n",
+                          i, nameS, valueS);
+
+        /* Do we want this ? Preserve the headers, maybe someone will
+           need them. Alternative is to use a different buffer every time,
+           which may be more efficient. */
+        /* We probably don't need that, we'll send them in the next call */
+        /*Apache does it - will change if we add jk_map_apache
+           nameS=ae->cPool->pstrdup( ae->cPool, nameS ); */
+        /*         valueS=ae->cPool->pstrdup( ae->cPool, valueS ); */
+
+        s->headers_out->add(env, s->headers_out, nameS, valueS);
+    }
+
+
+    if (debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "handler.response(): status=%d headers=%d\n",
+                      s->status, headerCount);
+
+    err = s->head(env, s);
+    if (err != JK_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "handler.response() Error sending response");
+        return JK_HANDLER_ERROR;
+    }
+
+    return JK_HANDLER_OK;
+}
+
+/** SEND_BODY_CHUNK handler
+ */
+static int JK_METHOD jk2_handler_sendChunk(jk_env_t *env, void *target,
+                                           jk_endpoint_t *ae, jk_msg_t *msg)
+{
+    jk_ws_service_t *r = target;
+    int err;
+    int len;
+    char *buf;
+
+    buf = msg->getBytes(env, msg, &len);
+
+    err = r->write(env, r, buf, len);
+    if (err != JK_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "Write failed - user closed connection or other network problems\n");
+        return JK_HANDLER_ERROR;
+    }
+
+    return JK_HANDLER_OK;
+}
+
+static int JK_METHOD jk2_handler_endResponse(jk_env_t *env, void *target,
+                                             jk_endpoint_t *ae, jk_msg_t *msg)
+{
+    int reuse = (int)msg->getByte(env, msg);
+
+    if ((reuse & 0X01) != reuse) {
+        /*
+         * Strange protocol error.
+         */
+        reuse = JK_FALSE;
+    }
+    if (reuse == JK_FALSE)
+        ae->recoverable = JK_FALSE;
+
+    return JK_HANDLER_LAST;
+}
+
+/** GET_BODY_CHUNK handler
+ */
+static int JK_METHOD jk2_handler_getChunk(jk_env_t *env, void *target,
+                                          jk_endpoint_t *ae, jk_msg_t *msg)
+{
+    jk_ws_service_t *r = target;
+    int len = msg->getInt(env, msg);
+
+    if (len > AJP13_MAX_SEND_BODY_SZ) {
+        len = AJP13_MAX_SEND_BODY_SZ;
+    }
+    if (len > r->left_bytes_to_send) {
+        len = r->left_bytes_to_send;
+    }
+    if (len < 0) {
+        len = 0;
+    }
+
+/*     env->l->jkLog(env, env->l, JK_LOG_INFO, */
+/*                   "handler_request.getChunk() - read len=%d\n",len); */
+    msg->reset(env, msg);
+
+    len = msg->appendFromServer(env, msg, r, ae, len);
+    /* the right place to add file storage for upload */
+    if (len >= 0) {
+        r->content_read += len;
+        return JK_HANDLER_RESPONSE;
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                  "handler_request.getChunk() - read failed len=%d\n", len);
+    return JK_HANDLER_FATAL;
+}
+
+/** PONG Reply handler
+ */
+static int JK_METHOD jk2_handler_getPong(jk_env_t *env, void *target,
+                                         jk_endpoint_t *ae, jk_msg_t *msg)
+{
+    return JK_HANDLER_LAST;
+}
+
+static int JK_METHOD jk2_handler_response_invoke(jk_env_t *env,
+                                                 jk_bean_t *bean,
+                                                 jk_endpoint_t *ep, int code,
+                                                 jk_msg_t *msg, int raw)
+{
+    void *target = ep->currentRequest;
+
+    switch (code) {
+    case JK_HANDLE_AJP13_SEND_HEADERS:
+        return jk2_handler_startResponse(env, target, ep, msg);
+    case JK_HANDLE_AJP13_SEND_BODY_CHUNK:
+        return jk2_handler_sendChunk(env, target, ep, msg);
+    case JK_HANDLE_AJP13_END_RESPONSE:
+        return jk2_handler_endResponse(env, target, ep, msg);
+    case JK_HANDLE_AJP13_GET_BODY_CHUNK:
+        return jk2_handler_getChunk(env, target, ep, msg);
+    case JK_HANDLE_AJP13_PONG_REPLY:
+        return jk2_handler_getPong(env, target, ep, msg);
+    }
+    return JK_OK;
+}
+
+
+static int JK_METHOD jk2_handler_response_init(jk_env_t *env,
+                                               jk_handler_t * _this,
+                                               jk_workerEnv_t *wEnv)
+{
+    wEnv->registerHandler(env, wEnv, "handler.response",
+                          "sendHeaders", JK_HANDLE_AJP13_SEND_HEADERS,
+                          jk2_handler_startResponse, NULL);
+
+    wEnv->registerHandler(env, wEnv, "handler.response",
+                          "sendChunk", JK_HANDLE_AJP13_SEND_BODY_CHUNK,
+                          jk2_handler_sendChunk, NULL);
+
+    wEnv->registerHandler(env, wEnv, "handler.response",
+                          "endResponse", JK_HANDLE_AJP13_END_RESPONSE,
+                          jk2_handler_endResponse, NULL);
+
+    wEnv->registerHandler(env, wEnv, "handler.response",
+                          "getChunk", JK_HANDLE_AJP13_GET_BODY_CHUNK,
+                          jk2_handler_getChunk, NULL);
+
+    wEnv->registerHandler(env, wEnv, "handler.response",
+                          "pongResponse", JK_HANDLE_AJP13_PONG_REPLY,
+                          jk2_handler_getPong, NULL);
+
+    return JK_OK;
+}
+
+int JK_METHOD jk2_handler_response_factory(jk_env_t *env, jk_pool_t *pool,
+                                           jk_bean_t *result,
+                                           const char *type, const char *name)
+{
+    jk_handler_t *h;
+
+    h = (jk_handler_t *) pool->calloc(env, pool, sizeof(jk_handler_t));
+
+    h->init = jk2_handler_response_init;
+    result->invoke = jk2_handler_response_invoke;
+
+    result->object = h;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_logger_file.c b/connectors/jk/native2/common/jk_logger_file.c
new file mode 100644
index 0000000..5ce77fd
--- /dev/null
+++ b/connectors/jk/native2/common/jk_logger_file.c
@@ -0,0 +1,288 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Utility functions (mainly configuration)                   *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+
+#include "jk_env.h"
+#include "jk_map.h"
+#include "jk_logger.h"
+#include <stdio.h>
+#include <fcntl.h>
+
+#include "jk_registry.h"
+
+#define LOG_FORMAT          ("log_format")
+
+#define HUGE_BUFFER_SIZE (8*1024)
+#define LOG_LINE_SIZE    (1024)
+
+
+/* 
+ * define the log format, we're using by default the one from error.log 
+ *
+ * [Mon Mar 26 19:44:48 2001] [jk_uri_worker_map.c (155)]: Into jk_uri_worker_map_t::uri_worker_map_alloc
+ * log format used by apache in error.log
+ */
+#ifndef JK_TIME_FORMAT
+#define JK_TIME_FORMAT "[%a %b %d %H:%M:%S %Y] "
+#endif
+
+static const char *jk2_logger_file_logFmt = JK_TIME_FORMAT;
+
+static void jk2_logger_file_setTimeStr(jk_env_t *env, char *str, int len)
+{
+    apr_time_exp_t gmt;
+    apr_size_t l;
+
+    apr_time_exp_gmt(&gmt, apr_time_now());
+    apr_strftime(str, &l, len, jk2_logger_file_logFmt, &gmt);
+}
+
+static int JK_METHOD jk2_logger_file_log(jk_env_t *env, jk_logger_t *l,
+                                         int level, const char *what)
+{
+    apr_status_t rv = APR_SUCCESS;
+    apr_file_t *f = (apr_file_t *) l->logger_private;
+
+    if (f == NULL) {
+        /* This is usefull to debug what happens before logger is set.
+           On apache you need -X option ( no detach, single process ) */
+        if (what != NULL)
+            fprintf(stderr, what);
+        return JK_OK;
+    }
+    if (l && l->level <= level && l->logger_private && what) {
+        rv = apr_file_puts(what, f);
+        /* flush the log for each entry only for debug level */
+        if (rv == APR_SUCCESS && l->level == JK_LOG_DEBUG_LEVEL)
+            rv = apr_file_flush(f);
+        return JK_OK;
+    }
+
+    return JK_ERR;
+}
+
+int jk2_logger_file_parseLogLevel(jk_env_t *env, const char *level)
+{
+    if (!level)
+        return JK_LOG_INFO_LEVEL;
+
+    if (!strcasecmp(level, JK_LOG_INFO_VERB))
+        return JK_LOG_INFO_LEVEL;
+
+    if (!strcasecmp(level, JK_LOG_ERROR_VERB))
+        return JK_LOG_ERROR_LEVEL;
+
+    if (!strcasecmp(level, JK_LOG_EMERG_VERB))
+        return JK_LOG_EMERG_LEVEL;
+
+    return JK_LOG_DEBUG_LEVEL;
+}
+
+static int JK_METHOD jk2_logger_file_init(jk_env_t *env, jk_logger_t *_this)
+{
+    apr_status_t rv;
+    apr_file_t *oldF = (apr_file_t *) _this->logger_private;
+
+    int closeOld;
+    apr_file_t *f = NULL;
+    jk_workerEnv_t *workerEnv = env->getByName(env, "workerEnv");
+
+    /* workaround for APR insanity - APR closes the global system
+       stderr handle and invalidates all references to stderr if you
+       call apr_file_close on any stderr reference. Just don't close
+       stderr references. */
+    closeOld = oldF != NULL && _this->name != NULL &&
+        strcmp("stderr", _this->name) != 0;
+
+    if (!_this->name) {
+        _this->name = "${serverRoot}/logs/jk2.log";
+    }
+    _this->name = jk2_config_replaceProperties(env, workerEnv->initData,
+                                               _this->mbean->pool,
+                                               _this->name);
+    if (!_this->name || !strcmp("stderr", _this->name)) {
+        if ((rv = apr_file_open_stderr(&f, env->globalPool->_private)) !=
+            APR_SUCCESS) {
+            _this->jkLog(env, _this, JK_LOG_ERROR,
+                         "Can't open stderr file\n");
+            return JK_ERR;
+        }
+        _this->logger_private = f;
+    }
+    else {
+        if ((rv = apr_file_open(&f, _this->name,
+                                APR_APPEND | APR_READ | APR_WRITE |
+                                APR_CREATE, APR_OS_DEFAULT,
+                                env->globalPool->_private)) != APR_SUCCESS) {
+            _this->jkLog(env, _this, JK_LOG_ERROR, "Can't open log file %s\n",
+                         _this->name);
+            return JK_ERR;
+        }
+        _this->logger_private = f;
+    }
+    _this->jkLog(env, _this, JK_LOG_INFO,
+                 "Initializing log file %s\n", _this->name);
+
+    if (closeOld) {
+         apr_file_close(oldF);
+    }
+
+    return JK_OK;
+}
+
+static int jk2_logger_file_close(jk_env_t *env, jk_logger_t *_this)
+{
+    apr_status_t rv;
+    apr_file_t *f = _this->logger_private;
+
+    if (!f)
+        return JK_OK;
+
+    apr_file_flush(f);
+    rv = apr_file_close(f);
+    _this->logger_private = NULL;
+
+    /*     free(_this); */
+    return JK_OK;
+}
+
+static int JK_METHOD
+jk2_logger_file_setProperty(jk_env_t *env, jk_bean_t *mbean,
+                            char *name, void *valueP)
+{
+    jk_logger_t *_this = mbean->object;
+    char *value = valueP;
+    if (!strcmp(name, "name"))
+        _this->name = (char *)value;
+    else if (!strcmp(name, "file")) {
+        _this->name = (char *)value;
+        /* Set the file imediately */
+        jk2_logger_file_init(env, (jk_logger_t *)mbean->object);
+    }
+    else if (!strcmp(name, "timeFormat"))
+        jk2_logger_file_logFmt = value;
+    else if (!strcmp(name, "level")) {
+        _this->level = jk2_logger_file_parseLogLevel(env, value);
+        if (_this->level == 0) {
+            _this->jkLog(env, _this, JK_LOG_INFO,
+                         "Level %s %d \n", value, _this->level);
+        }
+    }
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_logger_file_jkVLog(jk_env_t *env, jk_logger_t *l,
+                                            const char *file,
+                                            int line,
+                                            int level,
+                                            const char *fmt, va_list args)
+{
+    int rc = 0;
+    char *buf;
+    char *fmt1;
+    apr_pool_t *aprPool = env->tmpPool->_private;
+    char rfctime[APR_RFC822_DATE_LEN];
+    apr_time_t time = apr_time_now();
+
+    if (!file || !args)
+        return -1;
+
+
+    if (l->logger_private == NULL || l->level <= level) {
+        char *f = (char *)(file + strlen(file) - 1);
+        char *slevel;
+        switch (level) {
+        case JK_LOG_INFO_LEVEL:
+            slevel = JK_LOG_INFO_VERB;
+            break;
+        case JK_LOG_ERROR_LEVEL:
+            slevel = JK_LOG_ERROR_VERB;
+            break;
+        case JK_LOG_EMERG_LEVEL:
+            slevel = JK_LOG_EMERG_VERB;
+            break;
+        case JK_LOG_DEBUG_LEVEL:
+        default:
+            slevel = JK_LOG_DEBUG_VERB;
+            break;
+        }
+        while (f != file && *f != '\\' && *f != '/')
+            f--;
+        if (f != file)
+            ++f;
+
+        /* XXX rfc822_date or apr_ctime ? */
+        apr_ctime(rfctime, time);
+        fmt1 =
+            apr_psprintf(aprPool, "[%s] (%5s ) [%s (%d)]  %s", rfctime,
+                         slevel, f, line, fmt);
+        buf = apr_pvsprintf(aprPool, fmt1, args);
+
+        l->log(env, l, level, buf);
+    }
+
+    return rc;
+}
+
+
+static int jk2_logger_file_jkLog(jk_env_t *env, jk_logger_t *l,
+                                 const char *file,
+                                 int line, int level, const char *fmt, ...)
+{
+    va_list args;
+    int rc;
+
+    va_start(args, fmt);
+    rc = jk2_logger_file_jkVLog(env, l, file, line, level, fmt, args);
+    va_end(args);
+
+    return rc;
+}
+
+int JK_METHOD jk2_logger_file_factory(jk_env_t *env, jk_pool_t *pool,
+                                      jk_bean_t *result,
+                                      const char *type, const char *name)
+{
+    jk_logger_t *log =
+        (jk_logger_t *)pool->calloc(env, pool, sizeof(jk_logger_t));
+
+    if (log == NULL) {
+        fprintf(stderr, "loggerFile.factory(): OutOfMemoryException\n");
+        return JK_ERR;
+    }
+
+    log->log = jk2_logger_file_log;
+    log->logger_private = NULL;
+    log->init = jk2_logger_file_init;
+    log->jkLog = jk2_logger_file_jkLog;
+    log->jkVLog = jk2_logger_file_jkVLog;
+    log->level = JK_LOG_ERROR_LEVEL;
+    jk2_logger_file_logFmt = JK_TIME_FORMAT;
+
+    result->object = log;
+    log->mbean = result;
+
+    result->setAttribute = jk2_logger_file_setProperty;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_logger_win32.c b/connectors/jk/native2/common/jk_logger_win32.c
new file mode 100644
index 0000000..2c11b61
--- /dev/null
+++ b/connectors/jk/native2/common/jk_logger_win32.c
@@ -0,0 +1,211 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Description: Logger implementation using win32's native logger,
+ * 
+ * 
+ * @author Costin Manolache
+ * @author Ignacio J. Ortega
+ */
+
+#include "jk_env.h"
+#include "jk_map.h"
+#include "jk_logger.h"
+#include <stdio.h>
+
+
+#define HUGE_BUFFER_SIZE (8*1024)
+#define JAKARTA_EVENT_SOURCE "Apache Jakarta Connector2"
+
+#ifdef WIN32
+
+#include "jk_logger_win32_message.h"
+
+static int JK_METHOD jk2_logger_win32_log(jk_env_t *env, jk_logger_t *l,
+                                          int level, const char *what)
+{
+    HANDLE h = RegisterEventSource(NULL, JAKARTA_EVENT_SOURCE);
+    LPCTSTR *Buffer;
+    Buffer = &what;
+    if (h == NULL) {
+        return JK_ERR;
+    }
+    if (l && l->level <= level && what) {
+        if (level == JK_LOG_DEBUG_LEVEL) {
+            ReportEvent(h, EVENTLOG_SUCCESS, 0, MSG_DEBUG, NULL, 1, 0, Buffer,
+                        NULL);
+        }
+        else if (level == JK_LOG_INFO_LEVEL) {
+            ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, MSG_INFO, NULL, 1, 0,
+                        Buffer, NULL);
+        }
+        else if (level == JK_LOG_ERROR_LEVEL) {
+            ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, MSG_ERROR, NULL, 1, 0,
+                        Buffer, NULL);
+        }
+        else if (level == JK_LOG_EMERG_LEVEL) {
+            ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, MSG_EMERG, NULL, 1, 0,
+                        Buffer, NULL);
+        }
+    }
+    DeregisterEventSource(h);
+
+    return JK_OK;
+}
+
+
+static int JK_METHOD jk2_logger_win32_init(jk_env_t *env, jk_logger_t *_this)
+{
+    HKEY hk;
+    DWORD dwData;
+    char event_key[100] =
+        "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\";
+
+    if (!RegCreateKey(HKEY_LOCAL_MACHINE,
+                      strcat(event_key, JAKARTA_EVENT_SOURCE), &hk)) {
+
+        RegSetValueEx(hk, "EventMessageFile", 0, REG_SZ, (LPBYTE) env->soName,
+                      strlen(env->soName) + 1);
+        dwData =
+            EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
+            EVENTLOG_INFORMATION_TYPE;
+
+        RegSetValueEx(hk, "TypesSupported", 0, REG_DWORD, (LPBYTE) & dwData,
+                      sizeof(DWORD));
+
+        RegCloseKey(hk);
+    }
+    return JK_OK;
+}
+
+
+static int JK_METHOD jk2_logger_win32_close(jk_env_t *env, jk_logger_t *_this)
+{
+
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_logger_win32_jkVLog(jk_env_t *env, jk_logger_t *l,
+                                             const char *file,
+                                             int line,
+                                             int level,
+                                             const char *fmt, va_list args)
+{
+    int rc = 0;
+    if (l->level <= level) {
+        char buf[HUGE_BUFFER_SIZE];
+        char *f = (char *)(file + strlen(file) - 1);
+        int used = 0;
+
+        while (f != file && '\\' != *f && '/' != *f) {
+            f--;
+        }
+        if (f != file) {
+            f++;
+        }
+
+        if (level >= JK_LOG_DEBUG_LEVEL) {
+            used +=
+                _snprintf(&buf[used], HUGE_BUFFER_SIZE, " [%s (%d)]: ", f,
+                          line);
+        }
+        if (used < 0) {
+            return 0;           /* [V] not sure what to return... */
+        }
+
+
+        rc = apr_vsnprintf(buf + used, HUGE_BUFFER_SIZE - used, fmt, args);
+
+        l->log(env, l, level, buf);
+    }
+    return rc;
+}
+
+static int jk2_logger_win32_jkLog(jk_env_t *env, jk_logger_t *l,
+                                  const char *file,
+                                  int line, int level, const char *fmt, ...)
+{
+    va_list args;
+    int rc;
+
+    va_start(args, fmt);
+    rc = jk2_logger_win32_jkVLog(env, l, file, line, level, fmt, args);
+    va_end(args);
+
+    return rc;
+}
+
+
+static int JK_METHOD
+jk2_logger_file_setProperty(jk_env_t *env, jk_bean_t *mbean,
+                            char *name, void *valueP)
+{
+    jk_logger_t *_this = mbean->object;
+    char *value = valueP;
+
+    if (strcmp(name, "level") == 0) {
+        _this->level = jk2_logger_file_parseLogLevel(env, value);
+        if (_this->level == 0) {
+            /*             _this->jkLog( env, _this, JK_LOG_ERROR, */
+            /*                           "Level %s %d \n", value, _this->level ); */
+        }
+        return JK_OK;
+    }
+    return JK_ERR;
+}
+
+
+
+int JK_METHOD
+jk2_logger_win32_factory(jk_env_t *env, jk_pool_t *pool, jk_bean_t *result,
+                         const char *type, const char *name)
+{
+    jk_logger_t *l = (jk_logger_t *)pool->calloc(env, pool,
+                                                 sizeof(jk_logger_t));
+
+    if (l == NULL) {
+        return JK_ERR;
+    }
+
+    l->log = jk2_logger_win32_log;
+    l->logger_private = NULL;
+    l->init = jk2_logger_win32_init;
+    l->jkLog = jk2_logger_win32_jkLog;
+    l->jkVLog = jk2_logger_win32_jkVLog;
+
+    l->level = JK_LOG_ERROR_LEVEL;
+
+    result->object = (void *)l;
+    l->mbean = result;
+    result->setAttribute = jk2_logger_file_setProperty;
+
+    return JK_OK;
+}
+
+#else
+int JK_METHOD
+jk2_logger_win32_factory(jk_env_t *env, jk_pool_t *pool, jk_bean_t *result,
+                         const char *type, const char *name)
+{
+    env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                  "win32logger.factory(): Support for win32 logger is disabled.");
+    env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                  "win32logger.factory(): Needs WINNT > 4.0 ");
+    result->disabled = 1;
+    return JK_FALSE;
+}
+#endif
diff --git a/connectors/jk/native2/common/jk_logger_win32_message.mc b/connectors/jk/native2/common/jk_logger_win32_message.mc
new file mode 100644
index 0000000..71ff52c
--- /dev/null
+++ b/connectors/jk/native2/common/jk_logger_win32_message.mc
@@ -0,0 +1,27 @@
+MessageId=0x1
+Severity=Error
+SymbolicName=MSG_EMERG
+Language=English
+Emerg: %1
+.
+
+MessageId=0x2
+Severity=Warning
+SymbolicName=MSG_ERROR
+Language=English
+Error:%1
+.
+
+MessageId=0x3
+Severity=Informational
+SymbolicName=MSG_INFO
+Language=English
+Info: %1
+.
+
+MessageId=0x4
+Severity=Success
+SymbolicName=MSG_DEBUG
+Language=English
+Debug: %1
+.
diff --git a/connectors/jk/native2/common/jk_map.c b/connectors/jk/native2/common/jk_map.c
new file mode 100644
index 0000000..47fea01
--- /dev/null
+++ b/connectors/jk/native2/common/jk_map.c
@@ -0,0 +1,416 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: General purpose map object                                 *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_map.h"
+
+#define CAPACITY_INC_SIZE (50)
+#define LENGTH_OF_LINE    (1024)
+
+typedef struct jk_map_private
+{
+    char **names;
+    void **values;
+    apr_uint32_t *keys;
+
+    int capacity;
+    int size;
+} jk_map_private_t;
+
+#if APR_CHARSET_EBCDIC
+#define CASE_MASK 0xbfbfbfbf
+#else
+#define CASE_MASK 0xdfdfdfdf
+#endif
+
+/* Compute the "checksum" for a key, consisting of the first
+ * 4 bytes, normalized for case-insensitivity and packed into
+ * an int...this checksum allows us to do a single integer
+ * comparison as a fast check to determine whether we can
+ * skip a strcasecmp
+ */
+#define COMPUTE_KEY_CHECKSUM(key, checksum)    \
+{                                              \
+    const char *k = (key);                     \
+    apr_uint32_t c = (apr_uint32_t)*k;         \
+    (checksum) = c;                            \
+    (checksum) <<= 8;                          \
+    if (c) {                                   \
+        c = (apr_uint32_t)*++k;                \
+        checksum |= c;                         \
+    }                                          \
+    (checksum) <<= 8;                          \
+    if (c) {                                   \
+        c = (apr_uint32_t)*++k;                \
+        checksum |= c;                         \
+    }                                          \
+    (checksum) <<= 8;                          \
+    if (c) {                                   \
+        c = (apr_uint32_t)*++k;                \
+        checksum |= c;                         \
+    }                                          \
+    checksum &= CASE_MASK;                     \
+}
+
+static void *jk2_map_default_get(jk_env_t *env, jk_map_t *m, const char *name)
+{
+    int i;
+    jk_map_private_t *mPriv;
+    apr_uint32_t checksum;
+
+    if (name == NULL)
+        return NULL;
+    mPriv = (jk_map_private_t *) m->_private;
+
+    COMPUTE_KEY_CHECKSUM(name, checksum);
+
+    for (i = 0; i < mPriv->size; i++) {
+        if (mPriv->keys[i] == checksum && strcmp(mPriv->names[i], name) == 0) {
+            /*fprintf(stderr, "jk_map.get found %s %s \n", name, mPriv->values[i]  ); */
+            return mPriv->values[i];
+        }
+    }
+    return NULL;
+}
+
+/* Make space for more elements
+ */
+static int jk2_map_default_realloc(jk_env_t *env, jk_map_t *m)
+{
+    jk_map_private_t *mPriv = m->_private;
+
+    if (mPriv->size >= mPriv->capacity) {
+        char **names;
+        void **values;
+        apr_uint32_t *keys;
+        int capacity = mPriv->capacity + CAPACITY_INC_SIZE;
+
+        names = (char **)m->pool->calloc(env, m->pool,
+                                         sizeof(char *) * capacity);
+        values = (void **)m->pool->calloc(env, m->pool,
+                                          sizeof(void *) * capacity);
+
+        keys = (apr_uint32_t *) m->pool->calloc(env, m->pool,
+                                                sizeof(apr_uint32_t) *
+                                                capacity);
+        if (names == NULL || values == NULL || keys == NULL) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "map.realloc(): AllocationError\n");
+            return JK_ERR;
+        }
+        m->keys = names;
+        m->values = values;
+
+        if (mPriv->capacity && mPriv->names)
+            memcpy(names, mPriv->names, sizeof(char *) * mPriv->capacity);
+
+        if (mPriv->capacity && mPriv->values)
+            memcpy(values, mPriv->values, sizeof(void *) * mPriv->capacity);
+
+        if (mPriv->capacity && mPriv->keys)
+            memcpy(keys, mPriv->keys, sizeof(apr_uint32_t) * mPriv->capacity);
+
+        mPriv->names = (char **)names;
+        mPriv->values = (void **)values;
+        mPriv->keys = keys;
+        mPriv->capacity = capacity;
+
+        return JK_OK;
+    }
+
+    return JK_OK;
+}
+
+
+static int jk2_map_default_put(jk_env_t *env, jk_map_t *m,
+                               const char *name, void *value, void **old)
+{
+    int rc = JK_ERR;
+    int i;
+    jk_map_private_t *mPriv;
+    apr_uint32_t checksum;
+
+    if (name == NULL)
+        return JK_ERR;
+
+    mPriv = (jk_map_private_t *) m->_private;
+
+    COMPUTE_KEY_CHECKSUM(name, checksum);
+
+    for (i = 0; i < mPriv->size; i++) {
+        if (mPriv->keys[i] == checksum && strcmp(mPriv->names[i], name) == 0) {
+            break;
+        }
+    }
+
+    /* Old value found */
+    if (i < mPriv->size) {
+        if (old != NULL)
+            *old = (void *)mPriv->values[i];    /* DIRTY */
+        mPriv->values[i] = value;
+        return JK_OK;
+    }
+
+    jk2_map_default_realloc(env, m);
+
+    if (mPriv->size < mPriv->capacity) {
+        mPriv->values[mPriv->size] = value;
+        /* XXX this is wrong - either we take ownership and copy both
+           name and value,
+           or none. The caller should do that if he needs !
+           Sure, but we should have our copy...
+           mPriv->names[mPriv->size] =  (char *)name; 
+         */
+        mPriv->names[mPriv->size] = m->pool->pstrdup(env, m->pool, name);
+        mPriv->keys[mPriv->size] = checksum;
+        mPriv->size++;
+        rc = JK_OK;
+    }
+    return rc;
+}
+
+static int jk2_map_default_add(jk_env_t *env, jk_map_t *m,
+                               const char *name, void *value)
+{
+    int rc = JK_ERR;
+    jk_map_private_t *mPriv;
+
+    if (name == NULL)
+        return JK_ERR;
+
+    mPriv = (jk_map_private_t *) m->_private;
+
+    jk2_map_default_realloc(env, m);
+
+    if (mPriv->size < mPriv->capacity) {
+        apr_uint32_t checksum;
+
+        COMPUTE_KEY_CHECKSUM(name, checksum);
+        mPriv->values[mPriv->size] = value;
+        /* XXX this is wrong - either we take ownership and copy both
+           name and value,
+           or none. The caller should do that if he needs !
+         */
+        /*     mPriv->names[mPriv->size] = m->pool->pstrdup(m->pool, name); */
+        mPriv->names[mPriv->size] = (char *)name;
+        mPriv->keys[mPriv->size] = checksum;
+        mPriv->size++;
+        rc = JK_OK;
+    }
+    return rc;
+}
+
+static int jk2_map_default_size(jk_env_t *env, jk_map_t *m)
+{
+    jk_map_private_t *mPriv;
+
+    /* assert(m!=NULL) -- we call it via m->... */
+    mPriv = (jk_map_private_t *) m->_private;
+    return mPriv->size;
+}
+
+static char *jk2_map_default_nameAt(jk_env_t *env, jk_map_t *m, int idex)
+{
+    jk_map_private_t *mPriv;
+
+    mPriv = (jk_map_private_t *) m->_private;
+
+    if (idex < 0 || idex > mPriv->size)
+        return NULL;
+
+    return (char *)mPriv->names[idex];
+}
+
+static void *jk2_map_default_valueAt(jk_env_t *env, jk_map_t *m, int idex)
+{
+    jk_map_private_t *mPriv;
+
+    mPriv = (jk_map_private_t *) m->_private;
+
+    if (idex < 0 || idex > mPriv->size)
+        return NULL;
+
+    return (void *)mPriv->values[idex];
+}
+
+static void jk2_map_default_clear(jk_env_t *env, jk_map_t *m)
+{
+    jk_map_private_t *mPriv;
+
+    /* assert(m!=NULL) -- we call it via m->... */
+    mPriv = (jk_map_private_t *) m->_private;
+    mPriv->size = 0;
+
+}
+
+static void jk2_map_default_init(jk_env_t *env, jk_map_t *m, int initialSize,
+                                 void *wrappedObj)
+{
+
+}
+
+static int jk2_map_append(jk_env_t *env, jk_map_t *dst, jk_map_t *src)
+{
+    /* This was badly broken in the original ! */
+    int sz = src->size(env, src);
+    int i;
+    for (i = 0; i < sz; i++) {
+        char *name = src->nameAt(env, src, i);
+        void *value = src->valueAt(env, src, i);
+
+        if (dst->get(env, dst, name) == NULL) {
+            int rc = dst->put(env, dst, name, value, NULL);
+            if (rc != JK_OK)
+                return rc;
+        }
+    }
+    return JK_OK;
+}
+
+char *jk2_map_concatKeys(jk_env_t *env, jk_map_t *map, char *delim)
+{
+    int i;
+    char *buf;
+    int len = 0;
+    int delimLen = strlen(delim);
+
+    int sz = map->size(env, map);
+    for (i = 0; i < sz; i++) {
+        if (map->keys[i] != NULL) {
+            len += strlen(map->keys[i]);
+            len += delimLen;
+        }
+    }
+    buf = env->tmpPool->calloc(env, env->tmpPool, len + 10);
+    len = 0;
+    for (i = 0; i < sz; i++) {
+        if (map->keys[i] != NULL) {
+            sprintf(buf + len, "%s%s", delim, map->keys[i]);
+            len += strlen(map->keys[i]) + delimLen;
+        }
+    }
+    buf[len] = '\0';
+    return buf;
+}
+
+void qsort2(char **a, void **d, int n)
+{
+    int i, j;
+    char *x, *w;
+
+    do {
+        i = 0;
+        j = n - 1;
+        x = a[j / 2];
+        do {
+            /* XXX: descending length sorting */
+            while (strlen(a[i]) > strlen(x))
+                i++;
+            while (strlen(a[j]) < strlen(x))
+                j--;
+            if (i > j)
+                break;
+            w = a[i];
+            a[i] = a[j];
+            a[j] = w;
+            w = d[i];
+            d[i] = d[j];
+            d[j] = w;
+        }
+        while (++i <= --j);
+        if (j + 1 < n - i) {
+            if (j > 0)
+                qsort2(a, d, j + 1);
+            a += i;
+            d += i;
+            n -= i;
+        }
+        else {
+            if (i < n - 1)
+                qsort2(a + i, d + i, n - i);
+            n = j + 1;
+        }
+    }
+    while (n > 1);
+}
+
+
+
+static void jk2_map_qsort(jk_env_t *env, jk_map_t *map)
+{
+    int n = map->size(env, map);
+
+    if (n < 2)
+        return;
+    qsort2(map->keys, map->values, n);
+}
+
+
+
+/* ==================== */
+/* Internal utils */
+
+
+int jk2_map_default_create(jk_env_t *env, jk_map_t **m, jk_pool_t *pool)
+{
+    jk_map_t *_this;
+    jk_map_private_t *mPriv;
+
+    if (m == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "map.create(): NullPointerException\n");
+        return JK_ERR;
+    }
+
+    _this = (jk_map_t *)pool->calloc(env, pool, sizeof(jk_map_t));
+    mPriv =
+        (jk_map_private_t *) pool->calloc(env, pool,
+                                          sizeof(jk_map_private_t));
+    *m = _this;
+
+    if (_this == NULL || mPriv == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "map.create(): AllocationError\n");
+        return JK_ERR;
+    }
+
+    _this->pool = pool;
+    _this->_private = mPriv;
+
+    mPriv->capacity = 0;
+    mPriv->size = 0;
+    mPriv->names = NULL;
+    mPriv->values = NULL;
+
+    _this->get = jk2_map_default_get;
+    _this->put = jk2_map_default_put;
+    _this->add = jk2_map_default_add;
+    _this->size = jk2_map_default_size;
+    _this->nameAt = jk2_map_default_nameAt;
+    _this->valueAt = jk2_map_default_valueAt;
+    _this->init = jk2_map_default_init;
+    _this->clear = jk2_map_default_clear;
+    _this->sort = jk2_map_qsort;
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_md5.c b/connectors/jk/native2/common/jk_md5.c
new file mode 100644
index 0000000..fc689a3
--- /dev/null
+++ b/connectors/jk/native2/common/jk_md5.c
@@ -0,0 +1,95 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/* Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+/***************************************************************************
+ * Description: MD5 encoding wrapper                                       *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+/*
+ * JK MD5 Encoding function (jk_MD5Encode)
+ *
+ * Use the APR_UTIL apr_md5
+ *
+ * Nota: If you use an EBCDIC system without Apache, you'll have to use MD5 encoding
+ * corresponding call or have a ebcdic2ascii() functions somewhere.
+ * For example current AS/400 have MD5 encoding support APIs but olders not....
+ */
+
+#include "jk_global.h"
+#include "jk_env.h"
+#include "jk_md5.h"
+#include "apr_md5.h"
+
+char *JK_METHOD jk2_hextocstr(unsigned char *org, char *dst, int n)
+{
+    char *os = dst;
+    unsigned char v;
+    static unsigned char zitohex[] = "0123456789ABCDEF";
+
+    while (--n >= 0) {
+        v = *org++;
+        *dst++ = zitohex[v >> 4];
+        *dst++ = zitohex[v & 0x0f];
+    }
+    *dst = 0;
+
+    return (os);
+}
+
+char *JK_METHOD jk2_md5(const unsigned char *org, const unsigned char *org2,
+                        char *dst)
+{
+    apr_md5_ctx_t ctx;
+    char buf[JK_MD5_DIGESTSIZE + 1];
+
+    apr_md5_init(&ctx);
+    apr_md5_update(&ctx, org, strlen(org));
+
+    if (org2 != NULL)
+        apr_md5_update(&ctx, org2, strlen(org2));
+
+    apr_md5_final(buf, &ctx);
+
+    return (jk2_hextocstr(buf, dst, JK_MD5_DIGESTSIZE));
+}
+
+
+/* Test values:
+ * ""                  D4 1D 8C D9 8F 00 B2 04  E9 80 09 98 EC F8 42 7E
+ * "a"                 0C C1 75 B9 C0 F1 B6 A8  31 C3 99 E2 69 77 26 61
+ * "abc                90 01 50 98 3C D2 4F B0  D6 96 3F 7D 28 E1 7F 72
+ * "message digest"    F9 6B 69 7D 7C B7 93 8D  52 5A 2F 31 AA F1 61 D0
+ *
+ */
+
+#ifdef TEST_JKMD5
+
+main(int argc, char **argv)
+{
+    char xxx[(2 * JK_MD5_DIGESTSIZE) + 1];
+
+    if (argc > 1)
+        printf("%s => %s\n", argv[1], jk2_md5(argv[1], NULL, xxx));
+}
+
+#endif
diff --git a/connectors/jk/native2/common/jk_msg_ajp.c b/connectors/jk/native2/common/jk_msg_ajp.c
new file mode 100644
index 0000000..8bc019e
--- /dev/null
+++ b/connectors/jk/native2/common/jk_msg_ajp.c
@@ -0,0 +1,581 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ *  Data marshaling. Originally based on Jserv's ajp12 and other similar
+ *  formats. Implements the jk_msg interface. 
+ *
+ *  Will be eventually replaced with XDR or CDR.
+ *
+ * @author:      Gal Shachor <shachor@il.ibm.com>                           
+ * @author:      Henri Gomez <hgomez@apache.org>                               
+ * @author:      Costin Manolache
+ */
+
+#include "jk_pool.h"
+#include "jk_msg.h"
+#include "jk_logger.h"
+#include "jk_endpoint.h"
+#include "jk_channel.h"
+#include "jk_requtil.h"
+
+/* Signature for the messages sent from Apache to tomcat
+ */
+#define AJP13_WS_HEADER           0x1234
+/* Size of the header ( signature + len )
+ */
+#define AJP_HEADER_LEN            (4)
+#define AJP_HEADER_SZ_LEN         (2)
+
+char *jk_HEX = "0123456789ABCDEFX";
+
+/*
+ * Debugging - display the buffer.
+ */
+static void jk2_msg_ajp_dump(jk_env_t *env, struct jk_msg *_this, char *err)
+{
+    unsigned int i = 0;
+    char line[80];
+    char *current;
+    unsigned int j;
+    unsigned int len = _this->len;
+
+    if (len > 1024)
+        len = 1024;
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "%s pos=%d len=%d max=%d \n",
+                  err, _this->pos, _this->len, _this->maxlen);
+
+    for (i = 0; i < len; i += 16) {
+        current = line;
+/*         I can't believe I did this ! That's the %.4x :-) 
+           *current++= jk_HEX[ ( i & 0xf000 ) >> 12  ]; */
+/*         *current++= jk_HEX[ ( i & 0x0f00 ) >> 8  ]; */
+/*         *current++= jk_HEX[ ( i & 0xf0 ) >> 4 ]; */
+/*         *current++= jk_HEX[ ( i & 0x0f )  ]; */
+
+        for (j = 0; j < 16; j++) {
+            unsigned char x = (_this->buf[i + j]);
+
+            *current++ = jk_HEX[x >> 4];
+            *current++ = jk_HEX[x & 0x0f];
+            *current++ = ' ';
+        }
+        *current++ = ' ';
+        *current++ = '-';
+        *current++ = ' ';
+        for (j = 0; j < 16; j++) {
+            unsigned char x = _this->buf[i + j];
+
+            if (x > 0x20 && x < 0x7F) {
+                *current++ = x;
+            }
+            else {
+                *current++ = '.';
+            }
+        }
+        *current++ = '\n';
+        *current++ = '\0';
+        env->l->jkLog(env, env->l, JK_LOG_INFO, "%.4x    %s", i, line);
+    }
+}
+
+
+static void jk2_msg_ajp_reset(jk_env_t *env, jk_msg_t *_this)
+{
+    _this->len = AJP_HEADER_LEN;
+    _this->pos = AJP_HEADER_LEN;
+}
+
+static void jk2_msg_ajp_end(jk_env_t *env, jk_msg_t *msg)
+{
+    unsigned short len = msg->len - AJP_HEADER_LEN;
+
+
+    if (msg->serverSide) {
+        msg->buf[0] = 0x41;
+        msg->buf[1] = 0x42;
+    }
+    else {
+        msg->buf[0] = 0x12;
+        msg->buf[1] = 0x34;
+    }
+
+    msg->buf[2] = (unsigned char)((len >> 8) & 0xFF);
+    msg->buf[3] = (unsigned char)(len & 0xFF);
+}
+
+
+
+static int jk2_msg_ajp_appendLong(jk_env_t *env, jk_msg_t *msg,
+                                  const unsigned long val)
+{
+    int len = msg->len;
+
+    if (len + AJP_HEADER_LEN > msg->maxlen) {
+        return JK_ERR;
+    }
+
+    msg->buf[len] = (unsigned char)((val >> 24) & 0xFF);
+    msg->buf[len + 1] = (unsigned char)((val >> 16) & 0xFF);
+    msg->buf[len + 2] = (unsigned char)((val >> 8) & 0xFF);
+    msg->buf[len + 3] = (unsigned char)(val & 0xFF);
+
+    msg->len += 4;
+
+    return JK_OK;
+}
+
+
+static int jk2_msg_ajp_appendInt(jk_env_t *env, jk_msg_t *msg,
+                                 const unsigned short val)
+{
+    int len = msg->len;
+    if (len + 2 > msg->maxlen) {
+        return JK_ERR;
+    }
+
+    msg->buf[len] = (unsigned char)((val >> 8) & 0xFF);
+    msg->buf[len + 1] = (unsigned char)(val & 0xFF);
+
+    msg->len += 2;
+
+    return JK_OK;
+}
+
+static int jk2_msg_ajp_appendByte(jk_env_t *env, jk_msg_t *msg,
+                                  unsigned char val)
+{
+    int len = msg->len;
+    if (len + 1 > msg->maxlen) {
+        return JK_ERR;
+    }
+
+    msg->buf[len] = val;
+    msg->len += 1;
+
+    return JK_OK;
+}
+
+static int jk2_msg_ajp_appendMap(jk_env_t *env, jk_msg_t *msg, jk_map_t *map)
+{
+    int rc;
+    int i;
+    int size = map->size(env, map);
+
+    rc = msg->appendInt(env, msg, (short)size);
+
+    for (i = 0; i < size; i++) {
+        char *name = map->nameAt(env, map, i);
+        char *val = map->valueAt(env, map, i);
+        if (rc == JK_OK)
+            rc = msg->appendString(env, msg, name);
+        if (rc == JK_OK)
+            msg->appendString(env, msg, val);
+    }
+
+    return rc;
+}
+
+static int jk2_msg_ajp_getMap(jk_env_t *env, jk_msg_t *msg, jk_map_t *map)
+{
+    int size = msg->getInt(env, msg);
+    int i;
+
+    if (size < 0) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "msg_ajp.getMap(): negative size %d\n", size);
+        return JK_ERR;
+    }
+    for (i = 0; i < size; i++) {
+        char *name = msg->getString(env, msg);
+        char *val = msg->getString(env, msg);
+
+        map->add(env, map, name, val);
+    }
+    return JK_OK;
+}
+
+
+static int jk2_msg_ajp_appendAString(jk_env_t *env, jk_msg_t *msg,
+                                     const char *param, int convert)
+{
+    int len;
+
+    if (param == NULL) {
+        msg->appendInt(env, msg, 0xFFFF);
+        return JK_OK;
+    }
+
+    len = strlen(param);
+    if (msg->len + len + 2 > msg->maxlen) {
+        return JK_ERR;
+    }
+
+    /* ignore error - we checked once */
+    msg->appendInt(env, msg, (unsigned short)len);
+
+    /* We checked for space !!  */
+    strncpy((char *)msg->buf + msg->len, param, len + 1);       /* including \0 */
+#if defined(AS400) || defined(_OSD_POSIX)
+    if (convert)
+        jk_xlate_to_ascii((char *)msg->buf + msg->len, len + 1);        /* convert from EBCDIC if needed */
+#endif
+    msg->len += len + 1;
+
+    return JK_OK;
+}
+
+
+
+static int jk2_msg_ajp_appendString(jk_env_t *env, jk_msg_t *msg,
+                                    const char *param)
+{
+    return jk2_msg_ajp_appendAString(env, msg, param, 1);
+}
+
+
+static int jk2_msg_ajp_appendAsciiString(jk_env_t *env, jk_msg_t *msg,
+                                         const char *param)
+{
+    return jk2_msg_ajp_appendAString(env, msg, param, 0);
+}
+
+
+static int jk2_msg_ajp_appendBytes(jk_env_t *env, jk_msg_t *msg,
+                                   const unsigned char *param, const int len)
+{
+    if (!len) {
+        return JK_OK;
+    }
+
+    if (msg->len + len > msg->maxlen) {
+        return JK_ERR;
+    }
+
+    /* We checked for space !!  */
+    memcpy((char *)msg->buf + msg->len, param, len);
+    msg->len += len;
+
+    return JK_OK;
+}
+
+static unsigned long jk2_msg_ajp_getLong(jk_env_t *env, jk_msg_t *msg)
+{
+    unsigned long i;
+
+    if (msg->pos + 3 > msg->len) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "msg_ajp.getLong(): BufferOverflowException %d %d\n",
+                      msg->pos, msg->len);
+        msg->dump(env, msg, "BUFFER OVERFLOW");
+        return -1;
+    }
+    i = ((msg->buf[(msg->pos++)] & 0xFF) << 24);
+    i |= ((msg->buf[(msg->pos++)] & 0xFF) << 16);
+    i |= ((msg->buf[(msg->pos++)] & 0xFF) << 8);
+    i |= ((msg->buf[(msg->pos++)] & 0xFF));
+    return i;
+}
+
+static unsigned short jk2_msg_ajp_getInt(jk_env_t *env, jk_msg_t *msg)
+{
+    int i;
+    if (msg->pos + 1 > msg->len) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "msg_ajp.geInt(): BufferOverflowException %d %d\n",
+                      msg->pos, msg->len);
+        msg->dump(env, msg, "BUFFER OVERFLOW");
+        return -1;
+    }
+    i = ((msg->buf[(msg->pos++)] & 0xFF) << 8);
+    i += ((msg->buf[(msg->pos++)] & 0xFF));
+    return i;
+}
+
+static unsigned short jk2_msg_ajp_peekInt(jk_env_t *env, jk_msg_t *msg)
+{
+    int i;
+    if (msg->pos + 1 > msg->len) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "msg_ajp.peekInt(): BufferOverflowException %d %d\n",
+                      msg->pos, msg->len);
+        msg->dump(env, msg, "BUFFER OVERFLOW");
+        return -1;
+    }
+    i = ((msg->buf[(msg->pos)] & 0xFF) << 8);
+    i += ((msg->buf[(msg->pos + 1)] & 0xFF));
+    return i;
+}
+
+static unsigned char jk2_msg_ajp_getByte(jk_env_t *env, jk_msg_t *msg)
+{
+    unsigned char rc;
+    if (msg->pos > msg->len) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "msg_ajp.getByte(): BufferOverflowException %d %d\n",
+                      msg->pos, msg->len);
+        msg->dump(env, msg, "BUFFER OVERFLOW");
+        return -1;
+    }
+    rc = msg->buf[msg->pos++];
+
+    return rc;
+}
+
+static char *jk2_msg_ajp_getString(jk_env_t *env, jk_msg_t *msg)
+{
+    int size = jk2_msg_ajp_getInt(env, msg);
+    int start = msg->pos;
+
+    if ((size < 0) || (size + start > msg->maxlen)) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "msg_ajp.getString(): BufferOverflowException %d %d\n",
+                      msg->pos, msg->len);
+        msg->dump(env, msg, "BUFFER OVERFLOW");
+        return (unsigned char *)"ERROR";        /* XXX */
+    }
+
+    msg->pos += size;
+    msg->pos++;                 /* terminating NULL */
+
+    return (unsigned char *)(msg->buf + start);
+}
+
+static unsigned char *jk2_msg_ajp_getBytes(jk_env_t *env, jk_msg_t *msg,
+                                           int *lenP)
+{
+    int size = jk2_msg_ajp_getInt(env, msg);
+    int start = msg->pos;
+
+    *lenP = size;
+
+    if ((size < 0) || (size + start > msg->maxlen)) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "msg_ajp.getBytes(): BufferOverflowException %d %d\n",
+                      msg->pos, msg->len);
+        msg->dump(env, msg, "BUFFER OVERFLOW");
+        return (unsigned char *)"ERROR";        /* XXX */
+    }
+
+    msg->pos += size;
+    msg->pos++;                 /* terminating NULL */
+
+    return (unsigned char *)(msg->buf + start);
+}
+
+
+/** Shame-less copy from somewhere.
+    assert (src != dst)
+ */
+static void jk2_swap_16(unsigned char *src, unsigned char *dst)
+{
+    *dst++ = *(src + 1);
+    *dst = *src;
+}
+
+/** Process the request header. At least the header must be
+    available - the channel may get more data it it can, to
+    avoid multiple system calls.
+*/
+static int jk2_msg_ajp_checkHeader(jk_env_t *env, jk_msg_t *msg,
+                                   jk_endpoint_t *ae)
+{
+    char *head = msg->buf;
+    int msglen;
+
+    if (!((head[0] == 0x41 && head[1] == 0x42) ||
+          (head[0] == 0x12 && head[1] == 0x34))) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "msgAjp.receive(): Bad signature %x%x\n",
+                      head[0], head[1]);
+        msg->dump(env, msg, "BAD MESSAGE: ");
+        return -1;
+    }
+
+    msglen = ((head[2] & 0xff) << 8);
+    msglen += (head[3] & 0xFF);
+
+    if (msglen > msg->maxlen) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "msgAjp.receive(): Incoming message is too big %d\n",
+                      msglen);
+        return -2;
+    }
+
+    msg->len = msglen + AJP_HEADER_LEN;
+    msg->pos = AJP_HEADER_LEN;
+
+    return msglen;
+}
+
+
+/** 
+ * Copy the msg buf into another one, sort of clone.
+ *
+ * Returns -1 on error, else number of bytes copied
+ */
+static int jk2_msg_ajp_copy(jk_env_t *env, jk_msg_t *msg, jk_msg_t *dmsg)
+{
+    if (dmsg == NULL)
+        return -1;
+
+    if (msg->len > dmsg->maxlen) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "msgAjp.copy(): destination buffer too small %d/%d\n",
+                      msg->len, dmsg->maxlen);
+        return -2;
+    }
+
+    memcpy(dmsg->buf, msg->buf, msg->len);
+    dmsg->len = msg->len;
+    dmsg->pos = msg->pos;
+
+    return dmsg->len;
+}
+
+/** 
+ * Special method. Will read data from the server and add them as
+ * bytes. It is equivalent with jk2_requtil_readFully() in a buffer
+ * and then jk_msg_appendBytes(), except that we use directly the
+ * internal buffer.
+ *
+ * Returns -1 on error, else number of bytes read
+ */
+static int jk2_msg_ajp_appendFromServer(jk_env_t *env, jk_msg_t *msg,
+                                        jk_ws_service_t *r,
+                                        jk_endpoint_t *ae, int len)
+{
+    unsigned char *read_buf = msg->buf;
+
+    jk2_msg_ajp_reset(env, msg);
+
+    read_buf += AJP_HEADER_LEN; /* leave some space for the buffer headers */
+    read_buf += AJP_HEADER_SZ_LEN;      /* leave some space for the read length */
+
+    /* Pick the max size since we don't know the content_length */
+    if (r->is_chunked && len == 0) {
+        len = AJP13_MAX_SEND_BODY_SZ;
+    }
+
+    if ((len = jk2_requtil_readFully(env, r, read_buf, len)) < 0) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "msgAjp.appendFromServer() error reading from server\n");
+        return -1;
+    }
+
+    if (!r->is_chunked) {
+        r->left_bytes_to_send -= len;
+    }
+
+    if (len > 0) {
+        /* Recipient recognizes empty packet as end of stream, not
+           an empty body packet */
+        if (0 != jk2_msg_ajp_appendInt(env, msg, (unsigned short)len)) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "msg.appendFromServer(): appendInt failed\n");
+            return -1;
+        }
+    }
+
+    msg->len = msg->len + len;
+
+    return len;
+}
+
+static void jk2_msg_ajp_init(jk_env_t *env, jk_msg_t *msg, int buffSize)
+{
+    msg->maxlen = buffSize;
+    msg->len = 0;
+
+    msg->headerLength = AJP_HEADER_LEN;
+
+    msg->reset = jk2_msg_ajp_reset;
+    msg->end = jk2_msg_ajp_end;
+    msg->dump = jk2_msg_ajp_dump;
+
+    msg->appendByte = jk2_msg_ajp_appendByte;
+    msg->appendBytes = jk2_msg_ajp_appendBytes;
+    msg->appendInt = jk2_msg_ajp_appendInt;
+    msg->appendLong = jk2_msg_ajp_appendLong;
+    msg->appendString = jk2_msg_ajp_appendString;
+    msg->appendAsciiString = jk2_msg_ajp_appendAsciiString;
+    msg->appendMap = jk2_msg_ajp_appendMap;
+
+    msg->appendFromServer = jk2_msg_ajp_appendFromServer;
+    msg->copy = jk2_msg_ajp_copy;
+
+    msg->getByte = jk2_msg_ajp_getByte;
+    msg->getInt = jk2_msg_ajp_getInt;
+    msg->peekInt = jk2_msg_ajp_peekInt;
+    msg->getLong = jk2_msg_ajp_getLong;
+    msg->getString = jk2_msg_ajp_getString;
+    msg->getMap = jk2_msg_ajp_getMap;
+    msg->getBytes = jk2_msg_ajp_getBytes;
+
+    msg->checkHeader = jk2_msg_ajp_checkHeader;
+}
+
+
+jk_msg_t *jk2_msg_ajp_create2(jk_env_t *env, jk_pool_t *pool, char *buf,
+                              int buffSize)
+{
+    jk_msg_t *msg = (jk_msg_t *)pool->calloc(env, pool, sizeof(jk_msg_t));
+
+    if (buffSize == 0)
+        buffSize = DEF_BUFFER_SZ;
+    if (!msg) {
+        return NULL;
+    }
+    msg->pool = pool;
+
+    msg->buf = buf;
+
+    if (msg->buf == NULL) {
+        return NULL;
+    }
+
+    jk2_msg_ajp_init(env, msg, buffSize);
+
+    msg->len = buffSize;
+
+    return msg;
+}
+
+
+jk_msg_t *jk2_msg_ajp_create(jk_env_t *env, jk_pool_t *pool, int buffSize)
+{
+    jk_msg_t *msg = (jk_msg_t *)pool->calloc(env, pool, sizeof(jk_msg_t));
+
+    if (buffSize == 0)
+        buffSize = DEF_BUFFER_SZ;
+    if (!msg) {
+        return NULL;
+    }
+    msg->pool = pool;
+    msg->serverSide = JK_FALSE;
+
+    msg->buf = (unsigned char *)msg->pool->alloc(env, msg->pool, buffSize);
+
+    if (msg->buf == NULL) {
+        return NULL;
+    }
+
+    jk2_msg_ajp_init(env, msg, buffSize);
+
+    return msg;
+}
diff --git a/connectors/jk/native2/common/jk_mutex.c b/connectors/jk/native2/common/jk_mutex.c
new file mode 100644
index 0000000..96986c2
--- /dev/null
+++ b/connectors/jk/native2/common/jk_mutex.c
@@ -0,0 +1,60 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Mutex support.
+ * 
+ * @author Costin Manolache
+ */
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_mutex.h"
+
+/* ==================== Dispatch messages from java ==================== */
+
+/** Called by java. Will call the right mutex method.
+ */
+int JK_METHOD jk2_mutex_invoke(jk_env_t *env, jk_bean_t *bean,
+                               jk_endpoint_t *ep, int code, jk_msg_t *msg,
+                               int raw)
+{
+    jk_mutex_t *mutex = (jk_mutex_t *)bean->object;
+    int rc = JK_OK;
+
+    if (mutex->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG, "mutex.%d() \n", code);
+
+    switch (code) {
+    case MUTEX_LOCK:{
+            if (mutex->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG, "mutex.lock()\n");
+            return mutex->lock(env, mutex);
+        }
+    case MUTEX_TRYLOCK:{
+            if (mutex->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG, "mutex.trylock()\n");
+            return mutex->tryLock(env, mutex);
+        }
+    case MUTEX_UNLOCK:{
+            if (mutex->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG, "mutex.unlock()\n");
+            return mutex->unLock(env, mutex);
+        }
+    }                           /* switch */
+    return JK_ERR;
+}
diff --git a/connectors/jk/native2/common/jk_mutex_proc.c b/connectors/jk/native2/common/jk_mutex_proc.c
new file mode 100644
index 0000000..03c336a
--- /dev/null
+++ b/connectors/jk/native2/common/jk_mutex_proc.c
@@ -0,0 +1,142 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Mutex support.
+ * 
+ * @author Costin Manolache
+ */
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_mutex.h"
+
+#include "apr_proc_mutex.h"
+
+
+static int JK_METHOD jk2_mutex_proc_init(jk_env_t *env, jk_bean_t *mutexB)
+{
+    jk_mutex_t *jkMutex = mutexB->object;
+
+    apr_proc_mutex_t *mutex;
+    apr_lockmech_e mech = (apr_lockmech_e) jkMutex->mechanism;
+
+    apr_pool_t *pool = (apr_pool_t *) env->getAprPool(env);
+
+    apr_status_t st;
+    char *fname = jkMutex->fname;
+
+    st = apr_proc_mutex_create(&mutex, fname, mech, pool);
+
+    jkMutex->privateData = mutex;
+
+    return st;
+}
+
+static int JK_METHOD jk2_mutex_proc_destroy(jk_env_t *env, jk_bean_t *mutexB)
+{
+    jk_mutex_t *jkMutex = mutexB->object;
+
+    apr_proc_mutex_t *mutex = (apr_proc_mutex_t *) jkMutex->privateData;
+    apr_status_t st = 0;
+
+    if (mutex != NULL)
+        st = apr_proc_mutex_destroy(mutex);
+
+    return st;
+}
+
+static int JK_METHOD jk2_mutex_proc_lock(jk_env_t *env, jk_mutex_t *jkMutex)
+{
+    apr_proc_mutex_t *mutex = (apr_proc_mutex_t *) jkMutex->privateData;
+    apr_status_t st;
+
+    st = apr_proc_mutex_lock(mutex);
+
+    return st;
+}
+
+static int JK_METHOD
+jk2_mutex_proc_tryLock(jk_env_t *env, jk_mutex_t *jkMutex)
+{
+    apr_proc_mutex_t *mutex = (apr_proc_mutex_t *) jkMutex->privateData;
+    apr_status_t st;
+
+    st = apr_proc_mutex_trylock(mutex);
+
+    return st;
+}
+
+static int JK_METHOD jk2_mutex_proc_unLock(jk_env_t *env, jk_mutex_t *jkMutex)
+{
+    apr_proc_mutex_t *mutex = (apr_proc_mutex_t *) jkMutex->privateData;
+    apr_status_t st;
+
+    st = apr_proc_mutex_unlock(mutex);
+
+    return st;
+}
+
+static int JK_METHOD jk2_mutex_proc_setAttribute(jk_env_t *env,
+                                                 jk_bean_t *mbean, char *name,
+                                                 void *valueP)
+{
+    jk_mutex_t *mutex = (jk_mutex_t *)mbean->object;
+    char *value = (char *)valueP;
+
+    if (strcmp("file", name) == 0) {
+        mutex->fname = value;
+    }
+    else if (strcmp("mechanism", name) == 0) {
+        mutex->mechanism = atoi(value);
+    }
+    else {
+        return JK_ERR;
+    }
+    return JK_OK;
+
+}
+
+int JK_METHOD jk2_mutex_proc_factory(jk_env_t *env, jk_pool_t *pool,
+                                     jk_bean_t *result,
+                                     const char *type, const char *name)
+{
+    jk_mutex_t *mutex;
+
+    mutex = (jk_mutex_t *)pool->calloc(env, pool, sizeof(jk_mutex_t));
+
+    if (mutex == NULL)
+        return JK_ERR;
+
+    mutex->pool = pool;
+    mutex->privateData = NULL;
+
+    result->setAttribute = jk2_mutex_proc_setAttribute;
+    /* result->getAttribute=jk2_mutex_getAttribute; */
+    mutex->mbean = result;
+    result->object = mutex;
+
+    result->init = jk2_mutex_proc_init;
+    result->destroy = jk2_mutex_proc_destroy;
+    result->invoke = jk2_mutex_invoke;
+
+    mutex->lock = jk2_mutex_proc_lock;
+    mutex->tryLock = jk2_mutex_proc_tryLock;
+    mutex->unLock = jk2_mutex_proc_unLock;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_mutex_thread.c b/connectors/jk/native2/common/jk_mutex_thread.c
new file mode 100644
index 0000000..16c1aa3
--- /dev/null
+++ b/connectors/jk/native2/common/jk_mutex_thread.c
@@ -0,0 +1,143 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Thread mutex support. Wrapper around apr. Old code in jk_mt.h used if no APR.
+ * 
+ * @author Costin Manolache
+ */
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_mutex.h"
+
+/* XXX TODO:
+   - In a JNI environment it is _essential_ we use the same locking mechanism as
+   java. We need to add a check to see if we have a JNIEnv, and use the java
+   locking abstractions.
+*/
+
+#if APR_HAS_THREADS
+
+#include "apr_thread_mutex.h"
+
+static int JK_METHOD jk2_mutex_thread_init(jk_env_t *env, jk_bean_t *mutexB)
+{
+    jk_mutex_t *jkMutex = mutexB->object;
+    mutexB->state = JK_STATE_INIT;
+
+    return apr_thread_mutex_create(&jkMutex->threadMutex,
+                                   0, (apr_pool_t *) env->getAprPool(env));
+}
+
+static int JK_METHOD
+jk2_mutex_thread_destroy(jk_env_t *env, jk_bean_t *mutexB)
+{
+    jk_mutex_t *jkMutex = mutexB->object;
+
+    mutexB->state = JK_STATE_NEW;
+    if (jkMutex == NULL || jkMutex->threadMutex == NULL)
+        return JK_ERR;
+
+    return apr_thread_mutex_destroy(jkMutex->threadMutex);
+}
+
+static int JK_METHOD jk2_mutex_thread_lock(jk_env_t *env, jk_mutex_t *jkMutex)
+{
+    if (jkMutex == NULL || jkMutex->threadMutex == NULL)
+        return JK_ERR;
+    return apr_thread_mutex_lock(jkMutex->threadMutex);
+}
+
+static int JK_METHOD
+jk2_mutex_thread_tryLock(jk_env_t *env, jk_mutex_t *jkMutex)
+{
+    if (jkMutex == NULL || jkMutex->threadMutex == NULL)
+        return JK_ERR;
+    return apr_thread_mutex_trylock(jkMutex->threadMutex);
+}
+
+static int JK_METHOD
+jk2_mutex_thread_unLock(jk_env_t *env, jk_mutex_t *jkMutex)
+{
+    if (jkMutex == NULL || jkMutex->threadMutex == NULL)
+        return JK_ERR;
+    return apr_thread_mutex_unlock(jkMutex->threadMutex);
+}
+
+#else /* All 3 cases we cover - apr and the 2 fallbacks */
+
+/*-------------------- No locking -------------------- */
+
+static int JK_METHOD jk2_mutex_thread_init(jk_env_t *env, jk_bean_t *mutexB)
+{
+    mutexB->state = JK_STATE_INIT;
+    return JK_OK;
+}
+
+static int JK_METHOD
+jk2_mutex_thread_destroy(jk_env_t *env, jk_bean_t *mutexB)
+{
+    mutexB->state = JK_STATE_NEW;
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_mutex_thread_lock(jk_env_t *env, jk_mutex_t *jkMutex)
+{
+    return JK_OK;
+}
+
+static int JK_METHOD
+jk2_mutex_thread_tryLock(jk_env_t *env, jk_mutex_t *jkMutex)
+{
+    return JK_OK;
+}
+
+static int JK_METHOD
+jk2_mutex_thread_unLock(jk_env_t *env, jk_mutex_t *jkMutex)
+{
+    return JK_OK;
+}
+
+#endif
+
+int JK_METHOD jk2_mutex_thread_factory(jk_env_t *env, jk_pool_t *pool,
+                                       jk_bean_t *result,
+                                       const char *type, const char *name)
+{
+    jk_mutex_t *mutex;
+
+    mutex = (jk_mutex_t *)pool->calloc(env, pool, sizeof(jk_mutex_t));
+
+    if (mutex == NULL)
+        return JK_ERR;
+
+    mutex->pool = pool;
+
+    mutex->mbean = result;
+    result->object = mutex;
+
+    result->init = jk2_mutex_thread_init;
+    result->destroy = jk2_mutex_thread_destroy;
+    result->invoke = jk2_mutex_invoke;
+
+    mutex->lock = jk2_mutex_thread_lock;
+    mutex->tryLock = jk2_mutex_thread_tryLock;
+    mutex->unLock = jk2_mutex_thread_unLock;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_nwmain.c b/connectors/jk/native2/common/jk_nwmain.c
new file mode 100644
index 0000000..4bf3b1b
--- /dev/null
+++ b/connectors/jk/native2/common/jk_nwmain.c
@@ -0,0 +1,102 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Netware Wrapper                                            *
+ * Author:      Mike Anderson <mmander@novell.com>                         *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifdef NETWARE
+/*
+ * NATIVE_MAIN
+ */
+
+/*
+ * INCLUDES
+ */
+
+#include <stdio.h>
+
+/* Apache 2/APR uses NOVELL_LIBC which has a different way of handling
+ * "library" nlms.  If we aren't on LIBC, use the old method
+ */
+
+#ifndef __NOVELL_LIBC__
+#include <nwthread.h>
+#include <netdb.h>
+
+NETDB_DEFINE_CONTEXT
+/*
+ * main ()
+ *
+ * Main entry point -- don't do much more than I've provided
+ *
+ * Entry:
+ *
+ * Exit:
+ *    Nothing
+ */
+void main()
+{
+    ExitThread(TSR_THREAD, 0);
+}
+#else /* __NOVELL_LIBC__ */
+
+/* Since we are on LibC, we need to handle our own startup and shutdown */
+
+#include <netware.h>
+#include "novsock2.h"
+
+int _NonAppStart
+    (void *NLMHandle,
+     void *errorScreen,
+     const char *cmdLine,
+     const char *loadDirPath,
+     size_t uninitializedDataLength,
+     void *NLMFileHandle,
+     int (*readRoutineP) (int conn, void *fileHandle, size_t offset,
+                          size_t nbytes, size_t * bytesRead, void *buffer),
+     size_t customDataOffset,
+     size_t customDataSize, int messageCount, const char **messages)
+{
+#pragma unused(cmdLine)
+#pragma unused(loadDirPath)
+#pragma unused(uninitializedDataLength)
+#pragma unused(NLMFileHandle)
+#pragma unused(readRoutineP)
+#pragma unused(customDataOffset)
+#pragma unused(customDataSize)
+#pragma unused(messageCount)
+#pragma unused(messages)
+
+    WSADATA wsaData;
+
+    return WSAStartup((WORD) MAKEWORD(2, 0), &wsaData);
+}
+
+void _NonAppStop(void)
+{
+    WSACleanup();
+}
+
+int _NonAppCheckUnload(void)
+{
+    return 0;
+}
+#endif /* __NOVELL_LIBC__ */
+
+#endif
diff --git a/connectors/jk/native2/common/jk_objCache.c b/connectors/jk/native2/common/jk_objCache.c
new file mode 100644
index 0000000..5ec1874
--- /dev/null
+++ b/connectors/jk/native2/common/jk_objCache.c
@@ -0,0 +1,138 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/* See jk_objCache.h for docs */
+
+#include "jk_global.h"
+#include "jk_pool.h"
+#include "jk_logger.h"
+#include "jk_env.h"
+
+#include "jk_objCache.h"
+
+static int jk2_objCache_put(jk_env_t *env, jk_objCache_t *_this, void *obj)
+{
+    int rc;
+
+    if (_this->size <= 0)
+        return JK_ERR;
+
+    if (_this->cs != NULL)
+        _this->cs->lock(env, _this->cs);
+
+    rc = JK_FALSE;
+
+    if (_this->count >= _this->size && _this->maxSize == -1) {
+        /* Realloc */
+        void **oldData = _this->data;
+        _this->data =
+            (void **)_this->pool->calloc(env, _this->pool,
+                                         2 * sizeof(void *) * _this->size);
+        memcpy(_this->data, oldData, _this->size);
+        _this->size *= 2;
+        /* XXX Can we destroy the old data ? Probably not.
+           It may be better to have a list - but it's unlikely
+           we'll have too many reallocations */
+    }
+
+    if (_this->count < _this->size) {
+        _this->data[_this->count++] = obj;
+    }
+
+    if (_this->cs != NULL)
+        _this->cs->unLock(env, _this->cs);
+
+    return JK_OK;
+}
+
+static int
+jk2_objCache_init(jk_env_t *env, jk_objCache_t *_this, int cacheSize)
+{
+    jk_bean_t *jkb;
+
+    if (cacheSize <= 0) {
+        _this->size = 64;
+    }
+    else {
+        _this->size = cacheSize;
+    }
+    _this->count = 0;
+    _this->maxSize = cacheSize;
+
+    _this->data =
+        (void **)_this->pool->calloc(env, _this->pool,
+                                     sizeof(void *) * _this->size);
+
+    if (_this->data == NULL)
+        return JK_ERR;
+
+    jkb = env->createBean2(env, _this->pool, "threadMutex", NULL);
+    if (jkb != NULL) {
+        _this->cs = jkb->object;
+        jkb->init(env, jkb);
+    }
+
+    return JK_OK;
+}
+
+static int jk2_objCache_destroy(jk_env_t *env, jk_objCache_t *_this)
+{
+
+    if (_this->cs != NULL)
+        _this->cs->mbean->destroy(env, _this->cs->mbean);
+
+    _this->count = 0;
+    /* Nothing to free, we use pools */
+
+    return JK_OK;
+}
+
+
+static void *jk2_objCache_get(jk_env_t *env, jk_objCache_t *_this)
+{
+    void *ae = NULL;
+
+    if (_this->cs != NULL)
+        _this->cs->lock(env, _this->cs);
+
+    if (_this->count > 0) {
+        _this->count--;
+        ae = _this->data[_this->count];
+    }
+
+    if (_this->cs != NULL)
+        _this->cs->unLock(env, _this->cs);
+
+    return ae;
+}
+
+jk_objCache_t *jk2_objCache_create(jk_env_t *env, jk_pool_t *pool)
+{
+    jk_objCache_t *_this = pool->calloc(env, pool, sizeof(jk_objCache_t));
+
+    _this->pool = pool;
+
+    _this->count = 0;
+    _this->size = 0;
+    _this->maxSize = -1;
+
+    _this->get = jk2_objCache_get;
+    _this->put = jk2_objCache_put;
+    _this->init = jk2_objCache_init;
+    _this->destroy = jk2_objCache_destroy;
+
+    return _this;
+}
diff --git a/connectors/jk/native2/common/jk_pool_apr.c b/connectors/jk/native2/common/jk_pool_apr.c
new file mode 100644
index 0000000..fb3d6e2
--- /dev/null
+++ b/connectors/jk/native2/common/jk_pool_apr.c
@@ -0,0 +1,225 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Pool using APR.
+ *
+ * @author Costin Manolache
+ */
+
+#include "apr_pools.h"
+#include "apr_strings.h"
+#include "apr_network_io.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+
+#include "jk_global.h"
+#include "jk_pool.h"
+#include "jk_env.h"
+
+/* 
+   JK_APR_POOL_DEBUG will enable verbose messages on allocation.
+
+   What's important is to see 'reset' after each request.
+*/
+
+
+/** Nothing - apache will take care ??
+ */
+static void jk2_pool_apr_close(jk_env_t *env, jk_pool_t *p)
+{
+#ifdef JK_APR_POOL_DEBUG
+    fprintf(stderr, "apr_close %#lx\n", p);
+#endif
+}
+
+/** Nothing - apache will take care.
+    XXX with jk pools we can implement 'recycling',
+    not sure what's the equivalent for apache
+*/
+static void jk2_pool_apr_reset(jk_env_t *env, jk_pool_t *p)
+{
+#ifdef JK_APR_POOL_DEBUG
+    fprintf(stderr, "apr_reset %#lx\n", p);
+#endif
+    apr_pool_clear(p->_private);
+}
+
+static void *jk2_pool_apr_calloc(jk_env_t *env, jk_pool_t *p, size_t size)
+{
+#ifdef JK_APR_POOL_DEBUG
+    fprintf(stderr, "apr_calloc %#lx %d\n", p, size);
+#endif
+    /* assert( p->_private != NULL ) */
+    return apr_pcalloc((apr_pool_t *) p->_private, (apr_size_t) size);
+}
+
+static void *jk2_pool_apr_alloc(jk_env_t *env, jk_pool_t *p, size_t size)
+{
+#ifdef JK_APR_POOL_DEBUG
+    fprintf(stderr, "apr_alloc %#lx %d\n", p, size);
+#endif
+
+    return apr_palloc((apr_pool_t *) p->_private, (apr_size_t) size);
+}
+
+static void *jk2_pool_apr_realloc(jk_env_t *env, jk_pool_t *p,
+                                  size_t sz, const void *old, size_t old_sz)
+{
+    void *rc;
+
+#ifdef JK_APR_POOL_DEBUG
+    fprintf(stderr, "apr_realloc %#lx %d\n", p, sz);
+#endif
+    if (!p || (!old && old_sz)) {
+        return NULL;
+    }
+
+    rc = jk2_pool_apr_alloc(env, p, sz);
+    if (rc) {
+        memcpy(rc, old, old_sz);
+    }
+
+    return rc;
+}
+
+static void *jk2_pool_apr_strdup(jk_env_t *env, jk_pool_t *p, const char *s)
+{
+#ifdef JK_APR_POOL_DEBUG
+    fprintf(stderr, "apr_strdup %#lx %d\n", p,
+            ((s == NULL) ? -1 : (int)strlen(s)));
+#endif
+    return apr_pstrdup((apr_pool_t *) p->_private, s);
+}
+
+static void jk2_pool_apr_initMethods(jk_env_t *env, jk_pool_t *_this);
+
+static jk_pool_t *jk2_pool_apr_createChild(jk_env_t *env, jk_pool_t *_this,
+                                           int sizeHint)
+{
+    apr_pool_t *parentAprPool = _this->_private;
+    apr_pool_t *childAprPool;
+    jk_pool_t *newPool;
+
+    apr_pool_create(&childAprPool, parentAprPool);
+
+    newPool = (jk_pool_t *)apr_palloc(parentAprPool, sizeof(jk_pool_t));
+
+    jk2_pool_apr_initMethods(env, newPool);
+    newPool->_private = childAprPool;
+
+    return newPool;
+}
+
+/** this is used to cache lengths in pstrcat */
+#define MAX_SAVED_LENGTHS  6
+
+static void *jk2_pool_apr_strcat(jk_env_t *env, jk_pool_t *p, ...)
+{
+
+    char *cp, *argp, *res;
+    apr_size_t saved_lengths[MAX_SAVED_LENGTHS];
+    int nargs = 0;
+
+    /* Pass one --- find length of required string */
+
+    apr_size_t len = 0;
+    va_list adummy;
+
+    va_start(adummy, p);
+
+    while ((cp = va_arg(adummy, char *)) != NULL)
+    {
+        apr_size_t cplen = strlen(cp);
+        if (nargs < MAX_SAVED_LENGTHS) {
+            saved_lengths[nargs++] = cplen;
+        }
+        len += cplen;
+    }
+
+    va_end(adummy);
+
+    /* Allocate the required string */
+
+    res = (char *)apr_palloc(p->_private, len + 1);
+    cp = res;
+
+    /* Pass two --- copy the argument strings into the result space */
+
+    va_start(adummy, p);
+
+    nargs = 0;
+    while ((argp = va_arg(adummy, char *)) != NULL)
+    {
+        if (nargs < MAX_SAVED_LENGTHS) {
+            len = saved_lengths[nargs++];
+        }
+        else {
+            len = strlen(argp);
+        }
+
+        memcpy(cp, argp, len);
+        cp += len;
+    }
+
+    va_end(adummy);
+
+    /* Return the result string */
+
+    *cp = '\0';
+
+    return res;
+}
+
+
+int JK_METHOD jk2_pool_apr_create(jk_env_t *env, jk_pool_t **newPool,
+                                  jk_pool_t *parent, void *aprPoolV)
+{
+    jk_pool_t *_this;
+    apr_pool_t *aprPool = (apr_pool_t *) aprPoolV;
+
+    _this = (jk_pool_t *)apr_palloc(aprPool, sizeof(jk_pool_t));
+    *newPool = _this;
+
+    _this->_private = aprPool;
+    jk2_pool_apr_initMethods(env, _this);
+    return JK_OK;
+}
+
+static void jk2_pool_apr_initMethods(jk_env_t *env, jk_pool_t *_this)
+{
+    /* methods */
+    _this->create = jk2_pool_apr_createChild;
+    _this->close = jk2_pool_apr_close;
+    _this->reset = jk2_pool_apr_reset;
+    _this->alloc = jk2_pool_apr_alloc;
+    _this->calloc = jk2_pool_apr_calloc;
+    _this->pstrdup = jk2_pool_apr_strdup;
+    _this->realloc = jk2_pool_apr_realloc;
+    _this->pstrcat = jk2_pool_apr_strcat;
+}
+
+
+/* Not used yet */
+int jk2_pool_apr_factory(jk_env_t *env, jk_pool_t *pool,
+                         jk_bean_t *result, char *type, char *name)
+{
+    jk_pool_t *_this = (jk_pool_t *)calloc(1, sizeof(jk_pool_t));
+
+    result->object = _this;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_registry.c b/connectors/jk/native2/common/jk_registry.c
new file mode 100644
index 0000000..6364dbc
--- /dev/null
+++ b/connectors/jk/native2/common/jk_registry.c
@@ -0,0 +1,106 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "jk_global.h"
+
+#include "jk_logger.h"
+#include "jk_pool.h"
+#include "jk_service.h"
+#include "jk_env.h"
+
+#include "jk_registry.h"
+
+
+/***************************************************************************
+ * Description: Worker list                                                *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+/** Static declarations for all 'hardcoded' modules. This is a hack, 
+ *  needed to initialize the static 'type system' for jk. 
+ *
+ *  A better solution would use 'introspection' and dlsyms to locate the 
+ *  factory, using the common naming schema. This is a bit harder to implement
+ *  ( we can do it later ) and may have problems on some buggy OSs ( I assume
+ *  most decent systems support .so/.dll and symbols, but you can never know )
+ * 
+ *  Both solutions could be used in paralel - for start we'll use this
+ *  hack and static registration. Don't put anything else here, just 
+ *  static declarations for the factory methods and the registrerFactory() call.
+ * 
+ * (based on jk_worker_list.h )
+ * @author:      Gal Shachor <shachor@il.ibm.com>
+ * @author:      Henri Gomez <hgomez@apache.org>
+ * @author:      Costin Manolache
+ *  
+ */
+
+/**
+ *   Init the components that we compile in by default. 
+ *   In future we should have a more flexible mechanism that would allow 
+ *   different server modules to load and register various jk objects, or 
+ *   even have jk load it's own modules from .so files.
+ * 
+ *   For now the static registrartion should work.
+ */
+void JK_METHOD jk2_registry_init(jk_env_t *env)
+{
+    if (env == NULL) {
+        /* XXX do something ! */
+        printf("jk2_registry_init: Assertion failed, env==NULL\n");
+        return;
+    }
+  /**
+   * Because the functions being referenced here (apjp14_work_factory, and
+   * lb_worker_factory) don't match the prototype declared for registerFactory,
+   * and because the MetroWerks compiler (used for NetWare) treats this as an
+   * error, I'm casting the function pointers to (void *) - mmanders
+   */
+    env->registerFactory(env, "logger.file", jk2_logger_file_factory);
+    env->registerFactory(env, "logger.win32", jk2_logger_win32_factory);
+    env->registerFactory(env, "workerEnv", jk2_workerEnv_factory);
+    env->registerFactory(env, "uriMap", jk2_uriMap_factory);
+    env->registerFactory(env, "uriEnv", jk2_uriEnv_factory);
+    env->registerFactory(env, "endpoint", jk2_endpoint_factory);
+    env->registerFactory(env, "uri", jk2_uriEnv_factory);
+    env->registerFactory(env, "config", jk2_config_file_factory);
+
+    env->registerFactory(env, "ajp13", jk2_worker_ajp13_factory);
+    env->registerFactory(env, "lb", jk2_worker_lb_factory);
+    env->registerFactory(env, "status", jk2_worker_status_factory);
+    env->registerFactory(env, "run", jk2_worker_run_factory);
+
+    env->registerFactory(env, "channel.un", jk2_channel_un_factory);
+
+    env->registerFactory(env, "channel.socket",
+                         jk2_channel_apr_socket_factory);
+
+    env->registerFactory(env, "shm", jk2_shm_factory);
+
+
+    env->registerFactory(env, "handler.response",
+                         jk2_handler_response_factory);
+    env->registerFactory(env, "handler.logon", jk2_handler_logon_factory);
+
+    env->registerFactory(env, "threadMutex", jk2_mutex_thread_factory);
+    env->registerFactory(env, "procMutex", jk2_mutex_proc_factory);
+
+    env->registerFactory(env, "channel.jni", jk2_channel_jni_factory);
+    env->registerFactory(env, "worker.jni", jk2_worker_jni_factory);
+    env->registerFactory(env, "vm", jk2_vm_factory);
+    env->registerFactory(env, "signal", jk2_signal_factory);
+    env->registerFactory(env, "user", jk2_user_factory);
+}
diff --git a/connectors/jk/native2/common/jk_registry.h b/connectors/jk/native2/common/jk_registry.h
new file mode 100644
index 0000000..9510144
--- /dev/null
+++ b/connectors/jk/native2/common/jk_registry.h
@@ -0,0 +1,157 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "jk_logger.h"
+#include "jk_pool.h"
+#include "jk_service.h"
+#include "jk_env.h"
+
+/***************************************************************************
+ * Description: Worker list                                                *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+/** Static declarations for all 'hardcoded' modules. This is a hack, 
+ *  needed to initialize the static 'type system' for jk. 
+ *
+ *  A better solution would use 'introspection' and dlsyms to locate the 
+ *  factory, using the common naming schema. This is a bit harder to implement
+ *  ( we can do it later ) and may have problems on some buggy OSs ( I assume
+ *  most decent systems support .so/.dll and symbols, but you can never know )
+ * 
+ *  Both solutions could be used in paralel - for start we'll use this
+ *  hack and static registration. Don't put anything else here, just 
+ *  static declarations for the factory methods and the registrerFactory() call.
+ * 
+ * (based on jk_worker_list.h )
+ * @author:      Gal Shachor <shachor@il.ibm.com>
+ * @author:      Henri Gomez <hgomez@apache.org>
+ * @author:      Costin Manolache
+ *  
+ */
+
+int JK_METHOD jk2_worker_ajp13_factory(jk_env_t *env, jk_pool_t *pool,
+                                       jk_bean_t *result,
+                                       const char *type, const char *name);
+
+
+int JK_METHOD jk2_worker_lb_factory(jk_env_t *env, jk_pool_t *pool,
+                                    jk_bean_t *result,
+                                    const char *type, const char *name);
+
+
+int JK_METHOD jk2_worker_jni_factory(jk_env_t *env, jk_pool_t *pool,
+                                     jk_bean_t *result,
+                                     const char *type, const char *name);
+
+int JK_METHOD jk2_vm_factory(jk_env_t *env, jk_pool_t *pool,
+                             jk_bean_t *result,
+                             const char *type, const char *name);
+
+int JK_METHOD jk2_channel_jni_factory(jk_env_t *env, jk_pool_t *pool,
+                                      jk_bean_t *result,
+                                      const char *type, const char *name);
+
+int JK_METHOD jk2_worker_status_factory(jk_env_t *env, jk_pool_t *pool,
+                                        jk_bean_t *result,
+                                        const char *type, const char *name);
+
+int JK_METHOD jk2_worker_run_factory(jk_env_t *env, jk_pool_t *pool,
+                                     jk_bean_t *result,
+                                     const char *type, const char *name);
+
+int JK_METHOD jk2_endpoint_factory(jk_env_t *env, jk_pool_t *pool,
+                                   jk_bean_t *result,
+                                   const char *type, const char *name);
+
+int JK_METHOD jk2_worker_ajp12_factory(jk_env_t *env, jk_pool_t *pool,
+                                       jk_bean_t *result,
+                                       const char *type, const char *name);
+
+int JK_METHOD jk2_channel_un_factory(jk_env_t *env, jk_pool_t *pool,
+                                     jk_bean_t *result,
+                                     const char *type, const char *name);
+
+/* Factories for 'new' types. We use the new factory interface,
+ *  workers will be updated later 
+ */
+int JK_METHOD jk2_channel_apr_socket_factory(jk_env_t *env, jk_pool_t *pool,
+                                             jk_bean_t *result,
+                                             const char *type,
+                                             const char *name);
+
+int JK_METHOD jk2_shm_factory(jk_env_t *env, jk_pool_t *pool,
+                              jk_bean_t *result,
+                              const char *type, const char *name);
+
+
+int JK_METHOD jk2_channel_jni_factory(jk_env_t *env, jk_pool_t *pool,
+                                      jk_bean_t *result,
+                                      const char *type, const char *name);
+
+int JK_METHOD jk2_channel_socket_factory(jk_env_t *env, jk_pool_t *pool,
+                                         jk_bean_t *result,
+                                         const char *type, const char *name);
+
+int JK_METHOD jk2_workerEnv_factory(jk_env_t *env, jk_pool_t *pool,
+                                    jk_bean_t *result,
+                                    const char *type, const char *name);
+
+int JK_METHOD jk2_logger_file_factory(jk_env_t *env, jk_pool_t *pool,
+                                      jk_bean_t *result,
+                                      const char *type, const char *name);
+
+
+int JK_METHOD jk2_handler_logon_factory(jk_env_t *env, jk_pool_t *pool,
+                                        jk_bean_t *result,
+                                        const char *type, const char *name);
+
+int JK_METHOD jk2_handler_response_factory(jk_env_t *env, jk_pool_t *pool,
+                                           jk_bean_t *result,
+                                           const char *type,
+                                           const char *name);
+
+int JK_METHOD jk2_uriMap_factory(jk_env_t *env, jk_pool_t *pool,
+                                 jk_bean_t *result, const char *type,
+                                 const char *name);
+
+int JK_METHOD jk2_uriEnv_factory(jk_env_t *env, jk_pool_t *pool,
+                                 jk_bean_t *result, const char *type,
+                                 const char *name);
+
+int JK_METHOD jk2_config_file_factory(jk_env_t *env, jk_pool_t *pool,
+                                      jk_bean_t *result, const char *type,
+                                      const char *name);
+
+int JK_METHOD jk2_logger_win32_factory(jk_env_t *env, jk_pool_t *pool,
+                                       jk_bean_t *result,
+                                       const char *type, const char *name);
+
+int JK_METHOD jk2_mutex_thread_factory(jk_env_t *env, jk_pool_t *pool,
+                                       jk_bean_t *result,
+                                       const char *type, const char *name);
+
+int JK_METHOD jk2_mutex_proc_factory(jk_env_t *env, jk_pool_t *pool,
+                                     jk_bean_t *result,
+                                     const char *type, const char *name);
+
+int JK_METHOD jk2_signal_factory(jk_env_t *env, jk_pool_t *pool,
+                                 jk_bean_t *result,
+                                 const char *type, const char *name);
+
+int JK_METHOD jk2_user_factory(jk_env_t *env, jk_pool_t *pool,
+                               jk_bean_t *result,
+                               const char *type, const char *name);
diff --git a/connectors/jk/native2/common/jk_requtil.c b/connectors/jk/native2/common/jk_requtil.c
new file mode 100644
index 0000000..d6021f9
--- /dev/null
+++ b/connectors/jk/native2/common/jk_requtil.c
@@ -0,0 +1,1095 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Utils for processing various request components
+ *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           
+ * Author:      Henri Gomez <hgomez@apache.org>                               
+ * Author:      Costin Manolache
+ */
+
+/* XXX make them virtual methods, allow servers to override
+ */
+
+#include "jk_global.h"
+#include "jk_channel.h"
+#include "jk_env.h"
+#include "jk_requtil.h"
+
+#ifdef AS400
+#include "util_ebcdic.h"
+#endif
+
+#define CHUNK_BUFFER_PAD          (12)
+
+static const char *response_trans_headers[] = {
+    "Content-Type",
+    "Content-Language",
+    "Content-Length",
+    "Date",
+    "Last-Modified",
+    "Location",
+    "Set-Cookie",
+    "Set-Cookie2",
+    "Servlet-Engine",
+    "Status",
+    "WWW-Authenticate"
+};
+
+/*
+ * Conditional request attributes
+ * 
+ */
+#define SC_A_CONTEXT            (unsigned char)1
+#define SC_A_SERVLET_PATH       (unsigned char)2
+#define SC_A_REMOTE_USER        (unsigned char)3
+#define SC_A_AUTH_TYPE          (unsigned char)4
+#define SC_A_QUERY_STRING       (unsigned char)5
+#define SC_A_JVM_ROUTE          (unsigned char)6
+#define SC_A_SSL_CERT           (unsigned char)7
+#define SC_A_SSL_CIPHER         (unsigned char)8
+#define SC_A_SSL_SESSION        (unsigned char)9
+#define SC_A_REQ_ATTRIBUTE      (unsigned char)10
+/* only in if JkOptions +ForwardKeySize */
+#define SC_A_SSL_KEY_SIZE       (unsigned char)11
+#define SC_A_SECRET             (unsigned char)12
+#define SC_A_STORED_METHOD      (unsigned char)13
+#define SC_A_ARE_DONE           (unsigned char)0xFF
+
+/*
+ * Forward a request from the web server to the servlet container.
+ */
+#define JK_AJP13_FORWARD_REQUEST    (unsigned char)2
+
+/* Important: ajp13 protocol has the strange habit of sending
+   a second ( untyped ) message imediately following the request,
+   with a first chunk of POST body. This is nice for small post
+   requests, since it avoids a roundtrip, but it's horrible
+   because it brakes the model. So we'll have to remember this
+   as an exception to the rule as long as ajp13 is alive
+*/
+
+
+/** Get header value using a lookup table. 
+ *
+ *
+ * long_res_header_for_sc
+ */
+const char *jk2_requtil_getHeaderById(jk_env_t *env, int sc)
+{
+    const char *rc = NULL;
+    if (sc <= SC_RES_HEADERS_NUM && sc > 0) {
+        rc = response_trans_headers[sc - 1];
+    }
+
+    return rc;
+}
+
+/**
+ * Get method id. 
+ *
+ * sc_for_req_method
+ */
+int jk2_requtil_getMethodId(jk_env_t *env, const char *method,
+                            unsigned char *sc)
+{
+    int rc = JK_OK;
+    if (0 == strcmp(method, "GET")) {
+        *sc = SC_M_GET;
+    }
+    else if (0 == strcmp(method, "POST")) {
+        *sc = SC_M_POST;
+    }
+    else if (0 == strcmp(method, "HEAD")) {
+        *sc = SC_M_HEAD;
+    }
+    else if (0 == strcmp(method, "PUT")) {
+        *sc = SC_M_PUT;
+    }
+    else if (0 == strcmp(method, "DELETE")) {
+        *sc = SC_M_DELETE;
+    }
+    else if (0 == strcmp(method, "OPTIONS")) {
+        *sc = SC_M_OPTIONS;
+    }
+    else if (0 == strcmp(method, "TRACE")) {
+        *sc = SC_M_TRACE;
+    }
+    else if (0 == strcmp(method, "PROPFIND")) {
+        *sc = SC_M_PROPFIND;
+    }
+    else if (0 == strcmp(method, "PROPPATCH")) {
+        *sc = SC_M_PROPPATCH;
+    }
+    else if (0 == strcmp(method, "MKCOL")) {
+        *sc = SC_M_MKCOL;
+    }
+    else if (0 == strcmp(method, "COPY")) {
+        *sc = SC_M_COPY;
+    }
+    else if (0 == strcmp(method, "MOVE")) {
+        *sc = SC_M_MOVE;
+    }
+    else if (0 == strcmp(method, "LOCK")) {
+        *sc = SC_M_LOCK;
+    }
+    else if (0 == strcmp(method, "UNLOCK")) {
+        *sc = SC_M_UNLOCK;
+    }
+    else if (0 == strcmp(method, "ACL")) {
+        *sc = SC_M_ACL;
+    }
+    else if (0 == strcmp(method, "REPORT")) {
+        *sc = SC_M_REPORT;
+    }
+    else if (0 == strcmp(method, "VERSION-CONTROL")) {
+        *sc = SC_M_VERSION_CONTROL;
+    }
+    else if (0 == strcmp(method, "CHECKIN")) {
+        *sc = SC_M_CHECKIN;
+    }
+    else if (0 == strcmp(method, "CHECKOUT")) {
+        *sc = SC_M_CHECKOUT;
+    }
+    else if (0 == strcmp(method, "UNCHECKOUT")) {
+        *sc = SC_M_UNCHECKOUT;
+    }
+    else if (0 == strcmp(method, "SEARCH")) {
+        *sc = SC_M_SEARCH;
+    }
+    else if (0 == strcmp(method, "MKWORKSPACE")) {
+        *sc = SC_M_MKWORKSPACE;
+    }
+    else if (0 == strcmp(method, "UPDATE")) {
+        *sc = SC_M_UPDATE;
+    }
+    else if (0 == strcmp(method, "LABEL")) {
+        *sc = SC_M_LABEL;
+    }
+    else if (0 == strcmp(method, "MERGE")) {
+        *sc = SC_M_MERGE;
+    }
+    else if (0 == strcmp(method, "BASELINE-CONTROL")) {
+        *sc = SC_M_BASELINE_CONTROL;
+    }
+    else if (0 == strcmp(method, "MKACTIVITY")) {
+        *sc = SC_M_MKACTIVITY;
+    }
+    else {
+		*sc = SC_M_JK_STORED;
+    }
+
+    return rc;
+}
+
+/**
+ * Get header id.
+ *
+ * sc_for_req_header
+ */
+int jk2_requtil_getHeaderId(jk_env_t *env, const char *header_name,
+                            unsigned short *sc)
+{
+/*     char lowerCased[30]; */
+
+/*     if( strlen( header_name ) > 30 ) */
+/*         return JK_FALSE; */
+/*     strncpy( lowerCased, header_name,  30 ); */
+
+
+    switch (header_name[0]) {
+    case 'a':
+    case 'A':
+        if (strncasecmp(header_name, "accept", 6) == 0) {
+            if ('-' == header_name[6]) {
+                if (!strcasecmp(header_name + 7, "charset")) {
+                    *sc = SC_ACCEPT_CHARSET;
+                }
+                else if (!strcasecmp(header_name + 7, "encoding")) {
+                    *sc = SC_ACCEPT_ENCODING;
+                }
+                else if (!strcasecmp(header_name + 7, "language")) {
+                    *sc = SC_ACCEPT_LANGUAGE;
+                }
+                else {
+                    return JK_ERR;
+                }
+            }
+            else if ('\0' == header_name[6]) {
+                *sc = SC_ACCEPT;
+            }
+            else {
+                return JK_ERR;
+            }
+        }
+        else if (!strcasecmp(header_name, "authorization")) {
+            *sc = SC_AUTHORIZATION;
+        }
+        else {
+            return JK_ERR;
+        }
+        break;
+
+    case 'c':
+    case 'C':
+        if (!strcasecmp(header_name, "cookie")) {
+            *sc = SC_COOKIE;
+        }
+        else if (!strcasecmp(header_name, "connection")) {
+            *sc = SC_CONNECTION;
+        }
+        else if (!strcasecmp(header_name, "content-type")) {
+            *sc = SC_CONTENT_TYPE;
+        }
+        else if (!strcasecmp(header_name, "content-length")) {
+            *sc = SC_CONTENT_LENGTH;
+        }
+        else if (!strcasecmp(header_name, "cookie2")) {
+            *sc = SC_COOKIE2;
+        }
+        else {
+            return JK_ERR;
+        }
+        break;
+
+    case 'h':
+    case 'H':
+        if (!strcasecmp(header_name, "host")) {
+            *sc = SC_HOST;
+        }
+        else {
+            return JK_ERR;
+        }
+        break;
+
+    case 'p':
+    case 'P':
+        if (!strcasecmp(header_name, "pragma")) {
+            *sc = SC_PRAGMA;
+        }
+        else {
+            return JK_ERR;
+        }
+        break;
+
+    case 'r':
+    case 'R':
+        if (!strcasecmp(header_name, "referer")) {
+            *sc = SC_REFERER;
+        }
+        else {
+            return JK_ERR;
+        }
+        break;
+
+    case 'u':
+    case 'U':
+        if (!strcasecmp(header_name, "user-agent")) {
+            *sc = SC_USER_AGENT;
+        }
+        else {
+            return JK_ERR;
+        }
+        break;
+
+    default:
+/*         env->l->jkLog(env, env->l, JK_LOG_INFO,  */
+/*                       "requtil.getHeaderId() long header %s\n", header_name); */
+
+        return JK_ERR;
+    }
+    return JK_OK;
+}
+
+/** Retrieve the cookie with the given name
+ */
+char *jk2_requtil_getCookieByName(jk_env_t *env, jk_ws_service_t *s,
+                                  const char *name)
+{
+    int i;
+    jk_map_t *headers = s->headers_in;
+
+    /* XXX use 'get' - and make sure jk_map has support for
+       case insensitive search */
+    for (i = 0; i < headers->size(NULL, headers); i++) {
+        if (0 == strcasecmp(headers->nameAt(NULL, headers, i), "cookie")) {
+
+            char *id_start;
+            for (id_start = strstr(headers->valueAt(NULL, headers, i), name);
+                 id_start; id_start = strstr(id_start + 1, name)) {
+                if ('=' == id_start[strlen(name)]) {
+                    /*
+                     * Session cookie was found, get it's value
+                     */
+                    id_start += (1 + strlen(name));
+                    if (strlen(id_start)) {
+                        char *id_end;
+                        id_start = s->pool->pstrdup(env, s->pool, id_start);
+                        if (id_end = strchr(id_start, ';')) {
+                            *id_end = '\0';
+                        }
+                        return id_start;
+                    }
+                }
+            }
+        }
+    }
+
+    return NULL;
+}
+
+/* Retrieve the parameter with the given name
+ */
+char *jk2_requtil_getPathParam(jk_env_t *env, jk_ws_service_t *s,
+                               const char *name)
+{
+    char *id_start = NULL;
+    for (id_start = strstr(s->req_uri, name);
+         id_start; id_start = strstr(id_start + 1, name)) {
+        if ('=' == id_start[strlen(name)]) {
+            /*
+             * Session path-cookie was found, get it's value
+             */
+            id_start += (1 + strlen(name));
+            if (strlen(id_start)) {
+                char *id_end;
+                id_start = s->pool->pstrdup(env, s->pool, id_start);
+                /* 
+                 * The query string is not part of req_uri, however
+                 * to be on the safe side lets remove the trailing query 
+                 * string if appended...
+                 */
+                if (id_end = strchr(id_start, '?')) {
+                    *id_end = '\0';
+                }
+                return id_start;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+/** Retrieve session id from the cookie or the parameter                      
+ * (parameter first)
+ */
+char *jk2_requtil_getSessionId(jk_env_t *env, jk_ws_service_t *s)
+{
+    char *val;
+    val = jk2_requtil_getPathParam(env, s, JK_PATH_SESSION_IDENTIFIER);
+    if (!val) {
+        val = jk2_requtil_getCookieByName(env, s, JK_SESSION_IDENTIFIER);
+    }
+    return val;
+}
+
+/** Extract the 'route' from the session id. The route is
+ *  the id of the worker that generated the session and where all
+ *  further requests in that session will be sent.
+*/
+char *jk2_requtil_getSessionRoute(jk_env_t *env, jk_ws_service_t *s)
+{
+    char *sessionid = jk2_requtil_getSessionId(env, s);
+    char *ch;
+
+    if (!sessionid) {
+        return NULL;
+    }
+
+    /*
+     * Balance parameter is appended to the end
+     */
+    ch = strrchr(sessionid, '.');
+    if (!ch) {
+        return 0;
+    }
+    ch++;
+    if (*ch == '\0') {
+        return NULL;
+    }
+    return ch;
+}
+
+/*
+ * Read data from the web server.
+ *
+ * Socket API didn't garanty all the data will be kept in a single 
+ * read, so we must loop up to all awaited data are received 
+ */
+int jk2_requtil_readFully(jk_env_t *env, jk_ws_service_t *s,
+                          unsigned char *buf, unsigned len)
+{
+    unsigned rdlen = 0;
+    unsigned padded_len = len;
+
+    if (s->is_chunked && s->no_more_chunks) {
+        return 0;
+    }
+    if (s->is_chunked) {
+        /* Corner case: buf must be large enough to hold next
+         * chunk size (if we're on or near a chunk border).
+         * Pad the length to a reasonable value, otherwise the
+         * read fails and the remaining chunks are tossed.
+         */
+        padded_len = (len < CHUNK_BUFFER_PAD) ? len : len - CHUNK_BUFFER_PAD;
+    }
+
+    while (rdlen < padded_len) {
+        unsigned this_time = 0;
+        if (s->read(env, s, buf + rdlen, len - rdlen, &this_time)) {
+            return -1;
+        }
+
+        if (0 == this_time) {
+            if (s->is_chunked) {
+                s->no_more_chunks = 1;  /* read no more */
+            }
+            break;
+        }
+        rdlen += this_time;
+    }
+
+    return (int)rdlen;
+}
+
+
+/* -------------------- Printf writing -------------------- */
+
+#define JK_BUF_SIZE 4096
+
+static int jk2_requtil_createBuffer(jk_env_t *env, jk_ws_service_t *s)
+{
+    int bsize = JK_BUF_SIZE;
+
+    s->outSize = bsize;
+    s->outBuf = (char *)s->pool->alloc(env, s->pool, bsize);
+
+    return JK_OK;
+}
+
+static void jk2_requtil_printf(jk_env_t *env, jk_ws_service_t *s, char *fmt,
+                               ...)
+{
+    va_list vargs;
+    int ret = 0;
+
+    if (s->outBuf == NULL) {
+        jk2_requtil_createBuffer(env, s);
+    }
+
+    va_start(vargs, fmt);
+    s->outPos = 0;              /* Temp - we don't buffer */
+    ret =
+        apr_vsnprintf(s->outBuf + s->outPos, s->outSize - s->outPos, fmt, vargs);
+    va_end(vargs);
+
+    s->write(env, s, s->outBuf, strlen(s->outBuf));
+}
+
+/* -------------------- Request serialization -------------------- */
+/* XXX optimization - this can be overriden by server to avoid
+   multiple copies
+*/
+/**
+  Message structure
+ 
+ 
+AJPV13_REQUEST/AJPV14_REQUEST=
+    request_prefix (1) (byte)
+    method         (byte)
+    protocol       (string)
+    req_uri        (string)
+    remote_addr    (string)
+    remote_host    (string)
+    server_name    (string)
+    server_port    (short)
+    is_ssl         (boolean)
+    num_headers    (short)
+    num_headers*(req_header_name header_value)
+
+    ?context       (byte)(string)
+    ?servlet_path  (byte)(string)
+    ?remote_user   (byte)(string)
+    ?auth_type     (byte)(string)
+    ?query_string  (byte)(string)
+    ?jvm_route     (byte)(string)
+    ?ssl_cert      (byte)(string)
+    ?ssl_cipher    (byte)(string)
+    ?ssl_session   (byte)(string)
+    ?ssl_key_size  (byte)(int)		via JkOptions +ForwardKeySize
+    request_terminator (byte)
+    ?body          content_length*(var binary)
+
+    Was: ajp_marshal_into_msgb
+ */
+int jk2_serialize_request13(jk_env_t *env, jk_msg_t *msg,
+                            jk_ws_service_t *s, jk_endpoint_t *ae)
+{
+    unsigned char method;
+    int i;
+    int headerCount;
+    int rc;
+    int debug = 0;
+
+    if (s->uriEnv != NULL) {
+        debug = s->uriEnv->mbean->debug;
+    }
+
+    rc = jk2_requtil_getMethodId(env, s->method, &method);
+    if (rc != JK_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "Error ajp_marshal_into_msgb - method %s\n",
+                      s->method);
+        return JK_ERR;
+    }
+
+    headerCount = s->headers_in->size(env, s->headers_in);
+
+    if (msg->appendByte(env, msg, JK_AJP13_FORWARD_REQUEST) ||
+        msg->appendByte(env, msg, method) ||
+        msg->appendString(env, msg, s->protocol) ||
+        msg->appendString(env, msg, s->req_uri) ||
+        msg->appendString(env, msg, s->remote_addr) ||
+        msg->appendString(env, msg, s->remote_host) ||
+        msg->appendString(env, msg, s->server_name) ||
+        msg->appendInt(env, msg, (unsigned short)s->server_port) ||
+        msg->appendByte(env, msg, (unsigned char)(s->is_ssl)) ||
+        msg->appendInt(env, msg, (unsigned short)(headerCount))) {
+
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "handle.request()  Error serializing the message head\n");
+        return JK_ERR;
+    }
+
+    for (i = 0; i < headerCount; i++) {
+        unsigned short sc;
+
+        char *name = s->headers_in->nameAt(env, s->headers_in, i);
+
+        if (jk2_requtil_getHeaderId(env, name, &sc) == JK_OK) {
+            /*  env->l->jkLog(env, env->l, JK_LOG_INFO, */
+            /*                "serialize.request() Add headerId %s %d\n", name, sc); */
+            if (msg->appendInt(env, msg, sc)) {
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "serialize.request() Error serializing header id\n");
+                return JK_ERR;
+            }
+        }
+        else {
+            if (debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "serialize.request() Add headerName %s\n",
+                              name);
+            if (msg->appendString(env, msg, name)) {
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "serialize.request() Error serializing header name\n");
+                return JK_ERR;
+            }
+        }
+
+        if (msg->appendString(env, msg,
+                              s->headers_in->valueAt(env, s->headers_in,
+                                                     i))) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "serialize.request() Error serializing header value\n");
+            return JK_ERR;
+        }
+    }
+
+    if (s->remote_user) {
+        if (msg->appendByte(env, msg, SC_A_REMOTE_USER) ||
+            msg->appendString(env, msg, s->remote_user)) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "serialize.request() Error serializing user name\n");
+            return JK_ERR;
+        }
+    }
+    if (s->auth_type) {
+        if (msg->appendByte(env, msg, SC_A_AUTH_TYPE) ||
+            msg->appendString(env, msg, s->auth_type)) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "handle.request() Error serializing auth type\n");
+            return JK_ERR;
+        }
+    }
+    if (s->query_string) {
+        if (msg->appendByte(env, msg, SC_A_QUERY_STRING) ||
+#ifdef AS400
+            msg->appendAsciiString(env, msg, s->query_string)) {
+#else
+            msg->appendString(env, msg, s->query_string)) {
+#endif
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "handle.request() Error serializing query string\n");
+            return JK_ERR;
+        }
+    }
+    /* XXX This can be sent only on startup ( ajp14 ) */
+
+    if (s->jvm_route) {
+        if (msg->appendByte(env, msg, SC_A_JVM_ROUTE) ||
+            msg->appendString(env, msg, s->jvm_route)) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "handle.request() Error serializing worker id\n");
+            return JK_ERR;
+        }
+    }
+
+    if (s->ssl_cert_len) {
+        if (msg->appendByte(env, msg, SC_A_SSL_CERT) ||
+            msg->appendString(env, msg, s->ssl_cert)) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "handle.request() Error serializing SSL cert\n");
+            return JK_ERR;
+        }
+    }
+
+    if (s->ssl_cipher) {
+        if (msg->appendByte(env, msg, SC_A_SSL_CIPHER) ||
+            msg->appendString(env, msg, s->ssl_cipher)) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "handle.request() Error serializing SSL cipher\n");
+            return JK_ERR;
+        }
+    }
+    if (s->ssl_session) {
+        if (msg->appendByte(env, msg, SC_A_SSL_SESSION) ||
+            msg->appendString(env, msg, s->ssl_session)) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "handle.request() Error serializing SSL session\n");
+            return JK_ERR;
+        }
+    }
+
+    /*
+     * ssl_key_size is required by Servlet 2.3 API
+     * added support only in ajp14 mode
+     * JFC removed: ae->proto == AJP14_PROTO
+     */
+    if (s->ssl_key_size != -1) {
+        if (msg->appendByte(env, msg, SC_A_SSL_KEY_SIZE) ||
+            msg->appendInt(env, msg, (unsigned short)s->ssl_key_size)) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "handle.request() Error serializing SSL key size\n");
+            return JK_ERR;
+        }
+    }
+
+    if (ae->worker->secret) {
+        if (msg->appendByte(env, msg, SC_A_SECRET) ||
+            msg->appendString(env, msg, ae->worker->secret)) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "handle.request() Error serializing secret\n");
+            return JK_ERR;
+        }
+    }
+
+    /* If the method was unrecognized, encode it as an attribute */
+	if (method == SC_M_JK_STORED) {
+		if (msg->appendByte(env, msg, SC_A_STORED_METHOD) ||
+            msg->appendString(env, msg, s->method)) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                         "handle.request() Error encoding method %s\n",
+                         s->method);
+		}
+	}
+
+    if (s->attributes->size(env, s->attributes) > 0) {
+        for (i = 0; i < s->attributes->size(env, s->attributes); i++) {
+            char *name = s->attributes->nameAt(env, s->attributes, i);
+            char *val = s->attributes->valueAt(env, s->attributes, i);
+            if (msg->appendByte(env, msg, SC_A_REQ_ATTRIBUTE) ||
+                msg->appendString(env, msg, name) ||
+                msg->appendString(env, msg, val)) {
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "handle.request() Error serializing attribute %s=%s\n",
+                              name, val);
+                return JK_ERR;
+            }
+        }
+    }
+
+    if (msg->appendByte(env, msg, SC_A_ARE_DONE)) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "handle.request() Error serializing end marker\n");
+        return JK_ERR;
+    }
+
+
+    if (debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "serialize.request() serialized %s\n", s->req_uri);
+
+    /*  msg->dump( env, msg, "Dump: " ); */
+    return JK_OK;
+}
+
+
+/** The inital BODY chunk 
+ */
+int jk2_serialize_postHead(jk_env_t *env, jk_msg_t *msg,
+                           jk_ws_service_t *r, jk_endpoint_t *ae)
+{
+    int len = r->left_bytes_to_send;
+
+    if (len > AJP13_MAX_SEND_BODY_SZ) {
+        len = AJP13_MAX_SEND_BODY_SZ;
+    }
+    if (len <= 0) {
+        len = 0;
+        return JK_OK;
+    }
+
+    len = msg->appendFromServer(env, msg, r, ae, len);
+    /* the right place to add file storage for upload */
+    if (len >= 0) {
+        r->content_read += len;
+        return JK_OK;
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                  "handler.marshalPostHead() - error len=%d\n", len);
+    return JK_ERR;
+}
+
+/* -------------------- Query decoding -------------------- */
+
+/** Read a query string into the map
+ */
+int jk2_requtil_queryRead(jk_env_t *env, jk_map_t *m, const char *query)
+{
+    char *sep;
+    char *value;
+    char *qry = m->pool->pstrdup(env, m->pool, query);
+
+    while (qry != NULL) {
+        sep = strchr(qry, '&');
+        if (sep != NULL) {
+            *sep = '\0';
+            sep++;
+        }
+
+        value = strchr(qry, '=');
+        if (value == NULL) {
+            value = "";
+        }
+        else {
+            *value = '\0';
+            value++;
+        }
+        m->add(env, m, m->pool->pstrdup(env, m->pool, qry),
+               m->pool->pstrdup(env, m->pool, value));
+        qry = sep;
+    }
+    return JK_OK;
+}
+
+
+/* -------------------- Request encoding -------------------- */
+/* Moved from IIS adapter */
+
+#define T_OS_ESCAPE_PATH	(4)
+
+static const unsigned char test_char_table[256] = {
+    0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, 14,
+        14,
+    14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 7, 6, 1, 6, 1, 1,
+    9, 9, 1, 0, 8, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 15,
+    15, 8, 15, 15, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 7, 0, 7, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 15, 7, 15, 1, 14, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
+};
+
+#define TEST_CHAR(c, f)	(test_char_table[(unsigned)(c)] & (f))
+
+static const char c2x_table[] = "0123456789abcdef";
+
+static unsigned char *c2x(unsigned what, unsigned char *where)
+{
+    *where++ = '%';
+    *where++ = c2x_table[what >> 4];
+    *where++ = c2x_table[what & 0xf];
+    return where;
+}
+
+int jk_requtil_escapeUrl(const char *path, char *dest, int destsize)
+{
+    const unsigned char *s = (const unsigned char *)path;
+    unsigned char *d = (unsigned char *)dest;
+    unsigned char *e = (unsigned char *)(dest + destsize - 1);
+    unsigned char *ee = (unsigned char *)(dest + destsize - 3);
+    unsigned c;
+
+    while ((c = *s)) {
+        if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
+            if (d >= ee)
+                return JK_ERR;
+            d = c2x(c, d);
+        }
+        else {
+            if (d >= e)
+                return JK_ERR;
+            *d++ = c;
+        }
+        ++s;
+    }
+    *d = '\0';
+    return JK_OK;
+}
+
+/* XXX Make it a default checking in uri worker map
+ */
+int jk_requtil_uriIsWebInf(char *uri)
+{
+    char *c = uri;
+    while (*c) {
+        *c = tolower(*c);
+        c++;
+    }
+    if (strstr(uri, "web-inf")) {
+        return JK_TRUE;
+    }
+    if (strstr(uri, "meta-inf")) {
+        return JK_TRUE;
+    }
+
+    return JK_FALSE;
+}
+
+static char x2c(const char *what)
+{
+    register char digit;
+
+    digit =
+        ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
+    digit *= 16;
+    digit +=
+        (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
+    return (digit);
+}
+
+int jk_requtil_unescapeUrl(char *url)
+{
+    register int x, y, badesc, badpath;
+
+    badesc = 0;
+    badpath = 0;
+    for (x = 0, y = 0; url[y]; ++x, ++y) {
+        if (url[y] != '%')
+            url[x] = url[y];
+        else {
+            if (!isxdigit(url[y + 1]) || !isxdigit(url[y + 2])) {
+                badesc = 1;
+                url[x] = '%';
+            }
+            else {
+                url[x] = x2c(&url[y + 1]);
+                y += 2;
+                if (url[x] == '/' || url[x] == '\0')
+                    badpath = 1;
+            }
+        }
+    }
+    url[x] = '\0';
+    if (badesc)
+        return -1;
+    else if (badpath)
+        return -2;
+    else
+        return JK_OK;
+}
+
+void jk_requtil_getParents(char *name)
+{
+    int l, w;
+
+    /* Four paseses, as per RFC 1808 */
+    /* a) remove ./ path segments */
+
+    for (l = 0, w = 0; name[l] != '\0';) {
+        if (name[l] == '.' && name[l + 1] == '/'
+            && (l == 0 || name[l - 1] == '/'))
+            l += 2;
+        else
+            name[w++] = name[l++];
+    }
+
+    /* b) remove trailing . path, segment */
+    if (w == 1 && name[0] == '.')
+        w--;
+    else if (w > 1 && name[w - 1] == '.' && name[w - 2] == '/')
+        w--;
+    name[w] = '\0';
+
+    /* c) remove all xx/../ segments. (including leading ../ and /../) */
+    l = 0;
+
+    while (name[l] != '\0') {
+        if (name[l] == '.' && name[l + 1] == '.' && name[l + 2] == '/' &&
+            (l == 0 || name[l - 1] == '/')) {
+            register int m = l + 3, n;
+
+            l = l - 2;
+            if (l >= 0) {
+                while (l >= 0 && name[l] != '/')
+                    l--;
+                l++;
+            }
+            else
+                l = 0;
+            n = l;
+            while ((name[n] = name[m]))
+                (++n, ++m);
+        }
+        else
+            ++l;
+    }
+
+    /* d) remove trailing xx/.. segment. */
+    if (l == 2 && name[0] == '.' && name[1] == '.')
+        name[0] = '\0';
+    else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.'
+             && name[l - 3] == '/') {
+        l = l - 4;
+        if (l >= 0) {
+            while (l >= 0 && name[l] != '/')
+                l--;
+            l++;
+        }
+        else
+            l = 0;
+        name[l] = '\0';
+    }
+}
+
+
+
+
+static const char begin_cert[] = "-----BEGIN CERTIFICATE-----\r\n";
+
+static const char end_cert[] = "-----END CERTIFICATE-----\r\n";
+
+static const char basis_64[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+int jk_requtil_base64CertLen(int len)
+{
+    int n = ((len + 2) / 3 * 4) + 1;    /* base64 encoded size */
+    n += (n + 63 / 64) * 2;     /* add CRLF's */
+    n += sizeof(begin_cert) + sizeof(end_cert) - 2;     /* add enclosing strings. */
+    return n;
+}
+
+int jk_requtil_base64EncodeCert(char *encoded,
+                                const unsigned char *string, int len)
+{
+    int i, c;
+    char *p;
+    const char *t;
+
+    p = encoded;
+
+    t = begin_cert;
+    while (*t != '\0')
+        *p++ = *t++;
+
+    c = 0;
+    for (i = 0; i < len - 2; i += 3) {
+        *p++ = basis_64[(string[i] >> 2) & 0x3F];
+        *p++ = basis_64[((string[i] & 0x3) << 4) |
+                        ((int)(string[i + 1] & 0xF0) >> 4)];
+        *p++ = basis_64[((string[i + 1] & 0xF) << 2) |
+                        ((int)(string[i + 2] & 0xC0) >> 6)];
+        *p++ = basis_64[string[i + 2] & 0x3F];
+        c += 4;
+        if (c >= 64) {
+            *p++ = '\r';
+            *p++ = '\n';
+            c = 0;
+        }
+    }
+    if (i < len) {
+        *p++ = basis_64[(string[i] >> 2) & 0x3F];
+        if (i == (len - 1)) {
+            *p++ = basis_64[((string[i] & 0x3) << 4)];
+            *p++ = '=';
+        }
+        else {
+            *p++ = basis_64[((string[i] & 0x3) << 4) |
+                            ((int)(string[i + 1] & 0xF0) >> 4)];
+            *p++ = basis_64[((string[i + 1] & 0xF) << 2)];
+        }
+        *p++ = '=';
+        c++;
+    }
+    if (c != 0) {
+        *p++ = '\r';
+        *p++ = '\n';
+    }
+
+    t = end_cert;
+    while (*t != '\0')
+        *p++ = *t++;
+
+    *p++ = '\0';
+    return p - encoded;
+}
+
+
+
+
+/** Initialize the request 
+ * 
+ * jk_init_ws_service
+ */
+void jk2_requtil_initRequest(jk_env_t *env, jk_ws_service_t *s)
+{
+    s->ws_private = NULL;
+    s->method = NULL;
+    s->protocol = NULL;
+    s->req_uri = NULL;
+    s->remote_addr = NULL;
+    s->remote_host = NULL;
+    s->remote_user = NULL;
+    s->auth_type = NULL;
+    s->query_string = NULL;
+    s->server_name = NULL;
+    s->server_port = 80;
+    s->server_software = NULL;
+    s->content_length = 0;
+    s->is_chunked = 0;
+    s->no_more_chunks = 0;
+    s->content_read = 0;
+    s->is_ssl = JK_FALSE;
+    s->ssl_cert = NULL;
+    s->ssl_cert_len = 0;
+    s->ssl_cipher = NULL;
+    s->ssl_session = NULL;
+    s->jvm_route = NULL;
+    s->uriEnv = NULL;
+    s->outBuf = NULL;
+    s->msg = NULL;
+
+    s->jkprintf = jk2_requtil_printf;
+}
diff --git a/connectors/jk/native2/common/jk_shm.c b/connectors/jk/native2/common/jk_shm.c
new file mode 100644
index 0000000..2648295
--- /dev/null
+++ b/connectors/jk/native2/common/jk_shm.c
@@ -0,0 +1,495 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ */
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_shm.h"
+
+#include "apr_shm.h"
+#include "apr_rmm.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+
+
+#define SHM_WRITE_SLOT 2
+#define SHM_RESET 5
+#define SHM_DUMP 6
+
+
+static int JK_METHOD jk2_shm_destroy(jk_env_t *env, jk_shm_t *shmem)
+{
+    apr_status_t rv = APR_SUCCESS;
+#if APR_HAS_SHARED_MEMORY
+    apr_shm_t *shm = (apr_shm_t *) shmem->privateData;
+
+    if (shm) {
+        if (shmem->attached)
+            rv = apr_shm_detach(shm);
+        else
+            rv = apr_shm_destroy(shm);
+    }
+#endif
+    shmem->head = NULL;
+    shmem->image = NULL;
+
+    return rv == APR_SUCCESS ? JK_OK : JK_ERR;
+}
+
+static int jk2_shm_create(jk_env_t *env, jk_shm_t *shmem)
+{
+    apr_status_t rc = APR_EGENERAL;
+    apr_shm_t *shm = NULL;
+    apr_pool_t *globalShmPool;
+
+    globalShmPool = (apr_pool_t *) env->getAprPool(env);
+
+    if (!globalShmPool) {
+        return JK_ERR;
+    }
+
+    if (shmem->inmem) {
+        shmem->head =
+            apr_pcalloc(globalShmPool,
+                        sizeof(jk_shm_head_t) + shmem->slotMaxCount);
+        shmem->image =
+            apr_pcalloc(globalShmPool, shmem->slotMaxCount * shmem->slotSize);
+        shmem->head->structSize = sizeof(jk_shm_head_t) + shmem->slotMaxCount;
+        shmem->head->slotSize = shmem->slotSize;
+        shmem->head->slotMaxCount = shmem->slotMaxCount;
+        return JK_OK;
+    }
+#if APR_HAS_SHARED_MEMORY
+    /* XXX: deal with anonymous */
+    if (strncmp(shmem->fname, "anon", 4) == 0) {
+        rc = apr_shm_create(&shm, shmem->size, NULL, globalShmPool);
+        if (APR_STATUS_IS_ENOTIMPL(rc)) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "shm.create(): Anonymous shared memory not implemented %d\n");
+            shmem->privateData = NULL;
+            return JK_ERR;
+        }
+    }
+    if (rc != APR_SUCCESS) {
+        rc = apr_shm_create(&shm, shmem->size, shmem->fname, globalShmPool);
+        if (rc == APR_EEXIST) {
+            /*
+             * The shm could have already been created (i.e. we may be a child process).
+             * See if we can attach to the existing shared memory.
+             */
+            rc = apr_shm_attach(&shm, shmem->fname, globalShmPool);
+            shmem->attached = 1;
+        }
+    }
+    if (rc != APR_SUCCESS) {
+        char ebuf[256];
+        apr_strerror(rc, ebuf, 256);
+
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "shm.create(): error creating shm %d %s\n", rc, ebuf);
+        shmem->privateData = NULL;
+        return JK_ERR;
+    }
+    shmem->privateData = shm;
+    shmem->head = (jk_shm_head_t *) apr_shm_baseaddr_get(shm);
+
+    if (!shmem->attached) {
+        /* Allocate header */
+        apr_size_t head_size = sizeof(jk_shm_head_t) + shmem->slotMaxCount;
+        /* align to slotSize */
+        head_size = APR_ALIGN(head_size, shmem->slotSize);
+        memset(shmem->head, 0, head_size);
+        if (shmem->head) {
+            shmem->head->structSize = (int)head_size;
+            shmem->head->slotSize = shmem->slotSize;
+            shmem->head->slotMaxCount = shmem->slotMaxCount;
+        }
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "shm.create() Created head %#lx size %d\n",
+                      shmem->head, head_size);
+
+    }
+    else {
+        shmem->slotSize = shmem->head->slotSize;
+        shmem->slotMaxCount = shmem->head->slotMaxCount;
+    }
+    shmem->image =
+        ((char *)apr_shm_baseaddr_get(shm)) + shmem->head->structSize;
+#endif
+    return JK_OK;
+}
+
+
+/* Create or reinit an existing scoreboard. The MPM can control whether
+ * the scoreboard is shared across multiple processes or not
+ */
+static int JK_METHOD jk2_shm_init(struct jk_env *env, jk_shm_t *shm)
+{
+
+    int rv = JK_OK;
+
+    /* In case the shm has been initialized already
+     * for the current process.
+     */
+    if (shm->head && shm->image)
+        return rv;
+
+    shm->privateData = NULL;
+
+    if (shm->fname == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "shm.init(): shm file not specified\n");
+        return JK_ERR;
+    }
+    if (!shm->slotMaxCount)
+        shm->slotMaxCount = 1;
+    shm->size = shm->slotSize * shm->slotMaxCount;
+    shm->size +=
+        APR_ALIGN(sizeof(jk_shm_head_t) + shm->slotMaxCount, shm->slotSize);
+
+    /* The smalles size is 64K */
+    shm->size = APR_ALIGN(shm->size, 1024 * 64);
+
+    if (shm->mbean->debug > 0) {
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "shm.init(): file=%s size=%d\n", shm->fname, shm->size);
+    }
+
+    rv = jk2_shm_create(env, shm);
+
+    if (rv != JK_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "shm.create(): error creating shm %s\n", shm->fname);
+        return rv;
+    }
+
+    if (shm->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "shm.create(): shm created %#lx %#lx %d\n",
+                      shm->head, shm->image, shm->attached);
+
+    return JK_OK;
+}
+
+/** Reset the scoreboard, in case it gets corrupted.
+ *  Will remove all slots and set the head in the original state.
+ */
+static int JK_METHOD jk2_shm_reset(jk_env_t *env, jk_shm_t *shm)
+{
+    if (shm->head == NULL) {
+        return JK_ERR;
+    }
+
+    shm->head->slotSize = shm->slotSize;
+    shm->head->slotMaxCount = shm->slotMaxCount;
+    shm->head->lastSlot = 0;
+    memset(shm->head->slots, 0, shm->head->slotMaxCount);
+    if (shm->mbean->debug > 0) {
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG, "shm.init() Reset %s %#lx\n",
+                      shm->fname, shm->image);
+    }
+    return JK_OK;
+}
+
+
+/* pos starts with 1 ( 0 is the head )
+ */
+jk_shm_slot_t *JK_METHOD jk2_shm_getSlot(struct jk_env * env,
+                                         struct jk_shm * shm, int pos)
+{
+    jk_shm_slot_t *slot = NULL;
+
+    if (!shm->image)
+        return NULL;
+    if (pos >= shm->slotMaxCount)
+        return NULL;
+    /* Use only allocated slots */
+    if (shm->head->slots[pos])
+        slot = (jk_shm_slot_t *) ((char *)shm->image + pos * shm->slotSize);
+    if (slot) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "shm.getSlot() found existing slot %d %s\n",
+                      pos * shm->slotSize, slot->name);
+    }
+    return slot;
+}
+
+jk_shm_slot_t *JK_METHOD jk2_shm_createSlot(struct jk_env * env,
+                                            struct jk_shm * shm,
+                                            char *name, int size)
+{
+    /* For now all slots are equal size
+     */
+    int i;
+    jk_shm_slot_t *slot = NULL;
+
+    if (shm->head != NULL) {
+        for (i = 0; i < shm->head->lastSlot; i++) {
+            slot = shm->getSlot(env, shm, i);
+            if (strncmp(slot->name, name, strlen(name)) == 0) {
+                env->l->jkLog(env, env->l, JK_LOG_INFO,
+                              "shm.createSlot() found existing slot %s\n",
+                              slot->name);
+                return slot;
+            }
+        }
+    }
+    else {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "shm.createSlot() no shared memory head\n");
+        return NULL;
+    }
+    /* Allocate new slot  
+     * TODO: We need the mutex here
+     */
+    for (i = shm->head->lastSlot; i < shm->head->slotMaxCount; i++) {
+        if (!shm->head->slots[i]) {
+            slot =
+                (jk_shm_slot_t *) ((char *)shm->image +
+                                   i * shm->head->slotSize);
+            /* Mark the slot as used */
+            shm->head->slots[i] = 1;
+            /* Clear any garbage data */
+            memset(slot, 0, shm->head->slotSize);
+            ++shm->head->lastSlot;
+            break;
+        }
+    }
+    if (slot == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "shm.createSlot() create %d returned NULL\n",
+                      shm->slotSize);
+        return NULL;
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "shm.createSlot() Created slot %d\n", shm->head->lastSlot);
+    strncpy(slot->name, name, 64);
+    slot->size = size;
+    return slot;
+}
+
+/** Get an ID that is unique across processes.
+ */
+int JK_METHOD jk2_shm_getId(struct jk_env *env, struct jk_shm *shm)
+{
+
+    return 0;
+}
+
+static void jk2_shm_resetEndpointStats(jk_env_t *env, struct jk_shm *shm)
+{
+    int i, j;
+
+    if (!shm || !shm->head) {
+        return;
+    }
+
+    for (i = 0; i < shm->head->lastSlot; i++) {
+        jk_shm_slot_t *slot = shm->getSlot(env, shm, i);
+
+        if (slot == NULL)
+            continue;
+
+        if (strncmp(slot->name, "epStat", 6) == 0) {
+            /* This is an endpoint slot */
+            void *data = slot->data;
+
+            for (j = 0; j < slot->structCnt; j++) {
+                jk_stat_t *statArray = (jk_stat_t *)data;
+                jk_stat_t *stat = statArray + j;
+
+                stat->reqCnt = 0;
+                stat->errCnt = 0;
+                stat->totalTime = 0;
+                stat->maxTime = 0;
+            }
+        }
+    }
+}
+
+
+static char *jk2_shm_setAttributeInfo[] = { "resetEndpointStats",
+    "file", "size",
+    "slots", "useMemory",
+    NULL
+};
+
+static int JK_METHOD jk2_shm_setAttribute(jk_env_t *env, jk_bean_t *mbean,
+                                          char *name, void *valueP)
+{
+    jk_shm_t *shm = (jk_shm_t *)mbean->object;
+    char *value = (char *)valueP;
+
+    if (strcmp("file", name) == 0) {
+        shm->fname = value;
+    }
+    else if (strcmp("size", name) == 0) {
+        shm->size = atoi(value);
+    }
+    else if (strcmp("slots", name) == 0) {
+        shm->slotMaxCount = atoi(value);
+    }
+    else if (strcmp("useMemory", name) == 0) {
+        shm->inmem = atoi(value);
+    }
+    else if (strcmp("resetEndpointStats", name) == 0) {
+        if (strcmp(value, "1") == 0)
+            jk2_shm_resetEndpointStats(env, shm);
+    }
+    else {
+        return JK_ERR;
+    }
+    return JK_OK;
+
+}
+
+static char *jk2_shm_getAttributeInfo[] =
+    { "file", "size", "slots", "useMemory", NULL };
+
+static void *JK_METHOD jk2_shm_getAttribute(jk_env_t *env, jk_bean_t *mbean,
+                                            char *name)
+{
+    jk_shm_t *shm = (jk_shm_t *)mbean->object;
+
+    if (strcmp(name, "file") == 0) {
+        return shm->fname;
+    }
+    else if (strcmp(name, "size") == 0) {
+        return jk2_env_itoa(env, shm->size);
+    }
+    else if (strcmp(name, "slots") == 0) {
+        return jk2_env_itoa(env, shm->slotMaxCount);
+    }
+    else if (strcmp(name, "useMemory") == 0) {
+        return jk2_env_itoa(env, shm->inmem);
+    }
+    return NULL;
+}
+
+
+/** Copy a chunk of data into a named slot
+ */
+static int jk2_shm_writeSlot(jk_env_t *env, jk_shm_t *shm,
+                             char *instanceName, char *buf, int len)
+{
+    jk_shm_slot_t *slot;
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "shm.writeSlot() %s %d\n", instanceName, len);
+    if ((size_t) len > (shm->slotSize - sizeof(jk_shm_slot_t))) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "shm.writeSlot() Packet too large %d %d\n",
+                      shm->slotSize - sizeof(jk_shm_slot_t), len);
+        return JK_ERR;
+    }
+    if (shm->head == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "shm.writeSlot() No head - shm was not initalized\n");
+        return JK_ERR;
+    }
+
+    slot = shm->createSlot(env, shm, instanceName, 0);
+
+    /*  Copy the body in the slot */
+    memcpy(slot->data, buf, len);
+    slot->size = len;
+    slot->ver++;
+    /* Update the head lb version number - that would triger
+       reconf on the next request */
+    shm->head->lbVer++;
+
+    return JK_OK;
+}
+
+/* ==================== Dispatch messages from java ==================== */
+
+/** Called by java. Will call the right shm method.
+ */
+static int JK_METHOD jk2_shm_invoke(jk_env_t *env, jk_bean_t *bean,
+                                    jk_endpoint_t *ep, int code,
+                                    jk_msg_t *msg, int raw)
+{
+    jk_shm_t *shm = (jk_shm_t *)bean->object;
+
+    if (shm->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG, "shm.%d() \n", code);
+
+    switch (code) {
+    case SHM_WRITE_SLOT:{
+            char *instanceName = msg->getString(env, msg);
+            char *buf = (char *)msg->buf;
+            int len = msg->len;
+
+            return jk2_shm_writeSlot(env, shm, instanceName, buf, len);
+        }
+    case SHM_RESET:{
+            jk2_shm_reset(env, shm);
+
+            return JK_OK;
+        }
+    case SHM_DUMP:{
+#if 0
+            char *name = msg->getString(env, msg);
+            /* XXX do we realy need that */
+            jk2_shm_dump(env, shm, name);
+#endif
+            return JK_OK;
+        }
+    }                           /* switch */
+    return JK_ERR;
+}
+
+int JK_METHOD jk2_shm_factory(jk_env_t *env, jk_pool_t *pool,
+                              jk_bean_t *result,
+                              const char *type, const char *name)
+{
+    jk_shm_t *shm;
+
+    shm = (jk_shm_t *)pool->calloc(env, pool, sizeof(jk_shm_t));
+
+    if (shm == NULL)
+        return JK_ERR;
+
+    shm->pool = pool;
+    shm->privateData = NULL;
+
+    shm->slotSize = DEFAULT_SLOT_SIZE;
+    shm->slotMaxCount = DEFAULT_SLOT_COUNT;
+
+    result->setAttribute = jk2_shm_setAttribute;
+    result->setAttributeInfo = jk2_shm_setAttributeInfo;
+    /* Add the following to this function - seems someone else */
+    /* thought of it based on the 'comment' previously there */
+    result->getAttributeInfo = jk2_shm_getAttributeInfo;
+    result->getAttribute = jk2_shm_getAttribute;
+    result->multiValueInfo = NULL;
+    shm->mbean = result;
+    result->object = shm;
+    result->invoke = jk2_shm_invoke;
+    shm->init = jk2_shm_init;
+    shm->destroy = jk2_shm_destroy;
+
+    shm->getSlot = jk2_shm_getSlot;
+    shm->createSlot = jk2_shm_createSlot;
+    shm->reset = jk2_shm_reset;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_signal.c b/connectors/jk/native2/common/jk_signal.c
new file mode 100644
index 0000000..cd7fcf6
--- /dev/null
+++ b/connectors/jk/native2/common/jk_signal.c
@@ -0,0 +1,127 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_channel.h"
+
+
+#ifdef HAVE_SIGNAL
+
+#include "signal.h"
+
+/** Deal with 'signals'. 
+ */
+
+static struct sigaction jkAction;
+
+/* We use a jni channel to send the notification to java
+ */
+static jk_channel_t *jniChannel;
+
+/* XXX we should sync or use multiple endpoints if multiple signals
+   can be concurent
+*/
+static jk_endpoint_t *signalEndpoint;
+
+static void jk2_SigAction(int sig)
+{
+    jk_env_t *env;
+
+    /* Make a callback using the jni channel */
+    fprintf(stderr, "Signal %d\n", sig);
+
+    if (jk_env_globalEnv == NULL) {
+        return;
+    }
+
+    env = jk_env_globalEnv->getEnv(jk_env_globalEnv);
+
+    if (jniChannel == NULL) {
+        jniChannel = env->getByName(env, "channel.jni:jni");
+        fprintf(stderr, "Got jniChannel %p\n", jniChannel);
+    }
+    if (jniChannel == NULL) {
+        return;
+    }
+    if (signalEndpoint == NULL) {
+        jk_bean_t *component = env->createBean2(env, NULL, "endpoint", NULL);
+        if (component == NULL) {
+            fprintf(stderr, "Can't create endpoint\n");
+            return;
+        }
+        component->init(env, component);
+        fprintf(stderr, "Create endpoint %p\n", component->object);
+        signalEndpoint = component->object;
+    }
+
+    /* Channel:jni should be initialized by the caller */
+
+    /* XXX make the callback */
+
+    jk_env_globalEnv->releaseEnv(jk_env_globalEnv, env);
+
+}
+
+
+/* XXX We need to: - preserve the old signal ( or get them ) - either
+     implement "waitSignal" or use invocation in jk2_SigAction
+
+     Probably waitSignal() is better ( we can have a thread that waits )
+ */
+
+static int jk2_signal_signal(jk_env_t *env, int signalNr)
+{
+    memset(&jkAction, 0, sizeof(jkAction));
+    jkAction.sa_handler = jk2_SigAction;
+    sigaction(signalNr, &jkAction, (void *)NULL);
+    return 0;
+}
+
+static int jk2_signal_sendSignal(jk_env_t *env, int target, int signo)
+{
+    return kill((pid_t) target, signo);
+}
+
+
+
+int JK_METHOD jk2_signal_factory(jk_env_t *env, jk_pool_t *pool,
+                                 jk_bean_t *result,
+                                 const char *type, const char *name)
+{
+    result->setAttribute = NULL;
+    result->object = "signal_struct_placeholder";
+    result->invoke = NULL;
+    result->init = NULL;
+    result->destroy = NULL;
+
+    return JK_OK;
+}
+
+
+
+#else /* ! HAVE_SIGNALS */
+
+int JK_METHOD jk2_signal_factory(jk_env_t *env, jk_pool_t *pool,
+                                 jk_bean_t *result,
+                                 const char *type, const char *name)
+{
+    result->disabled = JK_TRUE;
+    return JK_FALSE;
+}
+
+#endif
diff --git a/connectors/jk/native2/common/jk_uriEnv.c b/connectors/jk/native2/common/jk_uriEnv.c
new file mode 100644
index 0000000..95f213e
--- /dev/null
+++ b/connectors/jk/native2/common/jk_uriEnv.c
@@ -0,0 +1,540 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Location properties. UriEnv can be:
+ *                                                                 
+ * Exact Context -> /exact/uri=worker e.g. /examples/do[STAR]=ajp12
+ * Context Based -> /context/[STAR]=worker e.g. /examples/[STAR]=ajp12
+ * Context and suffix ->/context/[STAR].suffix=worker e.g. /examples/[STAR].jsp=ajp12
+ *                                                                         
+ */
+
+#include "jk_pool.h"
+#include "jk_env.h"
+#include "jk_uriMap.h"
+#include "jk_registry.h"
+
+#ifdef HAS_AP_PCRE
+#include "httpd.h"
+#else
+#ifdef HAS_PCRE
+#include "pcre.h"
+#include "pcreposix.h"
+#endif
+#endif
+
+/* return non-zero if pattern has any glob chars in it */
+
+static int jk2_is_wildmatch(const char *pattern)
+{
+    while (*pattern) {
+        switch (*pattern) {
+        case '?':
+        case '*':
+            return 1;
+        }
+        ++pattern;
+    }
+    return 0;
+}
+
+/** Parse the name:
+       VHOST/PATH
+
+    If VHOST is empty, we map to the default host.
+
+    The PATH will be further split in CONTEXT/LOCALPATH during init ( after
+    we have all uris, including the context paths ).
+*/
+static int jk2_uriEnv_parseName(jk_env_t *env, jk_uriEnv_t *uriEnv,
+                                char *name)
+{
+    char *uri = NULL;
+    char *colon;
+    char host[1024];
+    int pcre = 0;
+
+    if (*name == '$') {
+#if defined(HAS_PCRE) || defined(HAS_AP_PCRE)
+        ++name;
+        uriEnv->uri = uriEnv->pool->pstrdup(env, uriEnv->pool, name);
+        uriEnv->match_type = MATCH_TYPE_REGEXP;
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "uriEnv.parseName() parsing %s regexp\n", name);
+        {
+#ifdef HAS_AP_PCRE
+            regex_t *preg =
+                ap_pregcomp((apr_pool_t *) uriEnv->pool->_private,
+                            uriEnv->uri, REG_EXTENDED);
+            if (!preg) {
+#else
+            regex_t *preg =
+                (regex_t *) uriEnv->pool->calloc(env, uriEnv->pool,
+                                                 sizeof(regex_t));
+            if (regcomp(preg, uriEnv->uri, REG_EXTENDED)) {
+#endif
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "uriEnv.parseName() error compiling regexp %s\n",
+                              uri);
+                return JK_ERR;
+            }
+            uriEnv->regexp = preg;
+        }
+        return JK_OK;
+#else
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "uriEnv.parseName() parsing regexp %s not supported\n",
+                      uri);
+        return JK_ERR;
+#endif
+    }
+
+    strcpy(host, name);
+    colon = strchr(host, ':');
+    uri = strchr(host, '$');
+    if (uri)
+        pcre = 1;
+    if (colon != NULL) {
+        ++colon;
+        if (!uri)
+            uri = strchr(colon, '/');
+    }
+    else if (!uri)
+        uri = strchr(host, '/');
+
+    if (!uri) {
+        /* That's a virtual host definition ( no actual mapping, just global
+         * settings like aliases, etc
+         */
+
+        uriEnv->match_type = MATCH_TYPE_HOST;
+        if (colon)
+            uriEnv->port = atoi(colon);
+        uriEnv->virtual = uriEnv->pool->pstrdup(env, uriEnv->pool, host);
+        return JK_OK;
+    }
+
+    *uri = '\0';
+    if (colon)
+        uriEnv->port = atoi(colon);
+
+    /* If it doesn't start with /, it must have a vhost */
+    if (strlen(host) && uri != host) {
+        uriEnv->virtual =
+            uriEnv->pool->calloc(env, uriEnv->pool, strlen(host) + 1);
+        strncpy(uriEnv->virtual, name, strlen(host));
+    }
+    else
+        uriEnv->virtual = "*";
+
+    *uri = '/';
+    if (pcre) {
+        ++uri;
+        uriEnv->match_type = MATCH_TYPE_REGEXP;
+#if defined(HAS_PCRE) || defined(HAS_AP_PCRE)
+        uriEnv->uri = uriEnv->pool->pstrdup(env, uriEnv->pool, uri);
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "uriEnv.parseName() parsing regexp %s\n", uri);
+        {
+#ifdef HAS_AP_PCRE
+            regex_t *preg =
+                ap_pregcomp((apr_pool_t *) uriEnv->pool->_private,
+                            uriEnv->uri, REG_EXTENDED);
+            if (!preg) {
+#else
+            regex_t *preg =
+                (regex_t *) uriEnv->pool->calloc(env, uriEnv->pool,
+                                                 sizeof(regex_t));
+            if (regcomp(preg, uriEnv->uri, REG_EXTENDED)) {
+#endif
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "uriEnv.parseName() error compiling regexp %s\n",
+                              uri);
+                return JK_ERR;
+            }
+            uriEnv->regexp = preg;
+        }
+#else
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "uriEnv.parseName() parsing regexp %s not supported\n",
+                      uri);
+#endif
+    }
+    else
+        uriEnv->uri = uriEnv->pool->pstrdup(env, uriEnv->pool, uri);
+
+    return JK_OK;
+}
+
+static char *getAttInfo[] =
+    { "host", "uri", "group", "context", "inheritGlobals",
+    "match_type", "servlet", "timing", "aliases", "debug", "disabled",
+    NULL
+};
+static char *setAttInfo[] =
+    { "host", "uri", "group", "context", "inheritGlobals",
+    "servlet", "timing", "alias", "path", "debug", "disabled", 
+    NULL
+};
+
+static char *matchTypes[] = {
+    "exact",
+    "prefix",
+    "suffix",
+    "gensuffix",
+    "contextpath",
+    "host",
+    "context",
+    "regexp"
+};
+
+static void *JK_METHOD jk2_uriEnv_getAttribute(jk_env_t *env, jk_bean_t *bean,
+                                               char *name)
+{
+    jk_uriEnv_t *uriEnv = (jk_uriEnv_t *)bean->object;
+
+    if (strcmp(name, "host") == 0) {
+        return uriEnv->virtual;
+    }
+    else if (strcmp(name, "uri") == 0) {
+        return uriEnv->uri;
+    }
+    else if (strcmp(name, "group") == 0) {
+        return uriEnv->workerName;
+    }
+    else if (strcmp(name, "context") == 0) {
+        return uriEnv->contextPath;
+    }
+    else if (strcmp(name, "servlet") == 0) {
+        return uriEnv->servlet;
+    }
+    else if (strcmp(name, "prefix") == 0) {
+        return uriEnv->prefix;
+    }
+    else if (strcmp(name, "suffix") == 0) {
+        return uriEnv->suffix;
+    }
+    else if (strcmp(name, "match_type") == 0) {
+        return matchTypes[uriEnv->match_type];
+    }
+    else if (strcmp("timing", name) == 0) {
+        return jk2_env_itoa(env, uriEnv->timing);
+    }
+    else if (strcmp("aliases", name) == 0 && uriEnv->aliases) {
+        return jk2_map_concatKeys(env, uriEnv->aliases, ":");
+    }
+    else if (strcmp("path", name) == 0) {
+        return uriEnv->uri;
+    }
+    else if (strcmp("inheritGlobals", name) == 0) {
+        return jk2_env_itoa(env, uriEnv->inherit_globals);
+    }
+    else if (strcmp(name, "debug") == 0) {
+        return jk2_env_itoa(env, bean->debug);
+    }
+    else if (strcmp(name, "disabled") == 0) {
+        return jk2_env_itoa(env, bean->disabled);
+    }    return NULL;
+}
+
+
+static int JK_METHOD jk2_uriEnv_setAttribute(jk_env_t *env,
+                                             jk_bean_t *mbean,
+                                             char *nameParam, void *valueP)
+{
+    jk_uriEnv_t *uriEnv = mbean->object;
+    char *valueParam = valueP;
+
+    char *name = uriEnv->pool->pstrdup(env, uriEnv->pool, nameParam);
+    char *val = uriEnv->pool->pstrdup(env, uriEnv->pool, valueParam);
+
+    uriEnv->properties->add(env, uriEnv->properties, name, val);
+
+    if (strcmp("group", name) == 0) {
+        uriEnv->workerName = val;
+        return JK_OK;
+    }
+    else if (strcmp("context", name) == 0) {
+        uriEnv->contextPath = val;
+        uriEnv->ctxt_len = strlen(val);
+
+        if (strcmp(val, uriEnv->uri) == 0) {
+            uriEnv->match_type = MATCH_TYPE_CONTEXT;
+        }
+        return JK_OK;
+    }
+    else if (strcmp("servlet", name) == 0) {
+        uriEnv->servlet = val;
+    }
+    else if (strcmp("timing", name) == 0) {
+        uriEnv->timing = atoi(val);
+    }
+    else if (strcmp("alias", name) == 0) {
+        if (uriEnv->match_type == MATCH_TYPE_HOST) {
+            if (uriEnv->aliases == NULL) {
+                jk2_map_default_create(env, &uriEnv->aliases, mbean->pool);
+            }
+            uriEnv->aliases->put(env, uriEnv->aliases, val, uriEnv, NULL);
+        }
+    }
+    else if (strcmp("path", name) == 0) {
+        /** This is called from Location in jk2, it has the same effect
+         *    as using the constructor.
+         */
+        if (val == NULL)
+            uriEnv->uri = NULL;
+        else
+            uriEnv->uri = uriEnv->pool->pstrdup(env, uriEnv->pool, val);
+    }
+    else if (strcmp("inheritGlobals", name) == 0) {
+        uriEnv->inherit_globals = atoi(val);
+    }
+    else if (strcmp("worker", name) == 0) {
+        /* OLD - DEPRECATED */
+       
+		uriEnv->workerName = val;
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "uriEnv.setAttribute() the %s directive is deprecated. Use 'group' instead.\n",
+                      name);
+    }
+    else if (strcmp("uri", name) == 0) {
+        jk2_uriEnv_parseName(env, uriEnv, val);
+    }
+    else if (strcmp("name", name) == 0) {
+        jk2_uriEnv_parseName(env, uriEnv, val);
+    }
+    else if (strcmp("vhost", name) == 0) {
+        if (val == NULL)
+            uriEnv->virtual = NULL;
+         else
+            uriEnv->virtual = uriEnv->pool->pstrdup(env, uriEnv->pool, val);
+    }
+    else if (strcmp(name, "debug") == 0) {
+        mbean->debug = atoi(val);
+    }
+    else if (strcmp(name, "disabled") == 0) {
+        mbean->disabled = atoi(val);
+    }
+    return JK_OK;
+}
+
+
+static int JK_METHOD jk2_uriEnv_beanInit(jk_env_t *env, jk_bean_t *bean)
+{
+    jk_uriEnv_t *uriEnv = bean->object;
+    int res = JK_OK;
+
+    if (bean->state == JK_STATE_INIT)
+        return JK_OK;
+
+    if (uriEnv->init) {
+        res = uriEnv->init(env, uriEnv);
+    }
+    if (res == JK_OK) {
+        bean->state = JK_STATE_INIT;
+    }
+    return res;
+}
+
+static int jk2_uriEnv_init(jk_env_t *env, jk_uriEnv_t *uriEnv)
+{
+    char *uri = uriEnv->pool->pstrdup(env, uriEnv->pool, uriEnv->uri);
+
+    /* Set the worker */
+    char *wname = uriEnv->workerName;
+
+    if (uriEnv->workerEnv->timing == JK_TRUE) {
+        uriEnv->timing = JK_TRUE;
+    }
+    if (uriEnv->workerName == NULL) {
+        /* The default worker */
+        uriEnv->workerName =
+            uriEnv->uriMap->workerEnv->defaultWorker->mbean->name;
+        wname = uriEnv->workerName;
+        uriEnv->worker = uriEnv->uriMap->workerEnv->defaultWorker;
+
+        if (uriEnv->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "uriEnv.init() map %s %s %s\n",
+                          uriEnv->uri,
+                          uriEnv->uriMap->workerEnv->defaultWorker->mbean->
+                          name, uriEnv->workerName);
+        if (uriEnv->workerName == NULL) {
+            uriEnv->workerName = "lb:lb";
+        }
+    }
+
+    /* No further init - will be called by uriMap.init() */
+
+    if (uriEnv->workerName != NULL && uriEnv->worker == NULL) {
+        uriEnv->worker = env->getByName(env, wname);
+        if (uriEnv->worker == NULL) {
+            uriEnv->worker = env->getByName2(env, "lb", wname);
+            if (uriEnv->worker == NULL) {
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "uriEnv.init() map to invalid worker %s %s\n",
+                              uriEnv->uri, uriEnv->workerName);
+                /* XXX that's allways a 'lb' worker, create it */
+            }
+        }
+    }
+
+    if (uri == NULL)
+        return JK_ERR;
+
+    if (uriEnv->match_type == MATCH_TYPE_REGEXP) {
+        uriEnv->prefix = uri;
+        uriEnv->prefix_len = strlen(uriEnv->prefix);
+        uriEnv->suffix = NULL;
+        if (uriEnv->mbean->debug > 0) {
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "uriEnv.init() regexp mapping %s=%s \n",
+                          uriEnv->prefix, uriEnv->workerName);
+
+        }
+        return JK_OK;
+    }
+    if ('/' != uri[0]) {
+        /*
+         * JFC: please check...
+         * Not sure what to do, but I try to prevent problems.
+         * I have fixed jk_mount_context() in apaches/mod_jk2.c so we should
+         * not arrive here when using Apache.
+         */
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "uriEnv.init() context must start with '/' in %s\n",
+                      uri);
+        return JK_ERR;
+    }
+
+    /* set the mapping type */
+    if (!jk2_is_wildmatch(uri)) {
+        /* Something like:  JkMount /login/j_security_check ajp13 */
+        uriEnv->prefix = uri;
+        uriEnv->prefix_len = strlen(uriEnv->prefix);
+        uriEnv->suffix = NULL;
+        if (uriEnv->match_type != MATCH_TYPE_CONTEXT &&
+            uriEnv->match_type != MATCH_TYPE_HOST) {
+            /* Context and host maps do not have ASTERISK */
+            uriEnv->match_type = MATCH_TYPE_EXACT;
+        }
+        if (uriEnv->mbean->debug > 0) {
+            if (uriEnv->match_type == MATCH_TYPE_CONTEXT) {
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "uriEnv.init() context mapping %s=%s \n",
+                              uriEnv->prefix, uriEnv->workerName);
+
+            }
+            else if (uriEnv->match_type == MATCH_TYPE_HOST) {
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "uriEnv.init() host mapping %s=%s \n",
+                              uriEnv->virtual, uriEnv->workerName);
+            }
+            else {
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "uriEnv.init() exact mapping %s=%s \n",
+                              uriEnv->prefix, uriEnv->workerName);
+            }
+        }
+        return JK_OK;
+    }
+
+    if (uri[strlen(uri) - 1] == '*') {
+        /* context based /context/prefix/ASTERISK  */
+        uri[strlen(uri) - 1] = '\0';
+        uriEnv->suffix = NULL;
+        uriEnv->prefix = uri;
+        uriEnv->prefix_len = strlen(uriEnv->prefix);
+        uriEnv->match_type = MATCH_TYPE_PREFIX;
+        if (uriEnv->mbean->debug > 0) {
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "uriEnv.init() prefix mapping %s=%s\n",
+                          uriEnv->prefix, uriEnv->workerName);
+        }
+    }
+    else {
+        /*
+         * We have an * or ? in the uri.
+         */
+        uriEnv->suffix = uri;
+        uriEnv->prefix = NULL;
+        uriEnv->prefix_len = 0;
+        uriEnv->suffix_len = strlen(uri);
+        uriEnv->match_type = MATCH_TYPE_SUFFIX;
+        if (uriEnv->mbean->debug > 0) {
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "uriEnv.init() suffix mapping %s=%s\n",
+                          uriEnv->prefix, uriEnv->workerName);
+        }
+    }
+    if (uriEnv->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "uriEnv.init()  %s  host=%s  uri=%s type=%d ctx=%s prefix=%s suffix=%s\n",
+                      uriEnv->mbean->name, uriEnv->virtual, uriEnv->uri,
+                      uriEnv->match_type, uriEnv->contextPath, uriEnv->prefix,
+                      uriEnv->suffix);
+
+    uriEnv->mbean->state = JK_STATE_INIT;
+    return JK_OK;
+}
+
+int JK_METHOD jk2_uriEnv_factory(jk_env_t *env, jk_pool_t *pool,
+                                 jk_bean_t *result,
+                                 const char *type, const char *name)
+{
+    jk_pool_t *uriPool;
+    jk_uriEnv_t *uriEnv;
+
+    uriPool = (jk_pool_t *)pool->create(env, pool, HUGE_POOL_SIZE);
+
+    uriEnv = (jk_uriEnv_t *)pool->calloc(env, uriPool, sizeof(jk_uriEnv_t));
+
+    uriEnv->pool = uriPool;
+
+    jk2_map_default_create(env, &uriEnv->properties, uriPool);
+
+    result->init = jk2_uriEnv_beanInit;
+    uriEnv->init = jk2_uriEnv_init;
+
+    result->setAttribute = jk2_uriEnv_setAttribute;
+    result->getAttribute = jk2_uriEnv_getAttribute;
+    uriEnv->mbean = result;
+    result->object = uriEnv;
+    result->getAttributeInfo = getAttInfo;
+    result->setAttributeInfo = setAttInfo;
+
+    uriEnv->name = result->localName;
+    if (jk2_uriEnv_parseName(env, uriEnv, result->localName) != JK_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "uriEnv.factory() error parsing %s\n", uriEnv->name);
+        return JK_ERR;
+    }
+    uriEnv->workerEnv = env->getByName(env, "workerEnv");
+    uriEnv->workerEnv->uriMap->addUriEnv(env, uriEnv->workerEnv->uriMap,
+                                         uriEnv);
+    uriEnv->uriMap = uriEnv->workerEnv->uriMap;
+
+    /* ??? This may be turned on by default 
+     * so that global mappings are always present
+     * on each vhost, instead of explicitly defined.
+     */
+#if 1
+    uriEnv->inherit_globals = 1;
+#endif
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_uriMap.c b/connectors/jk/native2/common/jk_uriMap.c
new file mode 100644
index 0000000..bfe93c5
--- /dev/null
+++ b/connectors/jk/native2/common/jk_uriMap.c
@@ -0,0 +1,1121 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Description: URI to worker map object.                          
+ * Maps can be                                                     
+ *                                                                 
+ * Exact Context -> /exact/uri=worker e.g. /examples/do[STAR]=ajp12
+ * Context Based -> /context/[STAR]=worker e.g. /examples/[STAR]=ajp12
+ * Context and suffix ->/context/[STAR].suffix=worker e.g. /examples/[STAR].jsp=ajp12
+ *                                                                         
+ * This lets us either partition the work among the web server and the     
+ * servlet container.                                                      
+ *                                                                         
+ * @author: Gal Shachor <shachor@il.ibm.com>
+ * @author: Costin Manolache
+ */
+
+#include "jk_pool.h"
+#include "jk_env.h"
+#include "jk_uriMap.h"
+#include "jk_registry.h"
+
+#ifdef HAS_AP_PCRE
+#include "httpd.h"
+#define REGEXEC ap_regexec
+#else
+#ifdef HAS_PCRE
+#include "pcre.h"
+#include "pcreposix.h"
+#define REGEXEC regexec
+#endif
+#endif
+
+static INLINE const char *jk2_findExtension(jk_env_t *env, const char *uri);
+
+static int jk2_uriMap_checkUri(jk_env_t *env, jk_uriMap_t *uriMap,
+                               const char *uri);
+
+#ifdef WIN32
+static int jk2_uri_icase = 1;
+#else
+static int jk2_uri_icase = 0;
+#endif
+
+/*
+ * We are now in a security nightmare, it maybe that somebody sent 
+ * us a uri that looks like /top-secret.jsp. and the web server will 
+ * fumble and return the jsp content. 
+ *
+ * To solve that we will check for path info following the suffix, we 
+ * will also check that the end of the uri is not ".suffix.",
+ * ".suffix/", or ".suffix ".
+ *
+ * Was: check_security_fraud
+ */
+static int jk2_uriMap_checkUri(jk_env_t *env, jk_uriMap_t *uriMap,
+                               const char *uri)
+{
+    int i;
+
+    for (i = 0; i < uriMap->maps->size(env, uriMap->maps); i++) {
+        jk_uriEnv_t *uriEnv = uriMap->maps->valueAt(env, uriMap->maps, i);
+
+        if (MATCH_TYPE_SUFFIX == uriEnv->match_type) {
+            char *suffix_start;
+            for (suffix_start = strstr(uri, uriEnv->suffix);
+                 suffix_start;
+                 suffix_start = strstr(suffix_start + 1, uriEnv->suffix)) {
+
+                if ('.' != *(suffix_start - 1)) {
+                    continue;
+                }
+                else {
+                    char *after_suffix = suffix_start +
+                        strlen(uriEnv->suffix);
+
+                    if ((('.' == *after_suffix) ||
+                         ('/' == *after_suffix) ||
+                         (' ' == *after_suffix)) &&
+                        (0 == strncmp(uriEnv->prefix, uri,
+                                      uriEnv->prefix_len))) {
+                        /* 
+                         * Security violation !!!
+                         * this is a fraud.
+                         */
+                        return JK_ERR;
+                    }
+                }
+            }
+        }
+    }
+
+    return JK_OK;
+}
+
+/* Match = 0, NoMatch = 1, Abort = -1
+ * Based loosely on sections of wildmat.c by Rich Salz
+ */
+int jk2_strcmp_match(const char *str, const char *exp, int icase)
+{
+    int x, y;
+
+    for (x = 0, y = 0; exp[y]; ++y, ++x) {
+        if (!str[x] && exp[y] != '*')
+            return -1;
+        if (exp[y] == '*') {
+            while (exp[++y] == '*');
+            if (!exp[y])
+                return 0;
+            while (str[x]) {
+                int ret;
+                if ((ret = jk2_strcmp_match(&str[x++], &exp[y], icase)) != 1)
+                    return ret;
+            }
+            return -1;
+        }
+        else if (exp[y] != '?') {
+            if (icase && tolower(str[x]) != tolower(exp[y]))
+                return 1;
+            else if (!icase && str[x] != exp[y])
+                return 1;
+        }
+    }
+    return (str[x] != '\0');
+}
+
+
+/** Add a uri mapping. Called during uri: initialization. Will just copy the
+    uri in the table ( XXX use a map keyed on name ). In init() we process this
+    and set the right structures.
+ */
+static int jk2_uriMap_addUriEnv(jk_env_t *env, jk_uriMap_t *uriMap,
+                                jk_uriEnv_t *uriEnv)
+{
+    uriMap->maps->put(env, uriMap->maps, uriEnv->name, uriEnv, NULL);
+    if (uriMap->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "uriMap.addUriEnv() %s %s %s\n", uriEnv->name,
+                      uriEnv->virtual, uriEnv->uri);
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_uriMap_setProperty(jk_env_t *env, jk_bean_t *mbean,
+                                            char *name, void *valueP)
+{
+    return JK_OK;
+}
+
+static jk_uriEnv_t *jk2_uriMap_prefixMap(jk_env_t *env, jk_uriMap_t *uriMap,
+                                         jk_map_t *mapTable, const char *uri,
+                                         int uriLen)
+{
+    int best_match = 0;
+    jk_uriEnv_t *match = NULL;
+    int i;
+
+    int sz = mapTable->size(env, mapTable);
+    for (i = 0; i < sz; i++) {
+        jk_uriEnv_t *uwr = mapTable->valueAt(env, mapTable, i);
+        /* XXX: the maps are already sorted by length. Should we skip that ??? */
+        if (uriLen < uwr->prefix_len)
+            continue;
+        if (strncmp(uri, uwr->prefix, uwr->prefix_len) == 0) {
+            if (uwr->prefix_len > best_match) {
+                best_match = uwr->prefix_len;
+                match = uwr;
+            }
+        }
+    }
+    return match;
+}
+
+static jk_uriEnv_t *jk2_uriMap_contextMap(jk_env_t *env, jk_uriMap_t *uriMap,
+                                          jk_map_t *mapTable, const char *uri,
+                                          int uriLen)
+{
+    int i;
+    int sz = mapTable->size(env, mapTable);
+
+    for (i = 0; i < sz; i++) {
+        jk_uriEnv_t *uwr = mapTable->valueAt(env, mapTable, i);
+        if (uriLen != uwr->prefix_len - 1)
+            continue;
+        if (strncmp(uri, uwr->prefix, uriLen) == 0) {
+            return uwr;
+        }
+    }
+    return NULL;
+}
+
+static jk_uriEnv_t *jk2_uriMap_exactMap(jk_env_t *env, jk_uriMap_t *uriMap,
+                                        jk_map_t *mapTable, const char *uri,
+                                        int uriLen)
+{
+    int i;
+    int sz = mapTable->size(env, mapTable);
+    jk_uriEnv_t *match = NULL;
+
+    for (i = 0; i < sz; i++) {
+        jk_uriEnv_t *uwr = mapTable->valueAt(env, mapTable, i);
+
+        if (uriLen != uwr->prefix_len)
+            continue;
+        if (strncmp(uri, uwr->prefix, uriLen) == 0) {
+            return uwr;
+        }
+    }
+    return NULL;
+}
+
+static jk_uriEnv_t *jk2_uriMap_suffixMap(jk_env_t *env, jk_uriMap_t *uriMap,
+                                         jk_map_t *mapTable,
+                                         const char *suffix, int suffixLen)
+{
+    int i;
+    int sz = mapTable->size(env, mapTable);
+
+    for (i = 0; i < sz; i++) {
+        jk_uriEnv_t *uwr = mapTable->valueAt(env, mapTable, i);
+
+        if (!jk2_strcmp_match(suffix, uwr->suffix, jk2_uri_icase))
+            return uwr;
+    }
+    return NULL;
+}
+
+#define MAX_HOST_LENGTH			1024
+
+/* Find the vhost */
+static jk_uriEnv_t *jk2_uriMap_hostMap(jk_env_t *env, jk_uriMap_t *uriMap,
+                                       const char *vhost, int port)
+{
+    int i, j, n;
+    char *name;
+    char hostname[MAX_HOST_LENGTH] = { 0 };
+    char portSuffix[32];
+    int vhostLen;
+
+    if (port) {
+        if (vhost) {
+            vhostLen = strlen(vhost);
+            if (strchr(vhost, ':')) {
+                strncpy(hostname, vhost, MAX_HOST_LENGTH);
+            }
+            else {
+                strncpy(hostname, vhost, MAX_HOST_LENGTH);
+                if (strlen(vhost) < MAX_HOST_LENGTH - 1) {
+                    sprintf(portSuffix, ":%d", port);
+                    strncat(hostname + vhostLen, portSuffix,
+                            MAX_HOST_LENGTH - vhostLen);
+                }
+            }
+        }
+        else
+            sprintf(hostname, "*:%d", port);
+    }
+    else if (vhost)
+        strncpy(hostname, vhost, MAX_HOST_LENGTH);
+    else                        /* Return default host if vhost and port wasn't suplied */
+        return uriMap->vhosts->get(env, uriMap->vhosts, "*");
+
+    hostname[MAX_HOST_LENGTH - 1] = 0;
+
+    n = uriMap->vhosts->size(env, uriMap->vhosts);
+    /* Check the exact hostname:port first */
+    for (i = 0; i < n; i++) {
+        jk_uriEnv_t *uriEnv = uriMap->vhosts->valueAt(env, uriMap->vhosts, i);
+        name = uriMap->vhosts->nameAt(env, uriMap->vhosts, i);
+        /* Host name is not case sensitive */
+        if (strcasecmp(name, hostname) == 0 && port == uriEnv->port)
+            return uriEnv;
+    }
+
+    if (vhost) {
+        /* Check the hostname */
+        for (i = 0; i < n; i++) {
+            jk_uriEnv_t *uriEnv =
+                uriMap->vhosts->valueAt(env, uriMap->vhosts, i);
+            name = uriMap->vhosts->nameAt(env, uriMap->vhosts, i);
+            /* Host name is not case sensitive */
+            if (strcasecmp(name, vhost) == 0)
+                return uriEnv;
+        }
+        /* Then for each vhost, check the aliases */
+        for (i = 0; i < n; i++) {
+            jk_uriEnv_t *uriEnv =
+                uriMap->vhosts->valueAt(env, uriMap->vhosts, i);
+            if (uriEnv->aliases) {
+                int m = uriEnv->aliases->size(env, uriEnv->aliases);
+                for (j = 0; j < m; j++) {
+                    name = uriEnv->aliases->nameAt(env, uriEnv->aliases, j);
+                    if (strcasecmp(name, hostname) == 0)
+                        return uriEnv;
+                }
+            }
+        }
+    }
+    /* Finally, check aginst *:port hostname */
+    if (port) {
+        for (i = 0; i < n; i++) {
+            jk_uriEnv_t *uriEnv =
+                uriMap->vhosts->valueAt(env, uriMap->vhosts, i);
+            name = uriMap->vhosts->nameAt(env, uriMap->vhosts, i);
+            if ((strncmp(name, "*:", 2) == 0) && uriEnv->port == port) {
+                return uriEnv;
+            }
+        }
+    }
+    /* Return default host if none found */
+    return uriMap->vhosts->get(env, uriMap->vhosts, "*");
+}
+
+#if defined(HAS_PCRE) || defined(HAS_AP_PCRE)
+static jk_uriEnv_t *jk2_uriMap_regexpMap(jk_env_t *env, jk_uriMap_t *uriMap,
+                                         jk_map_t *mapTable, const char *uri)
+{
+    int i;
+    int sz = mapTable->size(env, mapTable);
+
+    for (i = 0; i < sz; i++) {
+        jk_uriEnv_t *uwr = mapTable->valueAt(env, mapTable, i);
+
+        if (uwr->regexp) {
+            regex_t *r = (regex_t *) uwr->regexp;
+            regmatch_t regm[10];
+            if (!REGEXEC(r, uri, r->re_nsub + 1, regm, 0)) {
+                return uwr;
+            }
+        }
+    }
+    return NULL;
+
+}
+#else
+static jk_uriEnv_t *jk2_uriMap_regexpMap(jk_env_t *env, jk_uriMap_t *uriMap,
+                                         jk_map_t *mapTable, const char *uri)
+{
+    return NULL;
+}
+#endif
+
+static void jk2_uriMap_createHosts(jk_env_t *env, jk_uriMap_t *uriMap)
+{
+    int i;
+
+    for (i = 0; i < uriMap->maps->size(env, uriMap->maps); i++) {
+        jk_uriEnv_t *uriEnv = uriMap->maps->valueAt(env, uriMap->maps, i);
+        if (uriEnv == NULL)
+            continue;
+        if (uriEnv->virtual != NULL && strlen(uriEnv->virtual)) {
+            if (uriEnv->match_type == MATCH_TYPE_HOST) {
+                jk2_map_default_create(env, &uriEnv->webapps, uriMap->pool);
+                uriMap->vhosts->put(env, uriMap->vhosts,
+                                    uriEnv->virtual, uriEnv, NULL);
+            }
+            else {              /* Create the missing vhosts */
+                if (!uriMap->vhosts->get(env, uriMap->vhosts,
+                                         uriEnv->virtual)) {
+                    /* Actually create the bean */
+                    jk_uriEnv_t *hostEnv = env->getByName2(env, "uri",
+                                                           uriEnv->virtual);
+                    if (hostEnv == NULL) {
+                        env->createBean2(env, uriMap->mbean->pool, "uri",
+                                         uriEnv->virtual);
+                        hostEnv = env->getByName2(env, "uri",
+                                                  uriEnv->virtual);
+                        if (!hostEnv) {
+                            /* XXX this is a error. */
+                            continue;
+                        }
+                        if (uriMap->mbean->debug > 0)
+                            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                          "uriMap.init() Create missing host %s\n",
+                                          uriEnv->virtual);
+                    }
+                    jk2_map_default_create(env, &hostEnv->webapps,
+                                           uriMap->pool);
+                    uriMap->vhosts->put(env, uriMap->vhosts, uriEnv->virtual,
+                                        hostEnv, NULL);
+
+                    env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                  "uriMap.init() Fixing Host %s\n",
+                                  uriEnv->virtual);
+                }
+            }
+        }
+    }
+
+    /** Make sure each vhost has a default context
+     */
+    for (i = 0; i < uriMap->vhosts->size(env, uriMap->vhosts); i++) {
+        jk_uriEnv_t *hostEnv =
+            uriMap->vhosts->valueAt(env, uriMap->vhosts, i);
+        jk_uriEnv_t *rootCtx;
+        char *uriPath;
+
+        if (hostEnv->virtual != NULL) {
+            uriPath = env->tmpPool->pstrcat(env, env->tmpPool,
+                                            hostEnv->virtual, "/", NULL);
+        }
+        else {
+            uriPath = "/";
+        }
+
+        rootCtx = env->getByName2(env, "uri", uriPath);
+        if (rootCtx == NULL) {
+            env->createBean2(env, uriMap->mbean->pool, "uri", uriPath);
+            rootCtx = env->getByName2(env, "uri", uriPath);
+            if (uriMap->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "uriMap.init() Create default context %s\n",
+                              uriPath);
+            rootCtx->mbean->setAttribute(env, rootCtx->mbean, "context", "/");
+        }
+    }
+}
+
+static void jk2_uriMap_createWebapps(jk_env_t *env, jk_uriMap_t *uriMap)
+{
+    int i;
+
+    /* Init all contexts */
+    /* For each context, init the local uri maps */
+    for (i = 0; i < uriMap->maps->size(env, uriMap->maps); i++) {
+        jk_uriEnv_t *uriEnv = uriMap->maps->valueAt(env, uriMap->maps, i);
+        char *uri;
+        char *context;
+        if (uriEnv == NULL) {
+            env->l->jkLog(env, env->l, JK_LOG_INFO, "uriMap.init() NPE\n");
+        }
+        uri = uriEnv->uri;
+        context = uriEnv->contextPath;
+
+        if (uri != NULL && context != NULL && strcmp(uri, context) == 0) {
+            char *vhost = uriEnv->virtual;
+            int port = uriEnv->port;
+            jk_uriEnv_t *hostEnv =
+                jk2_uriMap_hostMap(env, uriMap, vhost, port);
+            if (!hostEnv)
+                continue;
+            if (uriMap->mbean->debug > 5)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "uriMap.init() loaded context %s %s %#lx %#lx %#lx\n",
+                              uriEnv->virtual, context, hostEnv,
+                              hostEnv->webapps, uriMap->pool);
+            uriEnv->match_type = MATCH_TYPE_CONTEXT;
+
+            uriEnv->prefix = context;
+            uriEnv->prefix_len = strlen(context);
+            hostEnv->webapps->put(env, hostEnv->webapps, context, uriEnv,
+                                  NULL);
+            jk2_map_default_create(env, &uriEnv->exactMatch, uriMap->pool);
+            jk2_map_default_create(env, &uriEnv->prefixMatch, uriMap->pool);
+            jk2_map_default_create(env, &uriEnv->suffixMatch, uriMap->pool);
+            jk2_map_default_create(env, &uriEnv->regexpMatch, uriMap->pool);
+        }
+    }
+
+    /* Now, fix the webapps finding context from each uri 
+     * and create one if not found.
+     */
+    for (i = 0; i < uriMap->maps->size(env, uriMap->maps); i++) {
+        jk_uriEnv_t *uriEnv = uriMap->maps->valueAt(env, uriMap->maps, i);
+        char *vhost = uriEnv->virtual;
+        int port = uriEnv->port;
+        char *context = uriEnv->contextPath;
+        jk_uriEnv_t *hostEnv = jk2_uriMap_hostMap(env, uriMap, vhost, port);
+        jk_uriEnv_t *ctxEnv;
+
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "uriMap: fix uri %s context %s host %s\n",
+                      uriEnv->uri == NULL ? "null" : uriEnv->uri,
+                      uriEnv->contextPath ==
+                      NULL ? "null" : uriEnv->contextPath, hostEnv->virtual);
+
+        if (context == NULL) {
+            if (uriMap->mbean->debug > 5)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "uriMap: no context %s\n", uriEnv->uri);
+            continue;
+        }
+
+        ctxEnv = jk2_uriMap_exactMap(env, uriMap, hostEnv->webapps, context,
+                                     strlen(context));
+        /* if not alredy created, create it */
+        if (ctxEnv == NULL) {
+            jk_bean_t *mbean;
+            char *ctxname;
+
+            ctxname = uriEnv->pool->pstrcat(env, uriEnv->pool, vhost,
+                                            context, NULL);
+
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "uriMap: creating context %s\n", ctxname);
+            mbean = env->getBean2(env, "uri", ctxname);
+            if (mbean == NULL)
+                mbean = env->createBean2(env, uriMap->pool, "uri", ctxname);
+            if (mbean == NULL || mbean->object == NULL) {
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "uriMap: can't create context object %s\n",
+                              ctxname);
+                continue;
+            }
+            ctxEnv = mbean->object;
+            ctxEnv->match_type = MATCH_TYPE_CONTEXT;
+            ctxEnv->prefix = context;
+            ctxEnv->prefix_len = strlen(context);
+            ctxEnv->contextPath = context;
+            ctxEnv->ctxt_len = strlen(context);
+            hostEnv->webapps->put(env, hostEnv->webapps, context, ctxEnv,
+                                  NULL);
+            jk2_map_default_create(env, &ctxEnv->exactMatch, uriMap->pool);
+            jk2_map_default_create(env, &ctxEnv->prefixMatch, uriMap->pool);
+            jk2_map_default_create(env, &ctxEnv->suffixMatch, uriMap->pool);
+            jk2_map_default_create(env, &ctxEnv->regexpMatch, uriMap->pool);
+        }
+    }
+
+}
+
+static int jk2_uriMap_createMappings(jk_env_t *env, jk_uriMap_t *uriMap)
+{
+    int i;
+
+    if (uriMap->mbean->debug > 5)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "uriMap.init() processing mappings\n");
+
+    /* XXX We should also sort prefix mappings and maybe use binary search - but
+       it won't have too bigger benefits, the number of mappings per ctx is typically 
+       small
+     */
+    for (i = 0; i < uriMap->maps->size(env, uriMap->maps); i++) {
+        jk_uriEnv_t *uriEnv = uriMap->maps->valueAt(env, uriMap->maps, i);
+
+        char *vhost = uriEnv->virtual;
+        int port = uriEnv->port;
+        jk_uriEnv_t *hostEnv = jk2_uriMap_hostMap(env, uriMap, vhost, port);
+
+        char *uri = uriEnv->uri;
+        jk_uriEnv_t *ctxEnv = NULL;
+
+        if (hostEnv == NULL)
+            continue;
+        if (uri == NULL)
+            continue;
+        uriEnv->uriMap = uriMap;
+        uriEnv->init(env, uriEnv);
+
+        if (uri == NULL)
+            continue;
+
+        /* If the context was specified try to find the exact one */
+        if (uriEnv->contextPath != NULL)
+            ctxEnv = jk2_uriMap_exactMap(env, uriMap, hostEnv->webapps,
+                                         uriEnv->contextPath,
+                                         uriEnv->ctxt_len);
+        else if (uriEnv->match_type == MATCH_TYPE_REGEXP) {
+            ctxEnv = hostEnv->webapps->get(env, hostEnv->webapps, "/");
+        }
+        /* Next find by uri prefix */
+        if (ctxEnv == NULL)
+            ctxEnv = jk2_uriMap_prefixMap(env, uriMap, hostEnv->webapps, uri,
+                                          strlen(uri));
+
+        if (ctxEnv == NULL) {
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "uriMap.init() no context for %s\n", uri);
+            /* Normal case if Location is used */
+            continue;
+        }
+
+        /* Correct the context path if needed */
+        uriEnv->contextPath = ctxEnv->prefix;
+        uriEnv->ctxt_len = ctxEnv->prefix_len;
+
+        if (uriMap->mbean->debug > 5)
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "uriMap.init() adding context %s:%s for %s\n",
+                          vhost, ctxEnv->prefix, uri);
+
+        switch (uriEnv->match_type) {
+        case MATCH_TYPE_EXACT:
+            ctxEnv->exactMatch->add(env, ctxEnv->exactMatch, uri, uriEnv);
+            ctxEnv->exactMatch->sort(env, ctxEnv->exactMatch);
+            break;
+        case MATCH_TYPE_SUFFIX:
+            ctxEnv->suffixMatch->add(env, ctxEnv->suffixMatch, uri, uriEnv);
+            ctxEnv->suffixMatch->sort(env, ctxEnv->suffixMatch);
+            break;
+        case MATCH_TYPE_PREFIX:
+            ctxEnv->prefixMatch->add(env, ctxEnv->prefixMatch, uri, uriEnv);
+            ctxEnv->suffixMatch->sort(env, ctxEnv->prefixMatch);
+            break;
+        case MATCH_TYPE_REGEXP:
+            if (uriEnv->regexp)
+                ctxEnv->regexpMatch->add(env, ctxEnv->regexpMatch, uri,
+                                         uriEnv);
+            break;
+        }
+    }
+    return JK_OK;
+}
+
+static jk_uriEnv_t *jk2_uriMap_duplicateUri(jk_env_t *env,
+                                            jk_uriMap_t *uriMap,
+                                            jk_uriEnv_t *uriEnv,
+                                            jk_uriEnv_t *mapEnv)
+{
+    char *uriname;
+    jk_uriEnv_t *newEnv;
+    jk_bean_t *mbean;
+
+    uriname = uriEnv->pool->pstrcat(env, uriEnv->pool, uriEnv->name,
+                                    mapEnv->uri, NULL);
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "uriMap: creating duplicate of  uri %s\n", uriname);
+    mbean = env->getBean2(env, "uri", uriname);
+    if (mbean == NULL)
+        mbean = env->createBean2(env, uriMap->pool, "uri", uriname);
+    if (mbean == NULL || mbean->object == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "uriMap: can't create uri object %s\n", uriname);
+        return NULL;
+    }
+    newEnv = mbean->object;
+    newEnv->match_type = mapEnv->match_type;
+    newEnv->prefix = mapEnv->prefix;
+    newEnv->prefix_len = mapEnv->prefix_len;
+    newEnv->contextPath = mapEnv->contextPath;
+    newEnv->ctxt_len = mapEnv->ctxt_len;
+    newEnv->worker = mapEnv->worker;
+    newEnv->workerName = mapEnv->workerName;
+    newEnv->workerEnv = mapEnv->workerEnv;
+    newEnv->regexp = mapEnv->regexp;
+
+    return newEnv;
+}
+
+static jk_uriEnv_t *jk2_uriMap_duplicateContext(jk_env_t *env,
+                                                jk_uriMap_t *uriMap,
+                                                jk_uriEnv_t *uriEnv,
+                                                jk_uriEnv_t *mapEnv)
+{
+    char *uriname;
+    jk_uriEnv_t *newEnv;
+    jk_bean_t *mbean;
+
+    uriname = uriEnv->pool->pstrcat(env, uriEnv->pool, uriEnv->name,
+                                    mapEnv->contextPath, NULL);
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "uriMap: creating duplicate of context %s\n", uriname);
+    mbean = env->getBean2(env, "uri", uriname);
+    if (mbean == NULL)
+        mbean = env->createBean2(env, uriMap->pool, "uri", uriname);
+    if (mbean == NULL || mbean->object == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "uriMap: can't create uri object %s\n", uriname);
+        return NULL;
+    }
+    newEnv = mbean->object;
+    newEnv->match_type = mapEnv->match_type;
+    newEnv->prefix = mapEnv->prefix;
+    newEnv->prefix_len = mapEnv->prefix_len;
+    newEnv->contextPath = mapEnv->contextPath;
+    newEnv->ctxt_len = mapEnv->ctxt_len;
+    newEnv->worker = mapEnv->worker;
+    newEnv->workerName = mapEnv->workerName;
+    newEnv->workerEnv = mapEnv->workerEnv;
+
+    jk2_map_default_create(env, &newEnv->exactMatch, uriMap->pool);
+    jk2_map_default_create(env, &newEnv->prefixMatch, uriMap->pool);
+    jk2_map_default_create(env, &newEnv->suffixMatch, uriMap->pool);
+    jk2_map_default_create(env, &newEnv->regexpMatch, uriMap->pool);
+
+    return newEnv;
+}
+
+static int jk2_uriMap_createGlobals(jk_env_t *env, jk_uriMap_t *uriMap)
+{
+    int i, n;
+    jk_uriEnv_t *globalEnv;
+
+    if (uriMap->mbean->debug > 5)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "uriMap.init() creating global mappings\n");
+    globalEnv = uriMap->vhosts->get(env, uriMap->vhosts, "*");
+
+    n = uriMap->vhosts->size(env, uriMap->vhosts);
+    for (i = 0; i < n; i++) {
+        jk_uriEnv_t *uriEnv = uriMap->vhosts->valueAt(env, uriMap->vhosts, i);
+        /* Duplicate globals for each vhost that has inheritGlobals set */
+        if (uriEnv != globalEnv && uriEnv->inherit_globals) {
+            int j, m;
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "uriMap.init() global for %s\n", uriEnv->name);
+            m = globalEnv->webapps->size(env, globalEnv->webapps);
+            for (j = 0; j < m; j++) {
+                jk_uriEnv_t *ctxEnv;
+                jk_uriEnv_t *appEnv;
+                int k, l;
+
+                ctxEnv =
+                    globalEnv->webapps->valueAt(env, globalEnv->webapps, j);
+                appEnv =
+                    uriEnv->webapps->get(env, uriEnv->webapps,
+                                         ctxEnv->contextPath);
+
+                /* Create the webapp if it doesn't exists on the selected vhost */
+                if (!appEnv) {
+
+                    appEnv =
+                        jk2_uriMap_duplicateContext(env, uriMap, uriEnv,
+                                                    ctxEnv);
+                    uriEnv->webapps->put(env, uriEnv->webapps,
+                                         appEnv->contextPath, appEnv, NULL);
+                    env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                  "uriMap.init() creating global webapp %s for %s\n",
+                                  appEnv->contextPath, uriEnv->name);
+                }
+                /* Fix the exact matches */
+                l = ctxEnv->exactMatch->size(env, ctxEnv->exactMatch);
+                for (k = 0; k < l; k++) {
+                    jk_uriEnv_t *mapEnv;
+                    jk_uriEnv_t *newEnv;
+
+                    mapEnv =
+                        ctxEnv->exactMatch->valueAt(env, ctxEnv->exactMatch,
+                                                    k);
+                    newEnv =
+                        appEnv->exactMatch->get(env, appEnv->exactMatch,
+                                                mapEnv->uri);
+                    /* Create the new exact match uri */
+                    if (!newEnv) {
+                        newEnv =
+                            jk2_uriMap_duplicateUri(env, uriMap, uriEnv,
+                                                    mapEnv);
+                        appEnv->exactMatch->put(env, appEnv->exactMatch,
+                                                newEnv->name, newEnv, NULL);
+
+                    }
+                }
+                /* Fix the prefix matches */
+                l = ctxEnv->prefixMatch->size(env, ctxEnv->prefixMatch);
+                for (k = 0; k < l; k++) {
+                    jk_uriEnv_t *mapEnv;
+                    jk_uriEnv_t *newEnv;
+
+                    mapEnv =
+                        ctxEnv->prefixMatch->valueAt(env, ctxEnv->prefixMatch,
+                                                     k);
+                    newEnv =
+                        appEnv->prefixMatch->get(env, appEnv->prefixMatch,
+                                                 mapEnv->uri);
+                    /* Create the new prefix match uri */
+                    if (!newEnv) {
+                        newEnv =
+                            jk2_uriMap_duplicateUri(env, uriMap, uriEnv,
+                                                    mapEnv);
+                        appEnv->prefixMatch->put(env, appEnv->prefixMatch,
+                                                 newEnv->name, newEnv, NULL);
+                    }
+                }
+                /* Fix the suffix matches */
+                l = ctxEnv->suffixMatch->size(env, ctxEnv->suffixMatch);
+                for (k = 0; k < l; k++) {
+                    jk_uriEnv_t *mapEnv;
+                    jk_uriEnv_t *newEnv;
+
+                    mapEnv =
+                        ctxEnv->suffixMatch->valueAt(env, ctxEnv->suffixMatch,
+                                                     k);
+                    newEnv =
+                        appEnv->suffixMatch->get(env, appEnv->suffixMatch,
+                                                 mapEnv->uri);
+                    /* Create the new suffix match uri */
+                    if (!newEnv) {
+                        newEnv =
+                            jk2_uriMap_duplicateUri(env, uriMap, uriEnv,
+                                                    mapEnv);
+                        appEnv->suffixMatch->put(env, appEnv->prefixMatch,
+                                                 newEnv->name, newEnv, NULL);
+                    }
+                }
+                uriEnv->webapps->put(env, uriEnv->webapps,
+                                     appEnv->contextPath, appEnv, NULL);
+            }
+        }
+    }
+    return JK_OK;
+}
+
+static int jk2_uriMap_init(jk_env_t *env, jk_uriMap_t *uriMap)
+{
+    int rc = JK_OK;
+    jk_workerEnv_t *workerEnv = uriMap->workerEnv;
+    jk_bean_t *mbean = env->getBean2(env, "uri", "*");
+
+    /* create the default server */
+    if (mbean == NULL) {
+        mbean = env->createBean2(env, workerEnv->pool, "uri", "*");
+        if (mbean == NULL || mbean->object == NULL) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "uriMap.factory() Fail to create default host\n");
+            return JK_ERR;
+        }
+    }
+
+    /* Create virtual hosts and initialize them */
+    jk2_uriMap_createHosts(env, uriMap);
+
+    /* Create webapps and initialize them */
+    jk2_uriMap_createWebapps(env, uriMap);
+
+    /* All other mappings are added in the right context leaf. */
+    if ((rc = jk2_uriMap_createMappings(env, uriMap)) != JK_OK)
+        return rc;
+
+    /* Fix the global mappings for virtual hosts */
+    rc = jk2_uriMap_createGlobals(env, uriMap);
+
+    return rc;
+}
+
+static void jk2_uriMap_destroy(jk_env_t *env, jk_uriMap_t *uriMap)
+{
+
+    if (uriMap->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG, "uriMap.destroy()\n");
+
+    /* this can't be null ( or a NPE would have been generated */
+    uriMap->pool->close(env, uriMap->pool);
+}
+
+
+/* returns the index of the last occurrence of the 'ch' character
+   if ch=='\0' returns the length of the string str  */
+static INLINE int jk2_last_index_of(const char *str, char ch)
+{
+    const char *str_minus_one = str - 1;
+    const char *s = str + strlen(str);
+    while (s != str_minus_one && ch != *s) {
+        --s;
+    }
+    return (s - str);
+}
+
+/* find the suffix - only once. We also make sure
+   we check only the last component, as required by
+   servlet spec
+*/
+static INLINE const char *jk2_findExtension(jk_env_t *env, const char *uri)
+{
+    int suffix_start;
+    const char *suffix;
+
+    for (suffix_start = strlen(uri) - 1; suffix_start > 0; suffix_start--) {
+        if ('.' == uri[suffix_start] || '/' == uri[suffix_start])
+            break;
+    }
+    if ('.' != uri[suffix_start]) {
+        suffix_start = -1;
+        suffix = NULL;
+    }
+    else {
+        suffix_start++;
+        suffix = uri + suffix_start;
+    }
+    return suffix;
+}
+
+#define SAFE_URI_SIZE 8192
+
+static jk_uriEnv_t *jk2_uriMap_getHostCache(jk_env_t *env,
+                                            jk_uriMap_t *uriMap,
+                                            const char *vhost, int port)
+{
+    char key[MAX_HOST_LENGTH];
+    char portSuffix[32];
+    int vhostLen;
+
+    if (!vhost && !port)
+        return uriMap->vhosts->get(env, uriMap->vhosts, "*");
+    if (!vhost)
+        vhost = "*";
+    vhostLen = strlen(vhost);
+    strncpy(key, vhost, MAX_HOST_LENGTH);
+    if (vhostLen < MAX_HOST_LENGTH - 1) {
+        sprintf(portSuffix, ":%d", port);
+        strncat(key + vhostLen, portSuffix, MAX_HOST_LENGTH);
+    }
+    key[MAX_HOST_LENGTH - 1] = 0;
+    return uriMap->vhcache->get(env, uriMap->vhcache, key);
+}
+
+static void jk2_uriMap_addHostCache(jk_env_t *env, jk_uriMap_t *uriMap,
+                                    const char *vhost, int port,
+                                    jk_uriEnv_t *hostEnv)
+{
+    char *key;
+
+    if (!vhost)
+        vhost = "*";
+    key = uriMap->pool->calloc(env, uriMap->pool, strlen(vhost) + 8);
+
+    sprintf(key, "%s:%d", vhost, port);
+    uriMap->vhcache->add(env, uriMap->vhcache, key, hostEnv);
+}
+
+static jk_uriEnv_t *jk2_uriMap_mapUri(jk_env_t *env, jk_uriMap_t *uriMap,
+                                      const char *vhost, int port,
+                                      const char *uri)
+{
+    char *url_rewrite = NULL;
+    int uriLen;
+    jk_uriEnv_t *hostEnv;
+    jk_uriEnv_t *ctxEnv;
+    jk_uriEnv_t *match;
+
+    /* Ugly hack to avoid using non-thread safe code.
+       Modify the uri in place for uri session encoding, then
+       restore it to the original. That works since the processing
+       happens in a single thred. A better solution is to allocate
+       the jk_ws_service and it's pool and pass it as param */
+    char origChar = '\0';
+
+    /* XXX - need to make sure prefix match take precedence over
+       extension match ( now it doesn't )
+     */
+
+    if (uriMap == NULL || uri == NULL)
+        return NULL;
+
+    if (uriMap->mbean->debug > 1)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "uriMap.mapUri() hostname %s port %d uri %s\n", vhost,
+                      port, uri);
+    if (uri[0] != '/') {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "uriMap.mapUri() uri must start with /\n");
+        return NULL;
+    }
+
+    hostEnv = jk2_uriMap_getHostCache(env, uriMap, vhost, port);
+    if (!hostEnv) {
+        hostEnv = jk2_uriMap_hostMap(env, uriMap, vhost, port);
+        if (!hostEnv) {
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "uriMap.mapUri() cannot find host %s/\n", vhost);
+            return NULL;
+        }
+        if (uriMap->mbean->debug > 1)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "uriMap.mapUri() caching host %s\n",
+                          hostEnv->virtual);
+        jk2_uriMap_addHostCache(env, uriMap, vhost, port, hostEnv);
+    }
+    else if (uriMap->mbean->debug > 1)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "uriMap.mapUri() found host %s\n", hostEnv->virtual);
+
+    url_rewrite = strstr(uri, JK_PATH_SESSION_IDENTIFIER);
+
+    if (url_rewrite) {
+        origChar = *url_rewrite;
+        *url_rewrite = '\0';
+        if (uriMap->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "uriMap.mapUri() rewrote uri %s \n", uri);
+    }
+
+    uriLen = strlen(uri);
+
+    /* Map the context */
+    ctxEnv = jk2_uriMap_prefixMap(env, uriMap, hostEnv->webapps, uri, uriLen);
+
+    if (ctxEnv == NULL) {
+        if (url_rewrite)
+            *url_rewrite = origChar;
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "uriMap.mapUri() no context %s\n", uri);
+        return NULL;
+    }
+
+    if (uriMap->mbean->debug > 1)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "uriMap.mapUri() found ctx %s\n", ctxEnv->uri);
+
+    match = jk2_uriMap_regexpMap(env, uriMap, ctxEnv->regexpMatch, uri);
+    if (match != NULL) {
+        /* restore */
+        if (url_rewrite)
+            *url_rewrite = origChar;
+        if (uriMap->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "uriMap.mapUri() regexp match %s %s\n",
+                          uri, match->workerName);
+        return match;
+    }
+
+
+    /* As per Servlet spec, do exact match first */
+    match = jk2_uriMap_exactMap(env, uriMap, ctxEnv->exactMatch, uri, uriLen);
+    if (match != NULL) {
+        /* restore */
+        if (url_rewrite)
+            *url_rewrite = origChar;
+        if (uriMap->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "uriMap.mapUri() exact match %s %s\n",
+                          uri, match->workerName);
+        return match;
+    }
+
+    /* Then prefix match */
+    match =
+        jk2_uriMap_prefixMap(env, uriMap, ctxEnv->prefixMatch, uri, uriLen);
+    if (match != NULL) {
+        char c = uri[match->prefix_len - 1];
+        /* XXX Filter prefix matches to allow only exact 
+           matches with an optional path_info or query string at end.
+           Fixes Bugzilla#12141, needs review..
+         */
+        if ((uriLen > match->prefix_len && (c == '/' || c == '?')) ||
+            uriLen == match->prefix_len) {
+            /* restore */
+            if (url_rewrite)
+                *url_rewrite = origChar;
+            if (uriMap->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "uriMap.mapUri() prefix match %s %s\n",
+                              uri, match->workerName);
+            return match;
+        }
+    }
+
+    /* Try to find exact match of /uri and prefix /uri/star (*) */
+    match =
+        jk2_uriMap_contextMap(env, uriMap, ctxEnv->prefixMatch, uri, uriLen);
+    if (match != NULL) {
+        /* restore */
+        if (url_rewrite)
+            *url_rewrite = origChar;
+        if (uriMap->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "uriMap.mapUri() context match %s %s\n",
+                          uri, match->workerName);
+        return match;
+    }
+
+    /* And do a wild char match at the end */
+    match = jk2_uriMap_suffixMap(env, uriMap, ctxEnv->suffixMatch, uri, 0);
+    if (match != NULL) {
+        /* restore */
+        if (url_rewrite)
+            *url_rewrite = origChar;
+        if (uriMap->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "uriMap.mapUri() extension match %s %s\n",
+                          uri, match->workerName);
+        return match;
+    }
+
+    /* restore */
+    if (url_rewrite)
+        *url_rewrite = origChar;
+
+    if (uriMap->mbean->debug > 1)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "uriMap.mapUri() no match found\n");
+
+    return NULL;
+}
+
+int JK_METHOD jk2_uriMap_factory(jk_env_t *env, jk_pool_t *pool,
+                                 jk_bean_t *result, const char *type,
+                                 const char *name)
+{
+    jk_uriMap_t *uriMap;
+
+    uriMap = (jk_uriMap_t *)pool->calloc(env, pool, sizeof(jk_uriMap_t));
+    if (!uriMap) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "uriMap.factory() OutOfMemoryError\n");
+        return JK_ERR;
+    }
+
+    uriMap->pool = pool;
+
+    jk2_map_default_create(env, &uriMap->maps, pool);
+    jk2_map_default_create(env, &uriMap->vhosts, pool);
+    jk2_map_default_create(env, &uriMap->vhcache, pool);
+
+    uriMap->init = jk2_uriMap_init;
+    uriMap->destroy = jk2_uriMap_destroy;
+    uriMap->addUriEnv = jk2_uriMap_addUriEnv;
+    uriMap->checkUri = jk2_uriMap_checkUri;
+    uriMap->mapUri = jk2_uriMap_mapUri;
+
+    result->object = uriMap;
+    result->setAttribute = jk2_uriMap_setProperty;
+    uriMap->mbean = result;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_user.c b/connectors/jk/native2/common/jk_user.c
new file mode 100644
index 0000000..3d2b780
--- /dev/null
+++ b/connectors/jk/native2/common/jk_user.c
@@ -0,0 +1,118 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+
+#if !(defined(WIN32) || defined(NETWARE))
+
+#include <unistd.h>
+#include <pwd.h>
+
+typedef struct jk_user
+{
+    char *user;
+    char *group;
+} jk_user_t;
+
+/* -------------------- User related functions -------------------- */
+/* XXX move it to jk_user.c */
+
+static int JK_METHOD jk2_user_setAttribute(jk_env_t *env, jk_bean_t *mbean,
+                                           char *name, void *valueP)
+{
+    char *value = (char *)valueP;
+    jk_user_t *usr = (jk_user_t *) mbean->object;
+
+    if (strcmp("user", name) == 0) {
+        usr->user = value;
+    }
+    else if (strcmp("group", name) == 0) {
+        usr->group = value;
+    }
+    else {
+        return JK_ERR;
+    }
+    return JK_OK;
+
+}
+
+/** This is ugly, we should use a jk_msg to pass string/int/etc
+ */
+static void *JK_METHOD jk2_user_getAttribute(jk_env_t *env, jk_bean_t *mbean,
+                                             char *name)
+{
+
+    if (strcmp("pid", name) == 0) {
+        char *buf = env->tmpPool->calloc(env, env->tmpPool, 20);
+        sprintf(buf, "%d", getpid());
+        return buf;
+    }
+    return NULL;
+}
+
+static int JK_METHOD jk2_user_init(jk_env_t *env, jk_bean_t *chB)
+{
+    jk_user_t *usr = chB->object;
+    struct passwd *passwd;
+    int uid;
+    int gid;
+    int rc;
+
+    if (usr->user == NULL)
+        return JK_OK;
+
+    passwd = getpwnam(usr->user);
+    if (passwd == NULL) {
+        return JK_ERR;
+    }
+
+    uid = passwd->pw_uid;
+    gid = passwd->pw_gid;
+
+    rc = setuid(uid);
+
+    return rc;
+}
+
+int JK_METHOD jk2_user_factory(jk_env_t *env, jk_pool_t *pool,
+                               jk_bean_t *result,
+                               const char *type, const char *name)
+{
+    result->setAttribute = jk2_user_setAttribute;
+    result->getAttribute = jk2_user_getAttribute;
+    result->object = pool->calloc(env, pool, sizeof(jk_user_t));
+    result->invoke = NULL;
+    result->init = jk2_user_init;
+    result->destroy = NULL;
+
+    return JK_OK;
+}
+
+
+
+#else /* ! HAVE_SIGNALS */
+
+int JK_METHOD jk2_user_factory(jk_env_t *env, jk_pool_t *pool,
+                               jk_bean_t *result,
+                               const char *type, const char *name)
+{
+    result->disabled = JK_TRUE;
+    return JK_FALSE;
+}
+
+#endif
diff --git a/connectors/jk/native2/common/jk_vm_default.c b/connectors/jk/native2/common/jk_vm_default.c
new file mode 100644
index 0000000..a55a990
--- /dev/null
+++ b/connectors/jk/native2/common/jk_vm_default.c
@@ -0,0 +1,660 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * JNI utils. This is now organized as a class with virtual methods.
+ * This will allow a future split based on VM type, and simplification
+ * of the code ( no more #ifdefs, but different objects for different VMs).
+ *
+ * It should also allow to better work around VMs and support more
+ * ( I assume kaffe will need some special tricks, 1.4 will be different
+ *  than 1.1 and 1.2, IBM vms have their own paths, etc ).
+ *
+ * That's future - for now we use the current ugly code (  result
+ *  of few years of workarounds ). The refactoring should be done before adding
+ * anything new.
+ *
+ * @author:  Gal Shachor <shachor@il.ibm.com>
+ * @author: Costin Manolache
+ */
+
+#include "jk_workerEnv.h"
+#include "jk_env.h"
+#include "jk_bean.h"
+
+#ifdef HAVE_JNI
+
+#include "jk_global.h"
+#include "jk_vm.h"
+#include "jk_config.h"
+
+
+#if defined LINUX && defined APACHE2_SIGHACK
+#include <pthread.h>
+#include <signal.h>
+#include <bits/signum.h>
+#endif
+
+#if defined NETWARE && !defined __NOVELL_LIBC__
+#include <nwthread.h>
+#include <nwadv.h>
+#endif
+
+#include <jni.h>
+
+#ifdef APR_HAS_DSO
+#include "apr_dso.h"
+#else
+#error "You will need the APR's dso support for JNI"
+#endif
+
+
+#ifndef JNI_VERSION_1_2
+
+#warning -------------------------------------------------------
+#warning JAVA 1.1 IS NO LONGER SUPPORTED
+#warning -------------------------------------------------------
+
+int JK_METHOD jk2_vm_factory(jk_env_t *env, jk_pool_t *pool,
+                             jk_bean_t *result, char *type, char *name)
+{
+    return JK_ERR;
+}
+
+#else
+
+
+#ifndef JNI_VERSION_1_1
+#define JNI_VERSION_1_1 0x00010001
+#endif
+
+/* probably on an older system that doesn't support RTLD_NOW or RTLD_LAZY.
+ * The below define is a lie since we are really doing RTLD_LAZY since the
+ * system doesn't support RTLD_NOW.
+ */
+#ifndef RTLD_NOW
+#define RTLD_NOW 1
+#endif
+
+#ifndef RTLD_GLOBAL
+#define RTLD_GLOBAL 0
+#endif
+
+#define null_check(e) if ((e) == 0) return JK_FALSE
+
+static int jk2_detect_jvm_version(jk_env_t *env);
+static int jk2_open_jvm2(jk_env_t *env, jk_vm_t * p);
+
+jint(JNICALL * jni_get_default_java_vm_init_args) (void *) = NULL;
+jint(JNICALL * jni_create_java_vm) (JavaVM **, JNIEnv **, void *) = NULL;
+jint(JNICALL * jni_get_created_java_vms) (JavaVM **, int, int *) = NULL;
+
+/* Guessing - try all those to find the right dll
+ */
+static const char *defaultVM_PATH[] = {
+    "${JAVA_HOME}${fs}jre${fs}bin${fs}classic${fs}libjvm.${so}",
+    "${JAVA_HOME}${fs}jre${fs}bin${fs}client${fs}jvm.${so}",
+    "${JAVA_HOME}${fs}jre${fs}lib${fs}${arch}${fs}classic${fs}libjvm.${so}",
+    "${JAVA_HOME}${fs}jre${fs}lib${fs}${arch}${fs}client${fs}libjvm.${so}",
+    "${JAVA_HOME}${fs}jre${fs}bin${fs}classic${fs}jvm.${so}",
+    NULL
+};
+
+#if defined LINUX && defined APACHE2_SIGHACK
+static void jk2_linux_signal_hack()
+{
+    sigset_t newM;
+    sigset_t old;
+
+    sigemptyset(&newM);
+    pthread_sigmask(SIG_SETMASK, &newM, &old);
+
+    sigdelset(&old, SIGUSR1);
+    sigdelset(&old, SIGUSR2);
+    sigdelset(&old, SIGUNUSED);
+    sigdelset(&old, SIGRTMIN);
+    sigdelset(&old, SIGRTMIN + 1);
+    sigdelset(&old, SIGRTMIN + 2);
+    pthread_sigmask(SIG_SETMASK, &old, NULL);
+}
+
+static void jk2_print_signals(sigset_t * sset)
+{
+    int sig;
+    for (sig = 1; sig < 20; sig++) {
+        if (sigismember(sset, sig)) {
+            printf(" %d", sig);
+        }
+    }
+    printf("\n");
+}
+#endif
+
+/* JVM hooks */
+static int jk2_jni_exit_signaled = JK_FALSE;
+static int jk2_jni_exit_code = 0;
+static int jk2_jni_abort_signaled = JK_FALSE;
+
+static void jk2_jni_exit_hook(int code)
+{
+    jk2_jni_exit_signaled = JK_TRUE;
+    jk2_jni_abort_signaled = JK_TRUE;
+    jk2_jni_exit_code = code;
+
+#ifdef DEBUG
+    fprintf(stderr, "JVM exit hook called %d\n", code);
+#endif
+}
+
+static void jk2_jni_abort_hook()
+{
+    jk2_jni_abort_signaled = JK_TRUE;
+
+#ifdef DEBUG
+    fprintf(stderr, "JVM abort hook\n");
+#endif
+}
+
+/** Load the VM. Must be called after init.
+ */
+static int jk2_vm_loadJvm(jk_env_t *env, jk_vm_t * jkvm)
+{
+
+    apr_dso_handle_t *dsoHandle;
+    apr_status_t rc;
+    apr_pool_t *aprPool;
+
+    aprPool = (apr_pool_t *) env->getAprPool(env);
+
+    if (aprPool == NULL)
+        return JK_FALSE;
+
+    /* XXX How do I specify RTLD_NOW and RTLD_GLOBAL ? */
+    rc = apr_dso_load(&dsoHandle, jkvm->jvm_dll_path, aprPool);
+
+    if (rc == APR_SUCCESS) {
+        rc = apr_dso_sym((apr_dso_handle_sym_t *) & jni_create_java_vm,
+                         dsoHandle, "JNI_CreateJavaVM");
+    }
+
+    if (rc == APR_SUCCESS) {
+        rc = apr_dso_sym((apr_dso_handle_sym_t *) &
+                         jni_get_default_java_vm_init_args, dsoHandle,
+                         "JNI_GetDefaultJavaVMInitArgs");
+    }
+
+    if (rc == APR_SUCCESS) {
+        rc = apr_dso_sym((apr_dso_handle_sym_t *) & jni_get_created_java_vms,
+                         dsoHandle, "JNI_GetCreatedJavaVMs");
+    }
+
+    if (rc != APR_SUCCESS) {
+        char buf[256];
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "Can't load native library %s : %s\n",
+                      jkvm->jvm_dll_path, apr_dso_error(dsoHandle, buf, 256));
+        return JK_ERR;
+    }
+
+    if (jni_create_java_vm == NULL ||
+        jni_get_default_java_vm_init_args == NULL ||
+        jni_get_created_java_vms == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_EMERG,
+                      "jni.loadJvm() Can't resolve symbols %s\n",
+                      jkvm->jvm_dll_path);
+        apr_dso_unload(dsoHandle);
+        return JK_ERR;
+    }
+
+    if (jkvm->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "jni.loadJvm() %s symbols resolved\n",
+                      jkvm->jvm_dll_path);
+
+    return JK_OK;
+
+}
+
+
+static void *jk2_vm_attach(jk_env_t *env, jk_vm_t * jkvm)
+{
+    JNIEnv *rc = NULL;
+    int err;
+    JavaVM *jvm = (JavaVM *) jkvm->jvm;
+
+    if (jvm == NULL || jk2_jni_abort_signaled)
+        return NULL;
+
+#if defined LINUX && defined APACHE2_SIGHACK
+    /* [V] This message is important. If there are signal mask issues,    *
+     *     the JVM usually hangs when a new thread tries to attach to it  */
+    /* XXX do we need to do that on _each_ attach or only when we create
+       the vm ??? */
+    /*     jk2_linux_signal_hack(); */
+#endif
+
+    err = (*jvm)->GetEnv(jvm, (void **)&rc, JNI_VERSION_1_2);
+    /* If the current thread is allready attached to the VM return the
+       appropriate interface. There is no need to call the AttachCurrentThread.
+     */
+    if (err == 0) {
+        if (jkvm->mbean->debug >= 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "vm.attach() allready attached\n");
+        return (void *)rc;
+    }
+    /* The error code is either JNI_OK (allready attached) or JNI_EDETACHED.
+       Othere possibility is that specified version is not supported,
+       and the returned err in that case is JNI_EVERSION.
+     */
+    if (err != JNI_EDETACHED) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "vm.attach() GetEnv failed %d\n", err);
+        return NULL;
+    }
+
+    err = (*jvm)->AttachCurrentThread(jvm, (void **)
+                                      &rc, NULL);
+    if (err != 0) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "vm.attach() error %d\n", err);
+        return NULL;
+    }
+    if (jkvm->mbean->debug >= 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG, "vm.attach() ok\n");
+    return (void *)rc;
+}
+
+
+static void jk2_vm_detach(jk_env_t *env, jk_vm_t * jkvm)
+{
+    int err;
+    JavaVM *jvm = (JavaVM *) jkvm->jvm;
+
+    if (jvm == NULL || jk2_jni_abort_signaled) {
+        return;
+    }
+
+    err = (*jvm)->DetachCurrentThread(jvm);
+    if (err == 0) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO, "vm.detach() ok\n");
+    }
+    else {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "vm.detach() cannot detach from JVM.\n");
+    }
+}
+
+static int jk2_file_exists(jk_env_t *env, const char *f)
+{
+    if (f) {
+        struct stat st;
+        if ((0 == stat(f, &st)) && (st.st_mode & S_IFREG)) {
+            return JK_TRUE;
+        }
+    }
+    return JK_FALSE;
+}
+
+/* Add more guessing, based on various vm layouts */
+
+/* Some guessing - to spare the user ( who might know less
+   than we do ).
+*/
+#ifdef WIN32
+/* On WIN32 use the Registry couse Java itself relies on that.
+*/
+#define JAVASOFT_REGKEY "SOFTWARE\\JavaSoft\\Java Runtime Environment\\"
+
+static char *jk2_vm_guessJvmDll(jk_env_t *env, jk_map_t *props,
+                                jk_vm_t * jkvm)
+{
+    HKEY hkjs;
+    static char jvm[MAX_PATH + 1];
+    char reg[MAX_PATH + 1];
+    char *cver;
+    jk_pool_t *p = props->pool;
+    unsigned int err, klen = MAX_PATH;
+
+    strcpy(reg, JAVASOFT_REGKEY);
+    cver = &reg[sizeof(JAVASOFT_REGKEY) - 1];
+    if ((err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg,
+                            0, KEY_READ, &hkjs)) != ERROR_SUCCESS) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jni.guessJvmDll() failed to open Registry key\n");
+        return NULL;
+    }
+    if ((err = RegQueryValueEx(hkjs, "CurrentVersion", NULL, NULL,
+                               (unsigned char *)cver,
+                               &klen)) != ERROR_SUCCESS) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jni.guessJvmDll() failed obtaining Current Version\n");
+        RegCloseKey(hkjs);
+        return NULL;
+    }
+    RegCloseKey(hkjs);
+    if ((err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg,
+                            0, KEY_READ, &hkjs)) != ERROR_SUCCESS) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jni.guessJvmDll() failed to open Registry key\n");
+        return NULL;
+    }
+    klen = MAX_PATH;
+    if ((err = RegQueryValueEx(hkjs, "RuntimeLib", NULL, NULL,
+                               (unsigned char *)jvm,
+                               &klen)) != ERROR_SUCCESS) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jni.guessJvmDll() failed obtaining Runtime Library\n");
+        RegCloseKey(hkjs);
+        return NULL;
+    }
+    RegCloseKey(hkjs);
+
+    return jvm;
+}
+#else
+static char *jk2_vm_guessJvmDll(jk_env_t *env, jk_map_t *props,
+                                jk_vm_t * jkvm)
+{
+    char *jvm;
+    jk_pool_t *p = props->pool;
+    const char **current = defaultVM_PATH;
+
+    /* We need at least JAVA_HOME ( either env or in settings )
+     */
+    while (*current != NULL) {
+        jvm = jk2_config_replaceProperties(env, props, p,
+                                           (char *)p->pstrdup(env, p,
+                                                              *current));
+
+        if (jvm != NULL && jk2_file_exists(env, jvm)) {
+            char *ldlib;
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "jni.guessJvmDll() trying %s\n", jvm);
+            /* Check if the LD_LIBRARY_PATH points to the discovered jvm.
+             * XXX only tested on Linux.
+             */
+#if defined(LINUX)
+            ldlib = getenv(PATH_ENV_VARIABLE);
+            if (ldlib && strlen(ldlib)) {
+                char *token;
+
+                token = strtok(ldlib, PATH_SEPARATOR_STR);
+                while (token != NULL) {
+                    if (strncmp(token, jvm, strlen(token)) == 0) {
+                        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                                      "jni.guessJvmDll() found %s in %s.\n",
+                                      jvm, token);
+                        return jvm;
+                    }
+                    token = strtok(NULL, PATH_SEPARATOR_STR);
+                }
+                env->l->jkLog(env, env->l, JK_LOG_INFO,
+                              "jni.guessJvmDll() could not find %s in the LD_LIBRARY_PATH\n",
+                              jvm);
+                return NULL;
+            }
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "jni.guessJvmDll() LD_LIBRARY_PATH environment var is not set\n");
+            return NULL;
+#else
+            return jvm;
+#endif
+        }
+
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jni.guessJvmDll() failed %s\n", jvm);
+        current++;
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO, "jni.guessJvmDll() failed\n");
+
+    return NULL;
+}
+#endif
+
+static int jk2_vm_initVM(jk_env_t *env, jk_vm_t * jkvm)
+{
+    int jvm_version;
+    JDK1_1InitArgs vm_args11;
+    jk_map_t *props = jkvm->properties;
+    JavaVMInitArgs vm_args;
+    JNIEnv *penv;
+    JavaVMOption options[JK2_MAXOPTIONS * 2];
+    JavaVM *jvm;
+    int optn = 0, err, classn = 0, classl = 0, i;
+    char *classpath = NULL;
+
+    /** Make sure we have the vm dll */
+    if (jkvm->jvm_dll_path == NULL ||
+        !jk2_file_exists(env, jkvm->jvm_dll_path)) {
+        jkvm->jvm_dll_path = jk2_vm_guessJvmDll(env, props, jkvm);
+    }
+
+    if (jkvm->jvm_dll_path == NULL) {
+        jkvm->jvm_dll_path =
+            jk2_config_replaceProperties(env, props, props->pool,
+                                         "libjvm.${so}");
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "vm.init(): no jvm_dll_path, will use LD_LIBRARY_PATH %s\n",
+                      jkvm->jvm_dll_path);
+    }
+    else {
+        env->l->jkLog(env, env->l, JK_LOG_INFO, "vm.init(): Jni lib: %s\n",
+                      jkvm->jvm_dll_path);
+    }
+
+    err = jk2_vm_loadJvm(env, jkvm);
+
+    if (err != JK_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_EMERG,
+                      "jni.loadJvm() Error - can't load jvm dll\n");
+        /* [V] no detach needed here */
+        return JK_ERR;
+    }
+
+
+#if defined LINUX && defined APACHE2_SIGHACK
+    /* [V] This message is important. If there are signal mask issues,    *
+     *     the JVM usually hangs when a new thread tries to attach to it  */
+    /* XXX do we need to do that on _each_ attach or only when we create
+       the vm ??? */
+    jk2_linux_signal_hack();
+#endif
+
+    /* That's kind of strange - if we have JNI_VERSION_1_2 I assume
+       we also have 1.2, what do we detect ???? */
+
+    /* [V] Idea: ask for 1.2. If the JVM is 1.1 it will return 1.1 instead  */
+    /*     Note: asking for 1.1 won't work, 'cause 1.2 JVMs will return 1.1 */
+    vm_args11.version = JNI_VERSION_1_2;
+
+    if (0 != jni_get_default_java_vm_init_args(&vm_args11)) {
+        env->l->jkLog(env, env->l, JK_LOG_EMERG,
+                      "vm.detect() Fail-> can't get default vm init args\n");
+        return JK_ERR;
+    }
+
+    jvm_version = vm_args11.version;
+
+    if (jvm_version != JNI_VERSION_1_2) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "vm.detect() found: %X expecting 1.2\n", jvm_version);
+        return JK_ERR;
+    }
+
+    for (classn = 0; classn < jkvm->nClasspath; classn++)
+        classl += strlen(jkvm->classpath[classn]);
+    if (classl) {
+        classpath = jkvm->pool->calloc(env, jkvm->pool,
+                                       classl + classn +
+                                       sizeof("-Djava.class.path="));
+        strcpy(classpath, "-Djava.class.path=");
+        strcat(classpath, jkvm->classpath[0]);
+        for (i = 1; i < classn; i++) {
+            strcat(classpath, PATH_SEPARATOR_STR);
+            strcat(classpath, jkvm->classpath[i]);
+        }
+    }
+    while (jkvm->options[optn]) {
+        if (jkvm->mbean->debug > 1)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "vm.openJvm2() Option: %s\n", jkvm->options[optn]);
+        /* Pass it "as is" */
+        options[optn].optionString = jkvm->options[optn];
+        optn++;
+    }
+    if (classpath) {
+        if (jkvm->mbean->debug > 1)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "vm.openJvm2() Classpath: %s\n", classpath);
+        options[optn++].optionString = classpath;
+    }
+
+    /* Set the abort and exit hooks */
+    options[optn].optionString = "exit";
+    options[optn++].extraInfo = jk2_jni_exit_hook;
+    options[optn].optionString = "abort";
+    options[optn++].extraInfo = jk2_jni_abort_hook;
+
+    vm_args.version = JNI_VERSION_1_2;
+    vm_args.options = options;
+    vm_args.nOptions = optn;
+    vm_args.ignoreUnrecognized = JNI_TRUE;
+
+    err = jni_create_java_vm(&jvm, &penv, &vm_args);
+
+    if (JNI_EEXIST == err) {
+        int vmCount;
+
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "vm.open2() try to attach to existing vm.\n");
+
+        err = jni_get_created_java_vms(&jvm, 1, &vmCount);
+
+        if (NULL == jvm) {
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "vm.open1() error attaching %d\n", err);
+            return JK_ERR;
+        }
+
+        jkvm->jvm = (void *)jvm;
+        return JK_OK;
+    }
+    else if (err != 0) {
+        env->l->jkLog(env, env->l, JK_LOG_EMERG,
+                      "Fail-> could not create JVM, code: %d \n", err);
+        return JK_ERR;
+    }
+
+    jkvm->jvm = (void *)jvm;
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO, "vm.open2() done\n");
+
+    return JK_OK;
+}
+
+static void jk2_vm_destroy(jk_env_t *env, jk_vm_t * jkvm)
+{
+    int err;
+    JavaVM *jvm = (JavaVM *) jkvm->jvm;
+
+    if (jvm == NULL || jk2_jni_abort_signaled) {
+        return;
+    }
+
+    err = (*jvm)->DestroyJavaVM(jvm);
+    if (err == 0) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO, "vm.destroy() ok\n");
+    }
+    else {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "vm.destroy() cannot destroy the JVM.\n");
+    }
+}
+
+static int JK_METHOD
+jk2_jk_vm_setProperty(jk_env_t *env, jk_bean_t *mbean, char *name,
+                      void *valueP)
+{
+    jk_vm_t *jkvm = mbean->object;
+    char *value = valueP;
+
+    if (strcmp(name, "OPT") == 0) {
+        if (jkvm->nOptions < JK2_MAXOPTIONS) {
+            jkvm->options[jkvm->nOptions] = value;
+            jkvm->nOptions++;
+        }
+    }
+    else if (strcmp(name, "JVM") == 0) {
+        jkvm->jvm_dll_path = value;
+    }
+    else if (strcmp(name, "classpath") == 0) {
+        if (jkvm->nClasspath < JK2_MAXOPTIONS) {
+            jkvm->classpath[jkvm->nClasspath] = value;
+            jkvm->nClasspath++;
+        }
+    }
+    else {
+        return JK_ERR;
+    }
+
+    return JK_OK;
+}
+
+
+int JK_METHOD jk2_vm_factory(jk_env_t *env, jk_pool_t *pool,
+                             jk_bean_t *result, char *type, char *name)
+{
+    jk_vm_t *jkvm;
+    jk_workerEnv_t *workerEnv;
+
+    workerEnv = env->getByName(env, "workerEnv");
+
+    jkvm = (jk_vm_t *) pool->calloc(env, pool, sizeof(jk_vm_t));
+
+    jkvm->pool = pool;
+
+    jkvm->jvm_dll_path = NULL;
+    jkvm->nOptions = 0;
+
+    jkvm->init = jk2_vm_initVM;
+    jkvm->attach = jk2_vm_attach;
+    jkvm->detach = jk2_vm_detach;
+    jkvm->destroy = jk2_vm_destroy;
+
+    result->object = jkvm;
+    result->setAttribute = jk2_jk_vm_setProperty;
+    jkvm->mbean = result;
+
+    jkvm->properties = workerEnv->initData;
+
+    workerEnv->vm = jkvm;
+
+    return JK_OK;
+}
+
+#endif /* Java2 */
+
+#else /* HAVE_JNI */
+
+int JK_METHOD jk2_vm_factory(jk_env_t *env, jk_pool_t *pool,
+                             jk_bean_t *result, char *type, char *name)
+{
+    result->disabled = 1;
+    return JK_OK;
+}
+#endif
diff --git a/connectors/jk/native2/common/jk_workerEnv.c b/connectors/jk/native2/common/jk_workerEnv.c
new file mode 100644
index 0000000..7f28d77
--- /dev/null
+++ b/connectors/jk/native2/common/jk_workerEnv.c
@@ -0,0 +1,803 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Workers controller                                         *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+#include "jk_env.h"
+#include "jk_workerEnv.h"
+#include "jk_config.h"
+#include "jk_worker.h"
+#include "jk_shm.h"
+#include "jk_channel.h"
+#include "jk_registry.h"
+
+#define DEFAULT_WORKER              ("lb")
+
+static void jk2_workerEnv_close(jk_env_t *env, jk_workerEnv_t *wEnv);
+static void jk2_workerEnv_initHandlers(jk_env_t *env, jk_workerEnv_t *wEnv);
+static int jk2_workerEnv_init1(jk_env_t *env, jk_workerEnv_t *wEnv);
+
+/* ==================== Setup ==================== */
+
+
+static int JK_METHOD jk2_workerEnv_setAttribute(struct jk_env *env,
+                                                struct jk_bean *mbean,
+                                                char *name, void *valueP)
+{
+    jk_workerEnv_t *wEnv = mbean->object;
+    char *value = valueP;
+
+    /** XXX Should it be per/uri ?
+     */
+    if (strcmp(name, "logger") == 0) {
+        wEnv->logger_name = value;
+    }
+    else if (strcmp(name, "sslEnable") == 0) {
+        wEnv->ssl_enable = JK_TRUE;
+    }
+    else if (strcmp(name, "timing") == 0) {
+        wEnv->timing = atoi(value);
+    }
+    else if (strcmp(name, "httpsIndicator") == 0) {
+        wEnv->https_indicator = value;
+    }
+    else if (strcmp(name, "certsIndicator") == 0) {
+        wEnv->certs_indicator = value;
+    }
+    else if (strcmp(name, "cipherIndicator") == 0) {
+        wEnv->cipher_indicator = value;
+    }
+    else if (strcmp(name, "sessionIndicator") == 0) {
+        wEnv->session_indicator = value;
+    }
+    else if (strcmp(name, "keySizeIndicator") == 0) {
+        wEnv->key_size_indicator = value;
+    }
+    else if (strcmp(name, "forwardKeySize") == 0) {
+        wEnv->options |= JK_OPT_FWDKEYSIZE;
+        /* Small change in how we treat options: we have a default,
+           and assume that any option declared by user has the intention
+           of overriding the default ( "-Option == no option, leave the
+           default
+         */
+    }
+    else if (strcmp(name, "forwardURICompat") == 0) {
+        wEnv->options &= ~JK_OPT_FWDURIMASK;
+        wEnv->options |= JK_OPT_FWDURICOMPAT;
+    }
+    else if (strcmp(name, "forwardURICompatUnparsed") == 0) {
+        wEnv->options &= ~JK_OPT_FWDURIMASK;
+        wEnv->options |= JK_OPT_FWDURICOMPATUNPARSED;
+    }
+    else if (strcmp(name, "forwardURIEscaped") == 0) {
+        wEnv->options &= ~JK_OPT_FWDURIMASK;
+        wEnv->options |= JK_OPT_FWDURIESCAPED;
+    }
+    else if (strcmp(name, "noRecoveryIfRequestSent") == 0) {
+        wEnv->options &= ~JK_OPT_RECOSTRATEGYMASK;
+        wEnv->options |= JK_OPT_RECO_ABORTIFTCGETREQUEST;
+    }
+    else if (strcmp(name, "noRecoveryIfHeaderSent") == 0) {
+        wEnv->options &= ~JK_OPT_RECOSTRATEGYMASK;
+        wEnv->options |= JK_OPT_RECO_ABORTIFTCSENDHEADER;
+    }
+    else {
+        return JK_ERR;
+    }
+
+    return JK_OK;
+}
+
+
+static void jk2_workerEnv_close(jk_env_t *env, jk_workerEnv_t *wEnv)
+{
+    int sz;
+    int i;
+
+    sz = wEnv->worker_map->size(env, wEnv->worker_map);
+
+    for (i = 0; i < sz; i++) {
+        jk_worker_t *w = wEnv->worker_map->valueAt(env, wEnv->worker_map, i);
+        if (w) {
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "destroy worker %s\n",
+                          wEnv->worker_map->nameAt(env, wEnv->worker_map, i));
+            if (w->mbean->destroy != NULL)
+                w->mbean->destroy(env, w->mbean);
+        }
+    }
+    env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                  "workerEnv.close() done %d\n", sz);
+    wEnv->worker_map->clear(env, wEnv->worker_map);
+}
+
+static void jk2_workerEnv_checkSpace(jk_env_t *env, jk_pool_t *pool,
+                                     void ***tableP, int *sizeP, int id)
+{
+    void **newTable;
+    int i;
+    int newSize = id + 4;
+
+    if (*sizeP > id)
+        return;
+    /* resize the table */
+    newTable = (void **)pool->calloc(env, pool, newSize * sizeof(void *));
+    for (i = 0; i < *sizeP; i++) {
+        newTable[i] = (*tableP)[i];
+    }
+    *tableP = newTable;
+    *sizeP = newSize;
+}
+
+static int jk2_workerEnv_initWorker(jk_env_t *env,
+                                    jk_workerEnv_t *wEnv, jk_worker_t *w)
+{
+
+    int rc;
+
+    if ((w == NULL) || (w->mbean == NULL))
+        return JK_ERR;
+
+    if (w->mbean->disabled)
+        return JK_OK;
+
+    w->workerEnv = wEnv;
+
+    if (w->mbean->state >= JK_STATE_INIT)
+        return JK_OK;
+
+    if (w->mbean->init == NULL)
+        return JK_OK;
+
+    rc = w->mbean->init(env, w->mbean);
+
+    if (rc == JK_OK) {
+        w->mbean->state = JK_STATE_INIT;
+    }
+    else {
+        w->mbean->state = JK_STATE_DISABLED;
+        w->mbean->disabled = JK_TRUE;
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "workerEnv.initWorkers() init failed for %s\n",
+                      w->mbean->name);
+    }
+    return JK_OK;
+}
+
+static int jk2_workerEnv_initWorkers(jk_env_t *env, jk_workerEnv_t *wEnv)
+{
+    int i;
+
+    for (i = 0; i < wEnv->worker_map->size(env, wEnv->worker_map); i++) {
+        jk_worker_t *w = wEnv->worker_map->valueAt(env, wEnv->worker_map, i);
+        jk2_workerEnv_initWorker(env, wEnv, w);
+    }
+    return JK_OK;
+}
+
+
+
+static int jk2_workerEnv_initChannel(jk_env_t *env,
+                                     jk_workerEnv_t *wEnv, jk_channel_t *ch)
+{
+    int rc = JK_OK;
+
+    ch->workerEnv = wEnv;
+
+    if (ch->mbean->disabled)
+        return JK_OK;
+
+    if (ch->mbean->init != NULL) {
+        rc = ch->mbean->init(env, ch->mbean);
+        if (rc != JK_OK) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "workerEnv.initChannel() init failed for %s\n",
+                          ch->mbean->name);
+            /* Disable the associated worker */
+            ch->worker->channel = NULL;
+            ch->worker->channelName = NULL;
+        }
+        jk2_workerEnv_initWorker(env, wEnv, ch->worker);
+    }
+
+    return rc;
+}
+
+static int jk2_workerEnv_initChannels(jk_env_t *env, jk_workerEnv_t *wEnv)
+{
+    int i;
+
+    for (i = 0; i < wEnv->channel_map->size(env, wEnv->channel_map); i++) {
+        jk_channel_t *ch =
+            wEnv->channel_map->valueAt(env, wEnv->channel_map, i);
+        jk2_workerEnv_initChannel(env, wEnv, ch);
+    }
+    return JK_OK;
+}
+
+
+static void jk2_workerEnv_initHandlers(jk_env_t *env, jk_workerEnv_t *wEnv)
+{
+    /* Find the max message id */
+    /* XXX accessing private data... env most provide some method to get this */
+    jk_map_t *registry = env->_registry;
+    int size = registry->size(env, registry);
+    int i;
+
+    for (i = 0; i < size; i++) {
+        jk_handler_t *handler;
+
+        char *name = registry->nameAt(env, registry, i);
+        if (strncmp(name, "handler.", 8) == 0) {
+            jk_bean_t *jkb = env->createBean2(env, wEnv->pool, name, "");
+            if (jkb == NULL || jkb->object == NULL)
+                continue;
+            handler = (jk_handler_t *) jkb->object;
+            handler->init(env, handler, wEnv);
+        }
+    }
+}
+
+static int jk2_workerEnv_registerHandler(jk_env_t *env, jk_workerEnv_t *wEnv,
+                                         const char *type,
+                                         const char *name, int preferedId,
+                                         jk_handler_callback callback,
+                                         char *signature)
+{
+
+    jk_handler_t *h =
+        (jk_handler_t *) wEnv->pool->calloc(env, wEnv->pool,
+                                            sizeof(jk_handler_t));
+    h->name = (char *)name;
+    h->messageId = preferedId;
+    h->callback = callback;
+    h->workerEnv = wEnv;
+
+    jk2_workerEnv_checkSpace(env, wEnv->pool,
+                             (void ***)&wEnv->handlerTable,
+                             &wEnv->lastMessageId, h->messageId);
+    wEnv->handlerTable[h->messageId] = h;
+    /*wEnv->l->jkLog( wEnv->l, JK_LOG_INFO, "Registered %s %d\n", */
+    /*           handler->name, handler->messageId); */
+
+    return JK_OK;
+}
+
+/** Called from the parent, in a multi-process server.
+ */
+static int jk2_workerEnv_parentInit(jk_env_t *env, jk_workerEnv_t *wEnv)
+{
+    char *configFile;
+
+    env->l->init(env, env->l);
+
+    configFile = wEnv->config->file;
+    if (configFile == NULL) {
+        wEnv->config->setPropertyString(env, wEnv->config,
+                                        "config.file",
+                                        JK_WORKER_FILE_DEF);
+        configFile = wEnv->config->file;
+    }
+
+    if (wEnv->shm != NULL && wEnv->shm->mbean->disabled)
+        wEnv->shm = NULL;
+
+    if (wEnv->shm != NULL) {
+        /* Disable the shm if the initialization fails
+         */
+        if (wEnv->shm->init(env, wEnv->shm) == JK_ERR)
+            wEnv->shm = NULL;
+    }
+
+    if (wEnv->shm != NULL && wEnv->shm->head != NULL) {
+        wEnv->shm->reset(env, wEnv->shm);
+        if (wEnv->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "workerEnv.init() Reset shm\n");
+    }
+    return JK_OK;
+}
+
+/** Normal child init
+ */
+static int jk2_workerEnv_init(jk_env_t *env, jk_workerEnv_t *wEnv)
+{
+    char *configFile;
+    jk_bean_t *jkb;
+
+    /* We need to pid here - Linux will return the pid of the thread if
+       called after init(), and we'll not be able to locate the slot id
+       This is part of the workarounds needed for Apache2's removal of
+       child_num from connection.
+     */
+    wEnv->childProcessId = getpid();
+
+    configFile = wEnv->config->file;
+    if (configFile == NULL) {
+        wEnv->config->setPropertyString(env, wEnv->config,
+                                        "config.file",
+                                        JK_WORKER_FILE_DEF);
+        configFile = wEnv->config->file;
+    }
+
+    jkb = env->createBean2(env, wEnv->mbean->pool, "threadMutex", NULL);
+    if (jkb != NULL) {
+        wEnv->cs = jkb->object;
+        jkb->init(env, jkb);
+    }
+
+    if (wEnv->logger_name != NULL) {
+        jkb = env->getBean(env, wEnv->logger_name);
+        if (jkb == NULL) {
+            jkb = env->createBean(env, env->globalPool, wEnv->logger_name);
+        }
+        env->alias(env, wEnv->logger_name, "logger");
+        env->l = jkb->object;
+    }
+    env->l->init(env, env->l);
+
+    /* Set default worker. It'll be used for all uris that have no worker
+     */
+    if (wEnv->defaultWorker == NULL) {
+        jk_worker_t *w =
+            wEnv->worker_map->get(env, wEnv->worker_map, "lb:lb");
+
+        if (w == NULL) {
+            jk_bean_t *jkb = env->createBean2(env, wEnv->pool, "lb", "lb");
+            w = jkb->object;
+            if (wEnv->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "workerEnv.init() create default worker %s\n",
+                              jkb->name);
+        }
+        wEnv->defaultWorker = w;
+    }
+
+    if (wEnv->vm != NULL && !wEnv->vm->mbean->disabled) {
+        wEnv->vm->init(env, wEnv->vm);
+    }
+
+    jk2_workerEnv_initChannels(env, wEnv);
+
+    jk2_workerEnv_initWorkers(env, wEnv);
+    jk2_workerEnv_initHandlers(env, wEnv);
+
+    if (wEnv->shm != NULL && wEnv->shm->mbean->disabled)
+        wEnv->shm = NULL;
+
+    if (wEnv->shm != NULL) {
+        /* Disable the shm if the initialization fails
+         */
+        if (wEnv->shm->init(env, wEnv->shm) == JK_ERR)
+            wEnv->shm = NULL;
+    }
+
+    wEnv->epStat = NULL;
+
+    wEnv->uriMap->init(env, wEnv->uriMap);
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO, "workerEnv.init() ok %s\n",
+                  configFile);
+    return JK_OK;
+}
+
+static int jk2_workerEnv_dispatch(jk_env_t *env, jk_workerEnv_t *wEnv,
+                                  void *target, jk_endpoint_t *e, int code,
+                                  jk_msg_t *msg)
+{
+    jk_handler_t *handler;
+    int rc;
+    jk_handler_t **handlerTable = wEnv->handlerTable;
+    int maxHandler = wEnv->lastMessageId;
+
+    rc = -1;
+    handler = NULL;
+
+    if (code < maxHandler) {
+        handler = handlerTable[code];
+    }
+
+    if (handler == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "workerEnv.dispatch() Invalid code: %d\n", code);
+        e->reply->dump(env, e->reply, "Message: ");
+        return JK_ERR;
+    }
+
+    if (wEnv->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "workerEnv.dispatch() Calling %d %s\n",
+                      handler->messageId, handler->name);
+
+    /* Call the message handler */
+    rc = handler->callback(env, target, e, msg);
+    return rc;
+}
+
+/* XXX This should go to channel ( or a base class for socket channels ) */
+/*
+ * Process incoming messages.
+ *
+ * We will know only at read time if the remote host closed
+ * the connection (half-closed state - FIN-WAIT2). In that case
+ * we must close our side of the socket and abort emission.
+ * We will need another connection to send the request
+ * There is need of refactoring here since we mix 
+ * reply reception (tomcat -> apache) and request send (apache -> tomcat)
+ * and everything using the same buffer (repmsg)
+ * ajp13/ajp14 is async but handling read/send this way prevent nice recovery
+ * In fact if tomcat link is broken during upload (browser ->apache ->tomcat)
+ * we'll loose data and we'll have to abort the whole request.
+ */
+static int jk2_workerEnv_processCallbacks(jk_env_t *env, jk_workerEnv_t *wEnv,
+                                          jk_endpoint_t *ep,
+                                          jk_ws_service_t *req)
+{
+    int code;
+    jk_handler_t *handler;
+    int rc;
+    ep->currentRequest = req;
+
+    /* Process reply - this is the main loop */
+    /* Start read all reply message */
+    while (1) {
+        jk_msg_t *msg = ep->reply;
+        rc = -1;
+        handler = NULL;
+
+        if (ep->worker->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "workerEnv.callbacks() %s\n",
+                          ep->worker->channel->mbean->name);
+
+        msg->reset(env, msg);
+
+        /* Check for reply in timeout */
+        if (ep->worker->reply_timeout != 0) {
+            if (ep->worker->channel->
+                hasinput(env, ep->worker->channel, ep,
+                         ep->worker->reply_timeout) != JK_TRUE) {
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "workerEnv.processCallbacks() no reply after %d ms waiting\n",
+                              ep->worker->reply_timeout);
+
+                /* Error is unrecoverable if tomcat failed (Tomcat allready got request) */
+                if (wEnv->options & JK_OPT_RECO_ABORTIFTCGETREQUEST)
+                    req->is_recoverable_error = JK_FALSE;
+
+                return JK_ERR;
+            }
+        }
+
+        rc = ep->worker->channel->recv(env, ep->worker->channel, ep, msg);
+        if (rc != JK_OK) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "workerEnv.processCallbacks() Error reading reply\n");
+
+            /* Error is unrecoverable if tomcat failed (Tomcat allready got request) */
+            if (wEnv->options & JK_OPT_RECO_ABORTIFTCGETREQUEST) {
+                req->is_recoverable_error = JK_FALSE;
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "workerEnv.processCallbacks() by configuration, avoid recovery when tomcat has received request\n");
+            }
+
+            return rc;
+        }
+
+        /** After I received the first message ( head most likely ), it
+            is impossible to recover */
+        ep->recoverable = JK_FALSE;
+
+        if (ep->worker->mbean->debug > 10)
+            ep->request->dump(env, msg, "Received");
+
+        /* XXX Use this _only_ for backward compatibility.
+           invoke() and jk_bean should be used, probably with a first parameter
+           indicating the target component and a second param indicating the
+           code ( local to each component ).
+         */
+        code = (int)msg->getByte(env, msg);
+        rc = jk2_workerEnv_dispatch(env, wEnv, req, ep, code, msg);
+
+        /* Process the status code returned by handler */
+        switch (rc) {
+        case JK_HANDLER_LAST:
+            /* no more data to be sent, fine we have finish here */
+            return JK_OK;
+        case JK_HANDLER_OK:
+            /* One-time request, continue to listen */
+            break;
+        case JK_HANDLER_RESPONSE:
+            /* 
+             * in upload-mode there is no second chance since
+             * we may have allready send part of uploaded data 
+             * to Tomcat.
+             * In this case if Tomcat connection is broken we must 
+             * abort request and indicate error.
+             * A possible work-around could be to store the uploaded
+             * data to file and replay for it
+             */
+            req->is_recoverable_error = JK_FALSE;
+            if (ep->worker->mbean->debug > 10)
+                msg->dump(env, msg, "Apache->tomcat");
+
+            rc = ep->worker->channel->send(env, ep->worker->channel, ep, msg);
+            if (rc < 0) {
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "workerEnv.processCallbacks() error sending response data\n");
+                ep->recoverable = JK_FALSE;
+                return rc;
+            }
+            break;
+        case JK_HANDLER_ERROR:
+            /* Normal error ( for example writing to the client failed ).
+             * The ajp connection is still in a stable state but if we ask in configuration
+             * to abort when header has been send to client, mark as unrecoverable.
+             */
+            if (wEnv->options & JK_OPT_RECO_ABORTIFTCSENDHEADER) {
+                req->is_recoverable_error = JK_FALSE;
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "workerEnv.processCallbacks() by configuration, avoid recovery when tomcat has started to send headers to client\n");
+            }
+            else
+                ep->recoverable = JK_TRUE;      /* Should we do this ? not sure */
+
+            return rc;
+        case JK_HANDLER_FATAL:
+            /*
+             * Protocol error. We'll disconnect the ajp connection and reconnect.
+             */
+            ep->recoverable = JK_FALSE;
+            return rc;
+        default:
+            /* Unknown status */
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "workerEnv.processCallbacks() unknown status %d\n",
+                          rc);
+            ep->recoverable = JK_FALSE;
+            return rc;
+        }
+    }
+    return JK_ERR;
+}
+
+static int jk2_workerEnv_addChannel(jk_env_t *env, jk_workerEnv_t *wEnv,
+                                    jk_channel_t *ch)
+{
+    jk_bean_t *jkb;
+
+    ch->mbean->id = wEnv->channel_map->size(env, wEnv->channel_map);
+    wEnv->channel_map->add(env, wEnv->channel_map, ch->mbean->name, ch);
+
+    /* Automatically create the ajp13 worker to be used with this channel.
+     */
+    jkb =
+        env->createBean2(env, ch->mbean->pool, "ajp13", ch->mbean->localName);
+    if (jkb == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "workerEnv.addChannel(): Can't find ajp13 worker\n");
+        return JK_ERR;
+    }
+    ch->worker = jkb->object;
+    ch->worker->channelName = ch->mbean->name;
+    ch->worker->channel = ch;
+
+    return JK_OK;
+}
+
+static int jk2_workerEnv_addEndpoint(jk_env_t *env, jk_workerEnv_t *wEnv,
+                                     jk_endpoint_t *ep)
+{
+    int pos = wEnv->endpointMap->size(env, wEnv->endpointMap);
+
+    wEnv->endpointMap->add(env, wEnv->endpointMap, ep->mbean->localName, ep);
+    ep->mbean->id = pos;
+
+    if (ep->mbean->init(env, ep->mbean) == JK_ERR)
+        return JK_ERR;
+
+    return JK_OK;
+}
+
+
+static int jk2_workerEnv_addWorker(jk_env_t *env, jk_workerEnv_t *wEnv,
+                                   jk_worker_t *w)
+{
+    int err = JK_OK;
+    jk_worker_t *oldW = NULL;
+
+    w->workerEnv = wEnv;
+
+    w->mbean->id = wEnv->worker_map->size(env, wEnv->worker_map);
+
+    w->rPoolCache = jk2_objCache_create(env, w->mbean->pool);
+
+    err = w->rPoolCache->init(env, w->rPoolCache, 1024);        /* XXX make it unbound */
+
+    wEnv->worker_map->put(env, wEnv->worker_map, w->mbean->name, w,
+                          (void *)&oldW);
+
+    if (oldW != NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "workerEnv.addWorker() duplicated %s worker \n",
+                      w->mbean->name);
+        if (w->mbean->destroy != NULL)
+            oldW->mbean->destroy(env, oldW->mbean);
+    }
+
+    return JK_OK;
+}
+
+static jk_worker_t *jk2_workerEnv_createWorker(jk_env_t *env,
+                                               jk_workerEnv_t *wEnv,
+                                               char *type, char *name)
+{
+    jk_worker_t *w = NULL;
+    jk_bean_t *jkb;
+
+    /* First find if it already exists */
+    w = env->getByName(env, name);
+    if (w != NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "workerEnv.createWorker(): Using existing worker %s\n",
+                      name);
+        return w;
+    }
+
+    jkb = env->createBean(env, wEnv->pool, name);
+
+    if (jkb == NULL || jkb->object == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "workerEnv.createWorker(): factory can't create worker %s:%s\n",
+                      type, name);
+        return NULL;
+    }
+    w = (jk_worker_t *)jkb->object;
+
+    return w;
+}
+
+
+int JK_METHOD jk2_workerEnv_factory(jk_env_t *env, jk_pool_t *pool,
+                                    jk_bean_t *result,
+                                    const char *type, const char *name)
+{
+    jk_workerEnv_t *wEnv;
+    jk_bean_t *jkb;
+
+    wEnv = (jk_workerEnv_t *)pool->calloc(env, pool, sizeof(jk_workerEnv_t));
+
+    /* env->l->jkLog(env, env->l, JK_LOG_DEBUG, "Creating workerEnv %#lx\n", wEnv); */
+
+    result->object = wEnv;
+    wEnv->mbean = result;
+    result->setAttribute = jk2_workerEnv_setAttribute;
+
+    wEnv->pool = pool;
+
+    wEnv->initData = NULL;
+    jk2_map_default_create(env, &wEnv->initData, pool);
+
+    /* Add 'compile time' settings. Those are defined in jk_global,
+       with the other platform-specific settings. No need to ask
+       the user what we can find ourself
+     */
+    wEnv->initData->put(env, wEnv->initData, "fs", FILE_SEPARATOR_STR, NULL);
+    wEnv->initData->put(env, wEnv->initData, "ps", PATH_SEPARATOR_STR, NULL);
+    wEnv->initData->put(env, wEnv->initData, "so", SO_EXTENSION, NULL);
+    wEnv->initData->put(env, wEnv->initData, "arch", ARCH, NULL);
+
+
+    wEnv->logger_name = NULL;
+    wEnv->was_initialized = JK_FALSE;
+    wEnv->options = JK_OPT_FWDURIDEFAULT | JK_OPT_RECOSTRATEGYDEFAULT;
+
+    /*
+     * By default we will try to gather SSL info.
+     * Disable this functionality through JkExtractSSL
+     */
+    wEnv->ssl_enable = JK_TRUE;
+    /*
+     * The defaults ssl indicators match those in mod_ssl (seems
+     * to be in more use).
+     */
+    wEnv->https_indicator = "HTTPS";
+    wEnv->certs_indicator = "SSL_CLIENT_CERT";
+
+    /*
+     * The following (comented out) environment variables match apache_ssl!
+     * If you are using apache_sslapache_ssl uncomment them (or use the
+     * configuration directives to set them.
+     *
+     wEnv->cipher_indicator = "HTTPS_CIPHER";
+     wEnv->session_indicator = NULL;
+     */
+
+    /*
+     * The following environment variables match mod_ssl! If you
+     * are using another module (say apache_ssl) comment them out.
+     */
+    wEnv->cipher_indicator = "SSL_CIPHER";
+    wEnv->session_indicator = "SSL_SESSION_ID";
+    wEnv->key_size_indicator = "SSL_CIPHER_USEKEYSIZE";
+
+    /*     if(!map_alloc(&(wEnv->automount))) { */
+    /*         jk_error_exit(APLOG_MARK, APLOG_EMERG, s, p, "Memory error"); */
+    /*     } */
+
+    wEnv->uriMap = NULL;
+
+    wEnv->envvars_in_use = JK_FALSE;
+    jk2_map_default_create(env, &wEnv->envvars, pool);
+
+    jk2_map_default_create(env, &wEnv->worker_map, wEnv->pool);
+    jk2_map_default_create(env, &wEnv->channel_map, wEnv->pool);
+    jk2_map_default_create(env, &wEnv->endpointMap, wEnv->pool);
+
+    wEnv->perThreadWorker = 0;
+
+    /* methods */
+    wEnv->init = &jk2_workerEnv_init;
+    wEnv->parentInit = &jk2_workerEnv_parentInit;
+    wEnv->close = &jk2_workerEnv_close;
+    wEnv->addWorker = &jk2_workerEnv_addWorker;
+    wEnv->addChannel = &jk2_workerEnv_addChannel;
+    wEnv->addEndpoint = &jk2_workerEnv_addEndpoint;
+    wEnv->initChannel = &jk2_workerEnv_initChannel;
+    wEnv->registerHandler = &jk2_workerEnv_registerHandler;
+    wEnv->processCallbacks = &jk2_workerEnv_processCallbacks;
+    wEnv->dispatch = &jk2_workerEnv_dispatch;
+    wEnv->globalEnv = env;
+
+    jkb = env->createBean2(env, wEnv->pool, "uriMap", "");
+    if (jkb == NULL || jkb->object == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "Error getting uriMap implementation\n");
+        return JK_ERR;
+    }
+    wEnv->uriMap = jkb->object;
+    wEnv->uriMap->workerEnv = wEnv;
+
+    jkb = env->createBean2(env, wEnv->pool, "config", "");
+    if (jkb == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, "Error creating config\n");
+        return JK_ERR;
+    }
+    env->alias(env, "config:", "config");
+    wEnv->config = jkb->object;
+    wEnv->config->file = NULL;
+    wEnv->config->workerEnv = wEnv;
+    wEnv->config->map = wEnv->initData;
+
+    wEnv->childId = -1;
+
+    wEnv->epStat = NULL;
+    jkb = env->createBean2(env, wEnv->pool, "shm", "");
+    if (jkb == NULL) {
+        wEnv->shm = NULL;
+    }
+    else {
+        env->alias(env, "shm:", "shm");
+        wEnv->shm = (jk_shm_t *)jkb->object;
+    }
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_worker_ajp13.c b/connectors/jk/native2/common/jk_worker_ajp13.c
new file mode 100644
index 0000000..b0a990b
--- /dev/null
+++ b/connectors/jk/native2/common/jk_worker_ajp13.c
@@ -0,0 +1,1043 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Description: AJP13 next generation Bi-directional protocol.
+ *              Backward compatible with Ajp13
+ * Author:      Henri Gomez <hgomez@apache.org>
+ * Author:      Costin <costin@costin.dnt.ro>                              
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           
+ */
+
+#include "jk_global.h"
+#include "jk_pool.h"
+#include "jk_channel.h"
+#include "jk_msg.h"
+#include "jk_logger.h"
+#include "jk_handler.h"
+#include "jk_service.h"
+#include "jk_env.h"
+#include "jk_objCache.h"
+#include "jk_requtil.h"
+#include "jk_registry.h"
+
+/* -------------------- Impl -------------------- */
+/*
+ * Properties available to Get.
+ */
+static char *jk2_worker_ajp13_getAttributeInfo[] = {
+    "channel", "groups", "max_connections", "epCount", "level", "lb_factor",
+    "lb_value", "route", "routeRedirect","graceful", "errorState", "errorTime",
+    "connectTimeout", "replyTimeout", "prepostTimeout", "debug", "disabled", NULL
+};
+
+static char *jk2_worker_ajp13_multiValueInfo[] = { "group", NULL };
+
+/*
+ * Properties available to Set.
+ */
+static char *jk2_worker_ajp13_setAttributeInfo[] = {
+    "channel", "group", "secretkey", "max_connections", "level", "lb_factor",
+    "route", "routeRedirect", "graceful", "connectTimeout", "replyTimeout",
+    "prepostTimeout", "debug", "disabled", NULL
+};
+
+static void *JK_METHOD jk2_worker_ajp13_getAttribute(jk_env_t *env,
+                                                     jk_bean_t *bean,
+                                                     char *name)
+{
+    jk_worker_t *worker = (jk_worker_t *)bean->object;
+
+    if (strcmp(name, "channelName") == 0 || strcmp(name, "channel") == 0) {
+        if (worker->channel != NULL)
+            return worker->channel->mbean->name;
+        else
+            return worker->channelName;
+    }
+    else if (strcmp(name, "route") == 0) {
+        return worker->route;
+    }
+    else if (strcmp(name, "routeRedirect") == 0) {
+        return worker->routeRedirect;
+    }
+    else if (strcmp(name, "debug") == 0) {
+        return jk2_env_itoa(env, bean->debug);
+    }
+    else if (strcmp(name, "groups") == 0) {
+        return jk2_map_concatKeys(env, worker->groups, ":");
+    }
+    else if (strcmp(name, "max_connections") == 0) {
+        return jk2_env_itoa(env, worker->maxEndpoints);
+    }
+    else if (strcmp(name, "level") == 0) {
+        return jk2_env_itoa(env, worker->level);
+    }
+    else if (strcmp(name, "errorTime") == 0) {
+        return jk2_env_itoa(env, worker->error_time);
+    }
+    else if (strcmp(name, "lb_value") == 0) {
+        return jk2_env_itoa(env, worker->lb_value);
+    }
+    else if (strcmp(name, "lb_factor") == 0) {
+        return jk2_env_itoa(env, worker->lb_factor);
+    }
+    else if (strcmp(name, "errorState") == 0) {
+        return jk2_env_itoa(env, worker->in_error_state);
+    }
+    else if (strcmp(name, "graceful") == 0) {
+        return jk2_env_itoa(env, worker->graceful);
+    }
+    else if (strcmp(name, "connectTimeout") == 0) {
+        return jk2_env_itoa(env, worker->connect_timeout);
+    }
+    else if (strcmp(name, "replyTimeout") == 0) {
+        return jk2_env_itoa(env, worker->reply_timeout);
+    }
+    else if (strcmp(name, "prepostTimeout") == 0) {
+        return jk2_env_itoa(env, worker->prepost_timeout);
+    }
+    else if (strcmp(name, "disabled") == 0) {
+        return jk2_env_itoa(env, bean->disabled);
+    }
+    else if (strcmp(name, "epCount") == 0) {
+        if (worker->endpointCache == NULL)
+            return "0";
+        return jk2_env_itoa(env, worker->endpointCache->count);
+    }
+    else {
+        return NULL;
+    }
+}
+
+/*
+ * Set worker properties.
+ */
+static int JK_METHOD
+jk2_worker_ajp13_setAttribute(jk_env_t *env, jk_bean_t *mbean,
+                              char *name, void *valueP)
+{
+    jk_worker_t *ajp13 = (jk_worker_t *)mbean->object;
+    char *value = (char *)valueP;
+
+    if (strcmp(name, "secretkey") == 0) {
+        ajp13->secret = value;
+    }
+    else if (strcmp(name, "tomcatId") == 0) {
+        ajp13->route = value;
+    }
+    else if (strcmp(name, "route") == 0) {
+        ajp13->route = value;
+    }
+    else if (strcmp(name, "routeRedirect") == 0) {
+        ajp13->routeRedirect = value;
+    }
+    else if (strcmp(name, "graceful") == 0) {
+        ajp13->graceful = atoi(value);
+    }
+    else if (strcmp(name, "connectTimeout") == 0) {
+        ajp13->connect_timeout = atoi(value);
+    }
+    else if (strcmp(name, "replyTimeout") == 0) {
+        ajp13->reply_timeout = atoi(value);
+    }
+    else if (strcmp(name, "prepostTimeout") == 0) {
+        ajp13->prepost_timeout = atoi(value);
+    }
+    else if (strcmp(name, "disabled") == 0) {
+        mbean->disabled = atoi(value);
+    }
+    else if (strcmp(name, "group") == 0) {
+        ajp13->groups->add(env, ajp13->groups, value, ajp13);
+    }
+    else if (strcmp(name, "lb_factor") == 0) {
+        ajp13->lb_factor = atoi(value);
+    }
+    else if (strcmp(name, "level") == 0) {
+        ajp13->level = atoi(value);
+    }
+    else if (strcmp(name, "channel") == 0) {
+        ajp13->channelName = value;
+    }
+    else if (strcmp(name, "max_connections") == 0) {
+        ajp13->maxEndpoints = atoi(value);
+    }
+     else if (strcmp(name, "debug") == 0) {
+        mbean->debug = atoi(value);
+    }
+    else {
+        return JK_ERR;
+    }
+
+    return JK_OK;
+}
+
+/* Webserver ask container to take control (logon phase) */
+#define JK_AJP13_PING               (unsigned char)8
+
+/* Webserver check if container is alive, since container should respond by cpong */
+#define JK_AJP13_CPING              (unsigned char)10
+
+/* Container response to cping request */
+#define JK_AJP13_CPONG              (unsigned char)9
+
+/* 
+ * Build the ping cmd. Tomcat will get control and will be able 
+ * to send any command.
+ *
+ * +-----------------------+
+ * | PING CMD (1 byte)     |
+ * +-----------------------+
+ *
+ * XXX Add optional Key/Value set .
+ *  
+ */
+int jk2_serialize_ping(jk_env_t *env, jk_msg_t *msg, jk_endpoint_t *ae)
+{
+    int rc;
+
+    /* To be on the safe side */
+    msg->reset(env, msg);
+
+    rc = msg->appendByte(env, msg, JK_AJP13_PING);
+    if (rc != JK_OK)
+        return JK_ERR;
+
+    return JK_OK;
+}
+
+
+/* 
+ * Build the cping cmd. Tomcat should respond by a cpong.
+ *
+ * +-----------------------+
+ * | CPING CMD (1 byte)    |
+ * +-----------------------+
+ *
+ * XXX Add optional Key/Value set .
+ *  
+ */
+int jk2_serialize_cping(jk_env_t *env, jk_msg_t *msg, jk_endpoint_t *ae)
+{
+    int rc;
+
+    /* To be on the safe side */
+    msg->reset(env, msg);
+
+    rc = msg->appendByte(env, msg, JK_AJP13_CPING);
+    if (rc != JK_OK)
+        return JK_ERR;
+
+    return JK_OK;
+}
+
+
+/*
+ * Close the endpoint (clean buf and close socket)
+ */
+static void jk2_close_endpoint(jk_env_t *env, jk_endpoint_t *ae)
+{
+    if (ae->worker->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG, "endpoint.close() %s\n",
+                      ae->worker->mbean->name);
+
+    /*     ae->reuse = JK_FALSE; */
+    if (ae->worker->channel != NULL)
+        ae->worker->channel->close(env, ae->worker->channel, ae);
+
+    ae->sd = -1;
+    ae->recoverable = JK_TRUE;
+
+    ae->cPool->reset(env, ae->cPool);
+    /* ae->cPool->close( env, ae->cPool ); */
+
+    /* Don't touch the ae->pool, the object has important
+       statistics */
+    /* ae->pool->reset( env, ae->pool ); */
+    /*     ae->pool->close( env, ae->pool ); */
+}
+
+/** Check if a channel is alive, send a cping and wait for a cpong
+    during timeoutms
+ */
+static int jk2_check_alive(jk_env_t *env, jk_endpoint_t *ae, int timeout)
+{
+
+    int err;
+    jk_msg_t *msg = ae->reply;
+
+    jk2_serialize_cping(env, msg, ae);
+    err = ae->worker->channel->send(env, ae->worker->channel, ae, msg);
+
+    if (err != JK_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "ajp13.checkalive() can't send cping request to %s\n",
+                      ae->worker->mbean->name);
+
+        return JK_ERR;
+    }
+
+    if (ae->worker->channel->hasinput(env, ae->worker->channel, ae,
+                                      timeout) != JK_TRUE) {
+
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "ajp13.checkalive() can't get cpong reply from %s in %d ms\n",
+                      ae->worker->mbean->name, timeout);
+
+        return JK_ERR;
+    }
+
+    err = ae->worker->channel->recv(env, ae->worker->channel, ae, msg);
+
+    if (err != JK_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "ajp13.checkalive() can't read cpong reply from %s\n",
+                      ae->worker->mbean->name);
+
+        return JK_ERR;
+    }
+
+    return JK_OK;
+}
+
+/** Connect a channel, implementing the logging protocol if ajp13
+ */
+static int jk2_worker_ajp13_connect(jk_env_t *env, jk_endpoint_t *ae)
+{
+    jk_channel_t *channel = ae->worker->channel;
+    jk_msg_t *msg;
+
+    int err;
+
+    if (channel == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "ajp13.connect() no channel %s\n",
+                      ae->worker->mbean->name);
+        return JK_ERR;
+    }
+
+    if (ae->worker->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "ajp13.connect() %s %s\n", ae->worker->channelName,
+                      channel->mbean->name);
+
+    err = channel->open(env, channel, ae);
+
+    if (err != JK_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "ajp13.connect() failed %s\n", ae->worker->mbean->name);
+        return JK_ERR;
+    }
+
+    /* We just reconnected, reset error state
+     */
+    ae->worker->in_error_state = JK_FALSE;
+
+    ae->stats->connectedTime = apr_time_now();
+
+    /** XXX use a 'connected' field */
+    if (ae->sd == -1)
+        ae->sd = 0;
+
+    if (ae->worker->connect_timeout != 0) {
+        if (jk2_check_alive(env, ae, ae->worker->connect_timeout) != JK_OK)
+            return JK_ERR;
+    }
+    else if (ae->worker->prepost_timeout != 0) {
+        if (jk2_check_alive(env, ae, ae->worker->prepost_timeout) != JK_OK)
+            return JK_ERR;
+    }
+
+    /* Check if we must execute a logon after the physical connect */
+    if (ae->worker->secret == NULL)
+        return JK_OK;
+
+    /* Do the logon process */
+    env->l->jkLog(env, env->l, JK_LOG_INFO, "ajp13.connect() logging in\n");
+
+    /* use the reply buffer - it's a new channel, it is certainly not
+       in use. The request and post buffers are probably in use if this
+       is a reconnect */
+    msg = ae->reply;
+
+    /* send a ping message to told container to take control (logon phase) */
+    jk2_serialize_ping(env, msg, ae);
+    err = ae->worker->channel->send(env, ae->worker->channel, ae, msg);
+
+    /* Move to 'slave' mode, listening to messages */
+    err = ae->worker->workerEnv->processCallbacks(env, ae->worker->workerEnv,
+                                                  ae, NULL);
+
+    /* Anything but OK - the login failed
+     */
+    if (err != JK_OK) {
+        jk2_close_endpoint(env, ae);
+    }
+    return err;
+}
+
+/** There is no point of trying multiple times - each channel may
+    have built-in recovery mechanisms
+*/
+#define JK_RETRIES 2
+
+
+static int JK_METHOD
+jk2_worker_ajp13_forwardStream(jk_env_t *env, jk_worker_t *worker,
+                               jk_ws_service_t *s, jk_endpoint_t *e)
+{
+    int err = JK_OK;
+    int attempt;
+    int has_post_body = JK_FALSE;
+
+    e->recoverable = JK_TRUE;
+    s->is_recoverable_error = JK_TRUE;
+
+    /*
+     * Try to send the request on a valid endpoint. If one endpoint
+     * fails, close the channel and try again ( maybe tomcat was restarted )
+     * 
+     * XXX JK_RETRIES could be replaced by the number of workers in
+     * a load-balancing configuration 
+     */
+    for (attempt = 0; attempt < JK_RETRIES; attempt++) {
+
+        if (e->sd == -1) {
+            err = jk2_worker_ajp13_connect(env, e);
+            if (err != JK_OK) {
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "ajp13.service() failed to connect endpoint errno=%d %s\n",
+                              errno, strerror(errno));
+                e->worker->in_error_state = JK_TRUE;
+                return err;
+            }
+            if (worker->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "ajp13.service() connecting to endpoint \n");
+        }
+
+        err = e->worker->channel->send(env, e->worker->channel, e,
+                                       e->request);
+
+        if (e->worker->mbean->debug > 10)
+            e->request->dump(env, e->request, "Sent");
+
+        if (err != JK_OK) {
+            /* Can't send - bad endpoint, try again */
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "ajp13.service() error sending, reconnect %s %d %d %s\n",
+                          e->worker->channelName, err, errno,
+                          strerror(errno));
+            jk2_close_endpoint(env, e);
+            continue;
+        }
+
+        /* We should have a channel now, send the post data */
+
+        /* Prepare to send some post data ( ajp13 proto ). We do that after the
+           request was sent ( we're receiving data from client, can be slow, no
+           need to delay - we can do that in paralel. ( not very sure this is
+           very usefull, and it brakes the protocol ) ! */
+
+        /* || s->is_chunked - this can't be done here. The original protocol sends the first
+           chunk of post data ( based on Content-Length ), and that's what the java side expects.
+           Sending this data for chunked would break other ajp13 serers.
+
+           Note that chunking will continue to work - using the normal read.
+         */
+        if (has_post_body || s->left_bytes_to_send > 0
+            || s->reco_status == RECO_FILLED) {
+            /* We never sent any POST data and we check it we have to send at
+             * least of block of data (max 8k). These data will be kept in reply
+             * for resend if the remote Tomcat is down, a fact we will learn only
+             * doing a read (not yet) 
+             */
+
+            /* If we have the service recovery buffer FILLED and we're in first attempt */
+            /* recopy the recovery buffer in post instead of reading it from client */
+            if (s->reco_status == RECO_FILLED && (attempt == 0)) {
+                /* Get in post buf the previously saved POST */
+
+                if (s->reco_buf->copy(env, s->reco_buf, e->post) < 0) {
+                    s->is_recoverable_error = JK_FALSE;
+                    env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                                  "ajp13.service() can't use the LB recovery buffer, aborting\n");
+                    return JK_ERR;
+                }
+
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "ajp13.service() using the LB recovery buffer\n");
+            }
+            else {
+                if (attempt == 0)
+                    err = jk2_serialize_postHead(env, e->post, s, e);
+                else
+                    err = JK_OK;        /* We already have the initial body chunk */
+
+                if (e->worker->mbean->debug > 10)
+                    e->request->dump(env, e->request, "Post head");
+
+                if (err != JK_OK) {
+                    /* the browser stop sending data, no need to recover */
+                    /* e->recoverable = JK_FALSE; */
+                    s->is_recoverable_error = JK_FALSE;
+                    env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                                  "ajp13.service() Error receiving initial post %d %d %d\n",
+                                  err, errno, attempt);
+
+                    /* BR #27281 : Should we return HTTP 500 since its the user who stop the sending ? */
+                    /* may be not, so return another HTTP code -> use PARTIAL CONTENT, 206 instead */
+                    s->status = 206;
+                    return JK_ERR;
+                }
+
+                /* If a recovery buffer exist (LB mode), save here the post buf */
+                if (s->reco_status == RECO_INITED) {
+                    /* Save the post for recovery if needed */
+                    if (e->post->copy(env, e->post, s->reco_buf) < 0) {
+                        s->is_recoverable_error = JK_FALSE;
+                        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                                      "ajp13.service() can't save the LB recovery buffer, aborting\n");
+                        return JK_ERR;
+                    }
+                    else
+                        s->reco_status = RECO_FILLED;
+                }
+            }
+
+            has_post_body = JK_TRUE;
+            err = e->worker->channel->send(env, e->worker->channel, e,
+                                           e->post);
+            if (err != JK_OK) {
+                /* e->recoverable = JK_FALSE; */
+                /*                 s->is_recoverable_error = JK_FALSE; */
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "ajp13.service() Error sending initial post %d %d %d\n",
+                              err, errno, attempt);
+                jk2_close_endpoint(env, e);
+                continue;
+                /*  return JK_ERR; */
+            }
+        }
+
+        err =
+            e->worker->workerEnv->processCallbacks(env, e->worker->workerEnv,
+                                                   e, s);
+
+        /* if we can't get reply, check if no recover flag was set 
+         * if is_recoverable_error is cleared, we have started received 
+         * upload data and we must consider that operation is no more recoverable
+         */
+        if (err != JK_OK && !e->recoverable) {
+            s->is_recoverable_error = JK_FALSE;
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "ajp13.service() ajpGetReply unrecoverable error %d\n",
+                          err);
+            /* The connection is compromised, need to close it ! */
+            e->worker->in_error_state = 1;
+            return JK_ERR;
+        }
+
+        if (err != JK_OK) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "ajp13.service() ajpGetReply recoverable error %d\n",
+                          err);
+            jk2_close_endpoint(env, e);
+        }
+
+        if (err == JK_OK)
+            return err;
+    }
+    return err;
+}
+
+static int JK_METHOD
+jk2_worker_ajp13_forwardSingleThread(jk_env_t *env, jk_worker_t *worker,
+                                     jk_ws_service_t *s, jk_endpoint_t *e)
+{
+    int err;
+
+    if (e->worker->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "ajp13.forwardST() Before calling native channel %s\n",
+                      e->worker->channel->mbean->name);
+    err = e->worker->channel->send(env, e->worker->channel, e, e->request);
+    if (e->worker->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "ajp13.forwardST() After %d\n", err);
+
+    /* I assume no unrecoverable error can happen here - we're in a single thread,
+       so things are simpler ( at least in this area ) */
+    return err;
+}
+
+static int JK_METHOD
+jk2_worker_ajp13_service1(jk_env_t *env, jk_worker_t *w,
+                          jk_ws_service_t *s, jk_endpoint_t *e)
+{
+    int err;
+
+    if ((e == NULL) || (s == NULL)) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "ajp13.service() NullPointerException\n");
+        return JK_ERR;
+    }
+
+    if (w->channel == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "ajp13.service() no channel defined, error in init\n",
+                      w->mbean->name);
+        return JK_ERR;
+    }
+
+    e->currentRequest = s;
+
+    /* XXX configurable ? */
+    strncpy(e->stats->active, s->req_uri, 64);
+    /* Be sure this is null terminated if it's a long url */
+    e->stats->active[63] = '\0';
+
+    /* Prepare the messages we'll use. */
+    e->request->reset(env, e->request);
+    e->reply->reset(env, e->reply);
+    e->post->reset(env, e->post);
+
+    s->is_recoverable_error = JK_TRUE;
+    /* Up to now, we can recover */
+    e->recoverable = JK_TRUE;
+
+    s->left_bytes_to_send = s->content_length;
+    s->content_read = 0;
+
+    if (w->prepost_timeout != 0 && e->sd != -1) {
+        if (jk2_check_alive(env, e, e->worker->prepost_timeout) != JK_OK)
+            return JK_ERR;
+    }
+
+    /* 
+     * We get here initial request (in reqmsg)
+     */
+    err = jk2_serialize_request13(env, e->request, s, e);
+    if (err != JK_OK) {
+        s->is_recoverable_error = JK_FALSE;
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "ajp13.service(): error marshaling\n");
+        return JK_ERR;
+    }
+
+    if (w->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "ajp13.service() %s\n", w->channelName);
+
+    if (w->channel->beforeRequest != NULL) {
+        w->channel->beforeRequest(env, w->channel, w, e, s);
+    }
+
+    /* First message for this request */
+    if (w->channel->is_stream == JK_TRUE) {
+        err = jk2_worker_ajp13_forwardStream(env, w, s, e);
+    }
+    else {
+        err = jk2_worker_ajp13_forwardSingleThread(env, w, s, e);
+    }
+    if (err != JK_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "ajp13.service() Error  forwarding %s %d %d\n",
+                      e->worker->mbean->name, e->recoverable,
+                      e->worker->in_error_state);
+    }
+
+    if (w->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "ajp13.service() done %s\n", e->worker->mbean->name);
+
+    if (w->channel->afterRequest != NULL) {
+        w->channel->afterRequest(env, w->channel, w, e, s);
+    }
+
+    e->currentRequest = NULL;
+
+    return err;
+}
+
+
+
+
+static int JK_METHOD
+jk2_worker_ajp13_done(jk_env_t *env, jk_worker_t *we, jk_endpoint_t *e)
+{
+    jk_worker_t *w;
+    int rc = JK_OK;
+
+    w = e->worker;
+
+    if (e->cPool != NULL)
+        e->cPool->reset(env, e->cPool);
+
+    if (w->endpointCache == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, "ajp13.done() No pool\n");
+        return JK_ERR;
+    }
+
+    if (w->in_error_state) {
+        jk2_close_endpoint(env, e);
+        /*     if( w->mbean->debug > 0 )  */
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "ajp13.done() close endpoint %s error_state %d\n",
+                      w->mbean->name, w->in_error_state);
+    }
+
+    rc = w->endpointCache->put(env, w->endpointCache, e);
+    if (rc != JK_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "ajp13.done() Error recycling ep\n");
+        return rc;
+    }
+    if (w->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "ajp13.done() return to pool %s\n", w->mbean->name);
+    return JK_OK;
+}
+
+static int JK_METHOD
+jk2_worker_ajp13_getEndpoint(jk_env_t *env,
+                             jk_worker_t *ajp13, jk_endpoint_t **eP)
+{
+    jk_endpoint_t *e = NULL;
+    jk_bean_t *jkb;
+    int ret;
+
+    if (ajp13->secret == NULL) {
+    }
+
+    if (ajp13->endpointCache == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "ajp13.getEndpoint() No pool\n");
+        return JK_ERR;
+    }
+
+    e = ajp13->endpointCache->get(env, ajp13->endpointCache);
+
+    if (e != NULL) {
+        *eP = e;
+        return JK_OK;
+    }
+
+    if (ajp13->cs != NULL)
+        ajp13->cs->lock(env, ajp13->cs);
+
+    {
+        if (ajp13->maxEndpoints &&
+            ajp13->maxEndpoints <= ajp13->endpointMap->size(env,
+                                                            ajp13->
+                                                            endpointMap)) {
+            /* The maximum number of connections is reached */
+            ajp13->in_max_epcount = JK_TRUE;
+            if (ajp13->cs != NULL)
+                ajp13->cs->unLock(env, ajp13->cs);
+            if (ajp13->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "ajp13.getEndpoint(): maximum %d endpoints for %s reached\n",
+                              ajp13->maxEndpoints, ajp13->mbean->name);
+            return JK_ERR;
+        }
+        ajp13->in_max_epcount = JK_FALSE;
+
+        jkb = env->createBean2(env, ajp13->mbean->pool, "endpoint", NULL);
+        if (jkb == NULL) {
+            if (ajp13->cs != NULL)
+                ajp13->cs->unLock(env, ajp13->cs);
+            return JK_ERR;
+        }
+
+        e = (jk_endpoint_t *)jkb->object;
+        e->worker = ajp13;
+        e->sd = -1;
+
+        ajp13->endpointMap->add(env, ajp13->endpointMap, jkb->localName, jkb);
+
+        *eP = e;
+
+        ret = ajp13->workerEnv->addEndpoint(env, ajp13->workerEnv, e);
+    }
+    if (ajp13->cs != NULL)
+        ajp13->cs->unLock(env, ajp13->cs);
+
+    if (ajp13->mbean->debug > 0) {
+        if (ret == JK_OK)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "ajp13.getEndpoint(): Created endpoint %s %s \n",
+                          ajp13->mbean->name, jkb->name);
+        else
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "ajp13.getEndpoint(): endpoint creation %s %s failed\n",
+                          ajp13->mbean->name, jkb->name);
+    }
+
+    return ret;
+}
+
+/*
+ * Serve the request, using AJP13/AJP13
+ */
+static int JK_METHOD
+jk2_worker_ajp13_service(jk_env_t *env, jk_worker_t *w, jk_ws_service_t *s)
+{
+    int err;
+    jk_endpoint_t *e;
+
+    /* Get endpoint from the pool */
+    err = jk2_worker_ajp13_getEndpoint(env, w, &e);
+    if (err != JK_OK)
+        return err;
+
+    if ((w != NULL) && (w->channel != NULL) && (w->channel->status != NULL)) {
+        err = w->channel->status(env, w, w->channel);
+        if (err != JK_OK) {
+            jk2_worker_ajp13_done(env, w, e);
+            return err;
+        }
+    }
+
+    if (s->uriEnv != NULL && s->uriEnv->timing == JK_TRUE) {
+        e->stats->startTime = s->startTime;
+        e->stats->jkStartTime = e->stats->connectedTime = apr_time_now();
+        if (e->stats->startTime == 0)
+            e->stats->startTime = e->stats->jkStartTime;
+    }
+    e->stats->workerId = w->mbean->id;
+
+    err = jk2_worker_ajp13_service1(env, w, s, e);
+
+    /* One error doesn't mean the whole worker is in error state.
+     */
+    if (err == JK_OK) {
+        e->stats->reqCnt++;
+    }
+    else {
+        e->stats->errCnt++;
+    }
+
+    if (s->uriEnv != NULL && s->uriEnv->timing == JK_TRUE) {
+        apr_time_t reqTime;
+
+        e->stats->endTime = apr_time_now();
+        reqTime = e->stats->endTime - e->stats->startTime;
+
+        e->stats->totalTime += reqTime;
+        if (e->stats->maxTime < reqTime)
+            e->stats->maxTime = reqTime;
+    }
+    jk2_worker_ajp13_done(env, w, e);
+    return err;
+}
+
+
+static int JK_METHOD jk2_worker_ajp13_init(jk_env_t *env, jk_bean_t *bean)
+{
+    jk_worker_t *ajp13 = bean->object;
+    int rc;
+    int size;
+    int i;
+
+    if (ajp13->channel != NULL && ajp13->channel->mbean->debug > 0) {
+        ajp13->mbean->debug = ajp13->channel->mbean->debug;
+    }
+
+    if (ajp13->channel != NULL && ajp13->channel->mbean->disabled)
+        ajp13->mbean->disabled = JK_TRUE;
+
+    ajp13->endpointCache = jk2_objCache_create(env, ajp13->mbean->pool);
+
+    if (ajp13->endpointCache == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "ajp13.init(): error creating endpoint cache\n");
+        return JK_ERR;
+    }
+
+    /* Will grow */
+    rc = ajp13->endpointCache->init(env, ajp13->endpointCache, -1);
+    if (rc != JK_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "ajp13.init(): error creating endpoint cache\n");
+        return JK_ERR;
+    }
+
+    if (ajp13->channel == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "ajp13.init(): No channel %s\n",
+                      ajp13->mbean->localName);
+        /* That's ok - it may be added later */
+        /*         return JK_ERR; */
+    }
+
+    if (ajp13->route == NULL) {
+        /* Default - eventually the naming convention should become mandatory */
+        ajp13->route = bean->localName;
+    }
+
+    /* Find the groups we are member on and add ourself in
+     */
+    size = ajp13->groups->size(env, ajp13->groups);
+    if (size == 0) {
+        /* No explicit groups, it'll go to default lb */
+        jk_worker_t *lb = ajp13->workerEnv->defaultWorker;
+
+        lb->mbean->setAttribute(env, lb->mbean, "worker", ajp13->mbean->name);
+        if (ajp13->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "ajp13.init(): Adding %s to default lb\n",
+                          ajp13->mbean->localName);
+    }
+    else {
+        for (i = 0; i < size; i++) {
+            char *name = ajp13->groups->nameAt(env, ajp13->groups, i);
+            jk_worker_t *lb;
+
+            if (ajp13->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "ajp13.init(): Adding %s to %s\n",
+                              ajp13->mbean->localName, name);
+            if (strncmp(name, "lb:", 3) == 0) {
+                lb = env->getByName(env, name);
+                if (lb == NULL) {
+                    /* Create the lb group */
+                    if (ajp13->mbean->debug > 0)
+                        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                      "ajp13.init(): Automatically creating the group %s\n",
+                                      name);
+                    env->createBean(env, ajp13->workerEnv->mbean->pool, name);
+                    lb = env->getByName(env, name);
+                    if (lb == NULL) {
+                        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                                      "ajp13.init(): Failed to create %s\n",
+                                      name);
+                        return JK_ERR;
+                    }
+                }
+            }
+            else {
+                lb = env->getByName2(env, "lb", name);
+                if (lb == NULL) {
+                    /* Create the lb group */
+                    if (ajp13->mbean->debug > 0)
+                        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                      "ajp13.init(): Automatically creating the group %s\n",
+                                      name);
+                    env->createBean2(env, ajp13->workerEnv->mbean->pool, "lb",
+                                     name);
+                    lb = env->getByName2(env, "lb", name);
+                    if (lb == NULL) {
+                        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                                      "ajp13.init(): Failed to create %s\n",
+                                      name);
+                        return JK_ERR;
+                    }
+                }
+            }
+            lb->mbean->setAttribute(env, lb->mbean, "worker",
+                                    ajp13->mbean->name);
+        }
+
+    }
+
+    /* XXX Find any 'group' property - find or create an lb for that
+       and register it
+     */
+    return JK_OK;
+}
+
+
+static int JK_METHOD jk2_worker_ajp13_destroy(jk_env_t *env, jk_bean_t *bean)
+{
+    jk_worker_t *ajp13 = bean->object;
+    int i;
+
+    if (ajp13->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG, "ajp13.destroy()\n");
+
+    if (ajp13->endpointCache != NULL) {
+        jk_endpoint_t *e;
+        i = ajp13->endpointCache->count;
+        while (ajp13->endpointCache->count > 0) {
+
+            e = ajp13->endpointCache->get(env, ajp13->endpointCache);
+
+            if (e == NULL) {
+                /* we finished all endpoints in the cache */
+                break;
+            }
+
+
+            jk2_close_endpoint(env, e);
+        }
+        ajp13->endpointCache->destroy(env, ajp13->endpointCache);
+
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "ajp13.destroy() closed %d cached endpoints\n", i);
+    }
+
+    return JK_OK;
+}
+
+int JK_METHOD jk2_worker_ajp13_factory(jk_env_t *env, jk_pool_t *pool,
+                                       jk_bean_t *result,
+                                       const char *type, const char *name)
+{
+    jk_worker_t *w =
+        (jk_worker_t *)pool->calloc(env, pool, sizeof(jk_worker_t));
+    jk_bean_t *jkb;
+
+    if (name == NULL || w == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "ajp13.factory() NullPointerException\n");
+        return JK_ERR;
+    }
+
+    jk2_map_default_create(env, &w->groups, pool);
+    jk2_map_default_create(env, &w->endpointMap, pool);
+
+    jkb = env->createBean2(env, pool, "threadMutex", NULL);
+    if (jkb != NULL) {
+        w->cs = jkb->object;
+        jkb->init(env, jkb);
+    }
+
+    w->endpointCache = NULL;
+
+    w->channel = NULL;
+    w->secret = NULL;
+
+    w->lb_factor = 1;
+    w->graceful = 0;
+    w->service = jk2_worker_ajp13_service;
+
+    result->setAttribute = jk2_worker_ajp13_setAttribute;
+    result->getAttribute = jk2_worker_ajp13_getAttribute;
+    result->init = jk2_worker_ajp13_init;
+    result->destroy = jk2_worker_ajp13_destroy;
+
+    result->getAttributeInfo = jk2_worker_ajp13_getAttributeInfo;
+    result->multiValueInfo = jk2_worker_ajp13_multiValueInfo;
+    result->setAttributeInfo = jk2_worker_ajp13_setAttributeInfo;
+
+    result->object = w;
+    w->mbean = result;
+
+    w->workerEnv = env->getByName(env, "workerEnv");
+    w->workerEnv->addWorker(env, w->workerEnv, w);
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_worker_jni.c b/connectors/jk/native2/common/jk_worker_jni.c
new file mode 100644
index 0000000..0aa0afd
--- /dev/null
+++ b/connectors/jk/native2/common/jk_worker_jni.c
@@ -0,0 +1,504 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * In process JNI worker. It'll call an arbitrary java class's main method
+ * with given parameters and set a pointer to the java vm in the workerEnv.
+ * ( XXX and an optional set method to pass env, workerEnv pointers )
+ * 
+ * @author:  Gal Shachor <shachor@il.ibm.com>
+ * @author: Costin Manolache
+ */
+#include "jk_workerEnv.h"
+#include "jk_env.h"
+#include "jk_bean.h"
+
+#ifdef HAVE_JNI
+
+#include "apr_thread_proc.h"
+
+#include "jk_vm.h"
+#include "jk_registry.h"
+#include <jni.h>
+
+extern jint jk_jni_aprImpl_registerNatives(JNIEnv *, jclass);
+extern int jk_jni_status_code;
+
+/* default only, will be  configurable  */
+#define JAVA_BRIDGE_CLASS_NAME ("org/apache/jk/apr/TomcatStarter")
+#define JAVA_BRIDGE_CLASS_APRI ("org/apache/jk/apr/AprImpl")
+
+/* The class will be executed when the connector is started */
+#define JK2_WORKER_HOOK_STARTUP     0
+#define JK2_WORKER_HOOK_INIT        1
+#define JK2_WORKER_HOOK_CLOSE       2
+/* The class will be executed when the connector is about to be destroyed */
+#define JK2_WORKER_HOOK_SHUTDOWN    3
+
+struct jni_worker_data
+{
+    jclass jk_java_bridge_class;
+    jclass jk_java_bridge_apri_class;
+    jmethodID jk_main_method;
+    jmethodID jk_setout_method;
+    jmethodID jk_seterr_method;
+    char *className;
+    char *stdout_name;
+    char *stderr_name;
+    /* Hack to allow multiple 'options' for the class name */
+    char **classNameOptions;
+    char **args;
+    int nArgs;
+    int hook;
+};
+
+typedef struct jni_worker_data jni_worker_data_t;
+
+/** -------------------- Startup -------------------- */
+
+/** Static methods - much easier...
+ */
+static int jk2_get_method_ids(jk_env_t *env, jni_worker_data_t * p,
+                              JNIEnv * jniEnv)
+{
+    p->jk_main_method =
+        (*jniEnv)->GetStaticMethodID(jniEnv, p->jk_java_bridge_class,
+                                     "main", "([Ljava/lang/String;)V");
+    if (!p->jk_main_method) {
+        env->l->jkLog(env, env->l, JK_LOG_EMERG,
+                      "Can't find main(String [])\n");
+        return JK_ERR;
+    }
+    /* Only the startup hook can redirect the stdout/stderr */
+    if (p->hook == JK2_WORKER_HOOK_STARTUP) {
+        p->jk_setout_method =
+            (*jniEnv)->GetStaticMethodID(jniEnv, p->jk_java_bridge_apri_class,
+                                         "setOut", "(Ljava/lang/String;)V");
+        if (!p->jk_setout_method) {
+            env->l->jkLog(env, env->l, JK_LOG_EMERG,
+                          "Can't find AprImpl.setOut(String)");
+            return JK_ERR;
+        }
+
+        p->jk_seterr_method =
+            (*jniEnv)->GetStaticMethodID(jniEnv, p->jk_java_bridge_apri_class,
+                                         "setErr", "(Ljava/lang/String;)V");
+        if (!p->jk_seterr_method) {
+            env->l->jkLog(env, env->l, JK_LOG_EMERG,
+                          "Can't find AprImpl.setErr(String)\n");
+            return JK_ERR;
+        }
+    }
+
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_jni_worker_service(jk_env_t *env,
+                                            jk_worker_t *w,
+                                            jk_ws_service_t *s)
+{
+    return JK_ERR;
+}
+
+
+static int JK_METHOD jk2_jni_worker_setProperty(jk_env_t *env,
+                                                jk_bean_t *mbean, char *name,
+                                                void *valueP)
+{
+    jk_worker_t *_this = mbean->object;
+    char *value = valueP;
+    jni_worker_data_t *jniWorker;
+    int mem_config = 0;
+
+    if (!_this || !_this->worker_private) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "In validate, assert failed - invalid parameters\n");
+        return JK_ERR;
+    }
+
+    jniWorker = _this->worker_private;
+    if (strcmp(mbean->name, "worker.jni:onStartup") == 0)
+        jniWorker->hook = JK2_WORKER_HOOK_STARTUP;
+    else if (strncmp(mbean->name, "worker.jni:onInit",
+                     sizeof("worker.jni:onInit") - 1) == 0)
+        jniWorker->hook = JK2_WORKER_HOOK_INIT;
+    else if (strncmp(mbean->name, "worker.jni:onClose",
+                     sizeof("worker.jni:onClose") - 1) == 0)
+        jniWorker->hook = JK2_WORKER_HOOK_CLOSE;
+    else if (strcmp(mbean->name, "worker.jni:onShutdown") == 0)
+        jniWorker->hook = JK2_WORKER_HOOK_SHUTDOWN;
+
+    if (strcmp(name, "stdout") == 0) {
+        jniWorker->stdout_name = value;
+    }
+    else if (strcmp(name, "stderr") == 0) {
+        jniWorker->stderr_name = value;
+    }
+    else if (strcmp(name, "class") == 0) {
+        if (jniWorker->className != NULL) {
+            int i;
+            for (i = 0; i < 4; i++) {
+                if (jniWorker->classNameOptions[i] == NULL) {
+                    jniWorker->classNameOptions[i] = value;
+                }
+            }
+        }
+        else {
+            jniWorker->className = value;
+        }
+    }
+    else if (strcmp(name, "ARG") == 0) {
+        jniWorker->args[jniWorker->nArgs] = value;
+        jniWorker->nArgs++;
+    }
+    else {
+        return JK_ERR;
+    }
+
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_jni_worker_init(jk_env_t *env, jk_bean_t *bean)
+{
+    jk_worker_t *_this = bean->object;
+    jni_worker_data_t *jniWorker;
+    JNIEnv *jniEnv;
+    jstring cmd_line = NULL;
+    jstring stdout_name = NULL;
+    jstring stderr_name = NULL;
+    jint rc = 0;
+    char *str_config = NULL;
+    jk_map_t *props;
+    jk_vm_t *vm;
+    jclass jstringClass;
+    jarray jargs;
+    int i = 0;
+    if (!_this || !_this->worker_private) {
+        env->l->jkLog(env, env->l, JK_LOG_EMERG,
+                      "In init, assert failed - invalid parameters\n");
+        return JK_ERR;
+    }
+
+    vm = _this->workerEnv->vm;
+    if (vm == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "workerJni.init() No VM found\n");
+        return JK_ERR;
+    }
+
+    /* Allow only the first child to execute the worker */
+    if (_this->workerEnv->childId != 0) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "workerJni.Init() Skipping initialization for the %d %d\n",
+                      _this->workerEnv->childId,
+                      _this->workerEnv->childProcessId);
+        return JK_ERR;
+    }
+
+    props = _this->workerEnv->initData;
+    jniWorker = _this->worker_private;
+
+    if (jniWorker->className == NULL)
+        jniWorker->className = JAVA_BRIDGE_CLASS_NAME;
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "jni.validate() class= %s \n", jniWorker->className);
+
+    jniEnv = (JNIEnv *) vm->attach(env, vm);
+
+    if (jniEnv == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "workerJni.init() Can't attach to VM\n");
+        return JK_ERR;
+    }
+
+    if (jniWorker->stdout_name) {
+        stdout_name = (*jniEnv)->NewStringUTF(jniEnv, jniWorker->stdout_name);
+    }
+    if (jniWorker->stderr_name) {
+        stderr_name = (*jniEnv)->NewStringUTF(jniEnv, jniWorker->stderr_name);
+    }
+
+    jniWorker->jk_java_bridge_class =
+        (*jniEnv)->FindClass(jniEnv, jniWorker->className);
+
+    if (jniWorker->jk_java_bridge_class == NULL) {
+        int i;
+        for (i = 0; i < 4; i++) {
+            if (jniWorker->classNameOptions[i] != NULL) {
+                env->l->jkLog(env, env->l, JK_LOG_INFO,
+                              "jni.validate() try %s \n",
+                              jniWorker->classNameOptions[i]);
+                jniWorker->jk_java_bridge_class =
+                    (*jniEnv)->FindClass(jniEnv,
+                                         jniWorker->classNameOptions[i]);
+                if (jniWorker->jk_java_bridge_class != NULL)
+                    break;
+            }
+        }
+    }
+
+    if (jniWorker->jk_java_bridge_class == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "Can't find class %s\n", jniWorker->className);
+        /* [V] the detach here may segfault on 1.1 JVM... */
+        vm->detach(env, vm);
+        return JK_ERR;
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "Loaded %s\n", jniWorker->className);
+
+/* Instead of loading mod_jk2.so from java, use the JNI RegisterGlobals.
+ * XXX Need the way to customize JAVA_BRIDGE_CLASS_APRI, but since
+ * it's hardcoded in JniHandler.java doesn't matter for now.
+ */
+    jniWorker->jk_java_bridge_apri_class =
+        (*jniEnv)->FindClass(jniEnv, JAVA_BRIDGE_CLASS_APRI);
+
+    if (jniWorker->jk_java_bridge_apri_class == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "Can't find class %s\n", JAVA_BRIDGE_CLASS_APRI);
+        /* [V] the detach here may segfault on 1.1 JVM... */
+        vm->detach(env, vm);
+        return JK_ERR;
+    }
+
+    if (jniWorker->hook == JK2_WORKER_HOOK_STARTUP) {
+        rc = jk_jni_aprImpl_registerNatives(jniEnv,
+                                            jniWorker->
+                                            jk_java_bridge_apri_class);
+        if (rc != 0) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "Can't register native functions for %s \n",
+                          JAVA_BRIDGE_CLASS_APRI);
+            vm->detach(env, vm);
+            return JK_ERR;
+        }
+    }
+
+    rc = jk2_get_method_ids(env, jniWorker, jniEnv);
+    if (rc != JK_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_EMERG,
+                      "jniWorker: Fail-> can't get method ids\n");
+        /* [V] the detach here may segfault on 1.1 JVM... */
+        vm->detach(env, vm);
+        return rc;
+    }
+    /* Set out and err stadard files */
+    if (jniWorker->stdout_name && jniWorker->jk_setout_method) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jni.init() setting stdout=%s...\n",
+                      jniWorker->stdout_name);
+        (*jniEnv)->CallStaticVoidMethod(jniEnv,
+                                        jniWorker->jk_java_bridge_apri_class,
+                                        jniWorker->jk_setout_method,
+                                        stdout_name);
+    }
+
+    if (jniWorker->stderr_name && jniWorker->jk_seterr_method) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jni.init() setting stderr=%s...\n",
+                      jniWorker->stderr_name);
+        (*jniEnv)->CallStaticVoidMethod(jniEnv,
+                                        jniWorker->jk_java_bridge_apri_class,
+                                        jniWorker->jk_seterr_method,
+                                        stderr_name);
+    }
+
+    if (jniWorker->hook < JK2_WORKER_HOOK_CLOSE) {
+        jstringClass = (*jniEnv)->FindClass(jniEnv, "java/lang/String");
+
+        jargs =
+            (*jniEnv)->NewObjectArray(jniEnv, jniWorker->nArgs, jstringClass,
+                                      NULL);
+
+        for (i = 0; i < jniWorker->nArgs; i++) {
+            jstring arg = NULL;
+            if (jniWorker->args[i] != NULL) {
+                arg = (*jniEnv)->NewStringUTF(jniEnv, jniWorker->args[i]);
+                env->l->jkLog(env, env->l, JK_LOG_INFO,
+                              "jni.init() ARG %s\n", jniWorker->args[i]);
+            }
+            (*jniEnv)->SetObjectArrayElement(jniEnv, jargs, i, arg);
+        }
+
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jni.init() calling main()...\n");
+        (*jniEnv)->CallStaticVoidMethod(jniEnv,
+                                        jniWorker->jk_java_bridge_class,
+                                        jniWorker->jk_main_method, jargs);
+    }
+    else {
+        /* XXX Is it right thing to disable all non init hooks 
+         * or make that customizable.
+         */
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jni.init() disabling the non init hook worker\n");
+        _this->lb_disabled = JK_TRUE;
+    }
+#if 0
+    vm->detach(env, vm);
+#endif
+    /* XXX create a jni channel */
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_jni_worker_destroy(jk_env_t *env, jk_bean_t *bean)
+{
+    jk_worker_t *_this = bean->object;
+    jni_worker_data_t *jniWorker;
+    jk_vm_t *vm;
+    JNIEnv *jniEnv;
+    jstring cmd_line = NULL;
+    jstring stdout_name = NULL;
+    jstring stderr_name = NULL;
+    jclass jstringClass;
+    jarray jargs;
+    jstring arg = NULL;
+
+    if (!_this || !_this->worker_private) {
+        env->l->jkLog(env, env->l, JK_LOG_EMERG,
+                      "In destroy, assert failed - invalid parameters\n");
+        return JK_ERR;
+    }
+    vm = _this->workerEnv->vm;
+
+    if (vm == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "jni.destroy() No VM found\n");
+        return JK_ERR;
+    }
+
+    jniWorker = _this->worker_private;
+
+    if (jniWorker->hook < JK2_WORKER_HOOK_CLOSE) {
+        if (bean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "jni.destroy(), done...worker is not hooked for close\n");
+        return JK_OK;
+    }
+    if (jniWorker->jk_java_bridge_class == NULL ||
+        jniWorker->jk_main_method == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jni.destroy(), done...worker does not have java methods\n");
+        return JK_OK;
+    }
+    if ((jniEnv = vm->attach(env, vm))) {
+        int i;
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jni.destroy(), shutting down Tomcat...\n");
+
+        jstringClass = (*jniEnv)->FindClass(jniEnv, "java/lang/String");
+        jargs =
+            (*jniEnv)->NewObjectArray(jniEnv, jniWorker->nArgs, jstringClass,
+                                      NULL);
+        for (i = 0; i < jniWorker->nArgs; i++) {
+            jstring arg = NULL;
+            if (jniWorker->args[i] != NULL) {
+                arg = (*jniEnv)->NewStringUTF(jniEnv, jniWorker->args[i]);
+                env->l->jkLog(env, env->l, JK_LOG_INFO,
+                              "jni.init() ARG %s\n", jniWorker->args[i]);
+            }
+            (*jniEnv)->SetObjectArrayElement(jniEnv, jargs, i, arg);
+        }
+
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jni.destroy() calling main()...\n");
+
+        jk_jni_status_code = 0;
+        (*jniEnv)->CallStaticVoidMethod(jniEnv,
+                                        jniWorker->jk_java_bridge_class,
+                                        jniWorker->jk_main_method, jargs);
+        if (jniWorker->hook == JK2_WORKER_HOOK_SHUTDOWN) {
+#if APR_HAS_THREADS
+            while (jk_jni_status_code != 2) {
+                apr_thread_yield();
+            }
+#endif
+            vm->detach(env, vm);
+        }
+    }
+    env->l->jkLog(env, env->l, JK_LOG_INFO, "jni.destroy() done\n");
+
+    return JK_OK;
+}
+
+int JK_METHOD jk2_worker_jni_factory(jk_env_t *env, jk_pool_t *pool,
+                                     jk_bean_t *result,
+                                     const char *type, const char *name)
+{
+    jk_worker_t *_this;
+    jni_worker_data_t *jniData;
+    jk_workerEnv_t *wEnv;
+
+    if (name == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_EMERG,
+                      "jni.factory() NullPointerException name==null\n");
+        return JK_ERR;
+    }
+
+    wEnv = env->getByName(env, "workerEnv");
+
+    /* No singleton - you can have multiple jni workers,
+       running different bridges or starting different programs inprocess */
+
+    _this = (jk_worker_t *)pool->calloc(env, pool, sizeof(jk_worker_t));
+
+    jniData = (jni_worker_data_t *) pool->calloc(env, pool,
+                                                 sizeof(jni_worker_data_t));
+    if (_this == NULL || jniData == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "jni.factory() OutOfMemoryException \n");
+        return JK_ERR;
+    }
+
+    _this->worker_private = jniData;
+
+    jniData->jk_java_bridge_class = NULL;
+    jniData->classNameOptions =
+        (char **)pool->calloc(env, pool, 4 * sizeof(char *));
+
+    jniData->args = pool->calloc(env, pool, 64 * sizeof(char *));
+    jniData->nArgs = 0;
+    jniData->stdout_name = NULL;
+    jniData->stderr_name = NULL;
+
+    result->init = jk2_jni_worker_init;
+    result->destroy = jk2_jni_worker_destroy;
+    _this->service = jk2_jni_worker_service;
+
+    result->object = _this;
+    result->setAttribute = jk2_jni_worker_setProperty;
+    _this->mbean = result;
+
+    _this->workerEnv = wEnv;
+    _this->workerEnv->addWorker(env, _this->workerEnv, _this);
+
+    return JK_OK;
+}
+#else
+
+int JK_METHOD jk2_worker_jni_factory(jk_env_t *env, jk_pool_t *pool,
+                                     jk_bean_t *result,
+                                     const char *type, const char *name)
+{
+    result->disabled = 1;
+
+    return JK_OK;
+}
+
+#endif
diff --git a/connectors/jk/native2/common/jk_worker_lb.c b/connectors/jk/native2/common/jk_worker_lb.c
new file mode 100644
index 0000000..c0da99c
--- /dev/null
+++ b/connectors/jk/native2/common/jk_worker_lb.c
@@ -0,0 +1,714 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Load balancer worker, knows how to load balance among      *
+ *              several workers.                                           *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Costin Manolache
+ ***************************************************************************/
+
+#include "jk_pool.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+#include "jk_logger.h"
+#include "jk_config.h"
+#include "jk_env.h"
+#include "jk_requtil.h"
+
+#if APR_HAS_THREADS
+#include "apr_thread_proc.h"
+#endif
+
+#define DEFAULT_LB_FACTOR           (1.0)
+
+/* Time to wait before retry... XXX make it configurable*/
+#define WAIT_BEFORE_RECOVER 60
+
+#define MAX_ATTEMPTS 3
+
+#define NO_WORKER_MSG "The servlet container is temporary unavailable or being upgraded\n";
+
+#define STICKY_SESSION 1
+
+typedef struct
+{
+    struct jk_mutex *cs;
+    int attempts;
+    int recovery;
+    int timeout;
+    int sticky_session;
+    time_t error_time;
+
+} jk_worker_lb_private_t;
+
+/** Find the best worker. In process, check if timeout expired
+    for workers that failed in the past and give them another chance.
+
+    This will check the JSESSIONID and forward to the right worker
+    if in a session.
+
+    It'll also adjust the load balancing factors.
+*/
+static jk_worker_t *jk2_get_most_suitable_worker(jk_env_t *env,
+                                                 jk_worker_t *lb,
+                                                 jk_ws_service_t *s,
+                                                 int attempt)
+{
+    jk_worker_t *rc = NULL;
+    int lb_min = 0;
+    int lb_max = 0;
+    int i;
+    int j;
+    int level;
+    int currentLevel = JK_LB_LEVELS - 1;
+    char *session_route = NULL;
+    char *routeRedirect = NULL;
+    time_t now = 0;
+    jk_worker_lb_private_t *lb_priv = lb->worker_private;
+
+    if (lb_priv->sticky_session) {
+        session_route = jk2_requtil_getSessionRoute(env, s);
+    }
+
+    if (lb->mbean->debug > 0) {
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "lb.get_worker %d Session=%s\n",
+                      lb_priv->sticky_session,
+                      (session_route ? session_route : "NULL"));
+    }
+    if (session_route) {
+        for (level = 0; level < JK_LB_LEVELS; level++) {
+            for (i = 0; i < lb->workerCnt[level]; i++) {
+                jk_worker_t *w = lb->workerTables[level][i];
+
+                if (w->route != NULL && 0 == strcmp(session_route, w->route)) {
+                    if (w->routeRedirect != NULL) {
+                        /* Session was migrated to another worker */
+                        routeRedirect = w->routeRedirect;
+                        break;
+                    }
+
+                    if (attempt > 0 && w->in_error_state) {
+                        /* We already tried to revive this worker. */
+                        break;
+                    }
+                    else {
+                        return w;
+                    }
+                }
+            }
+        }
+    }
+    /* We have a session - but the worker is in error state
+       or has a "redirect".
+       Try the new worker.
+     */
+    if (routeRedirect != NULL) {
+        for (level = 0; level < JK_LB_LEVELS; level++) {
+            for (i = 0; i < lb->workerCnt[level]; i++) {
+                jk_worker_t *w = lb->workerTables[level][i];
+
+                if (w->route != NULL && 0 == strcmp(routeRedirect, w->route)) {
+                    if (attempt > 0 && w->in_error_state) {
+                        /* We already tried to revive this worker. */
+                        break;
+                    }
+                    else {
+                        return w;
+                    }
+                }
+            }
+        }
+    }
+
+    /** Get one worker that is ready
+     */
+    for (level = 0; level < JK_LB_LEVELS; level++) {
+        for (i = 0; i < lb->workerCnt[level]; i++) {
+            jk_worker_t *w = lb->workerTables[level][i];
+
+            if (w->mbean->disabled)
+                continue;
+            if (w->graceful)
+                continue;
+            if (w->in_error_state)
+                continue;
+            if (w->lb_disabled)
+                continue;
+
+            if (rc == NULL) {
+                rc = w;
+                currentLevel = level;
+                lb_min = w->lb_value;
+                continue;
+            }
+
+            if (w->lb_value < lb_min) {
+                lb_min = w->lb_value;
+                rc = w;
+                currentLevel = level;
+            }
+        }
+
+        if (rc != NULL) {
+            /* We found a valid worker on the current level, don't worry about the
+               higher levels.
+             */
+            break;
+        }
+
+        if (lb->hwBalanceErr > 0) {
+            /* don't go to higher levels - we'll return an error
+             */
+            currentLevel = 0;
+            break;
+        }
+    }
+
+    /** Reenable workers in error state if the timeout has passed.
+     *  Don't bother with 'higher' levels, since we'll never try them.
+     */
+    for (level = 0; level <= currentLevel; level++) {
+
+        for (i = 0; i < lb->workerCnt[level]; i++) {
+            jk_worker_t *w = lb->workerTables[level][i];
+
+            if (w->mbean->disabled)
+                continue;
+            if (w->lb_disabled)
+                continue;
+
+            if (w->in_error_state) {
+                /* Check if it's ready for recovery */
+                if (now == 0)
+                    now = time(NULL);
+
+                if ((now - w->error_time) > lb_priv->recovery) {
+                    env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                                  "lb.getWorker() reenable %s\n",
+                                  w->mbean->name);
+                    w->in_error_state = JK_FALSE;
+
+                    /* Find max lb */
+                    if (lb_max == 0) {
+                        for (j = 0; j < lb->workerCnt[level]; j++) {
+                            if (lb->workerTables[level][j]->lb_value > lb_max) {
+                                lb_max = lb->workerTables[level][j]->lb_value;
+                            }
+                        }
+                    }
+                    w->lb_value = lb_max;
+                }
+            }
+        }
+    }
+
+    /* If no active worker is found, we'll try all workers in error_state,
+     */
+    if (rc == NULL) {
+        /* no workers found (rc is null), now try as hard as possible to get a
+           worker anyway, pick one with largest error time.. */
+        int error_workers = 0;
+
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "lb.getWorker() All workers in error state, use the one with oldest error\n");
+
+        for (level = 0; level < JK_LB_LEVELS; level++) {
+            for (i = 0; i < lb->workerCnt[level]; i++) {
+                jk_worker_t *w = lb->workerTables[level][i];
+
+                if (w->mbean->disabled == JK_TRUE)
+                    continue;
+                if (w->graceful)
+                    continue;
+                if (w->lb_disabled)
+                    continue;
+
+                error_workers++;
+
+                if (rc == NULL) {
+                    rc = w;
+                    currentLevel = level;
+                    continue;
+                }
+                /* pick the oldest failed worker */
+                if (w->error_time < rc->error_time) {
+                    currentLevel = level;
+                    rc = w;
+                }
+            }
+
+            if (lb->hwBalanceErr > 0) {
+                /* Don't try higher levels, only level=0 */
+                break;
+            }
+        }
+
+        if (attempt >= error_workers) {
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "lb.getWorker() We tried all possible workers %d\n",
+                          attempt);
+            return NULL;
+
+        }
+    }
+
+    if (rc != NULL) {
+        /* It it's the default, it'll remain the default - we don't
+           increase the factor
+         */
+        rc->in_error_state = JK_FALSE;
+        if (rc->lb_value != 0) {
+            int newValue = rc->lb_value + rc->lb_factor;
+
+            if (newValue > 255) {
+                rc->lb_value = rc->lb_factor;
+                /* Roll over. This has 2 goals:
+                   - avoid the lb factor becoming too big, and give a chance to run to
+                   workers that were in error state ( I think it's cleaner than looking for "max" )
+                   - the actual lb_value will be 1 byte. Even on the craziest platform, that
+                   will be an atomic write. We do a lot of operations on lb_value in a MT environment,
+                   and the chance of reading something inconsistent is considerable. Since APR
+                   will not support atomic - and adding a CS would cost too much, this is actually
+                   a good solution.
+
+                   Note that lb_value is not used for anything critical - just to balance the load,
+                   the worst that may happen is having a worker stay idle for 255 requests.
+                 */
+                for (i = 0; i < lb->workerCnt[currentLevel]; i++) {
+                    jk_worker_t *w = lb->workerTables[currentLevel][i];
+                    w->lb_value = w->lb_factor;
+                }
+            }
+            else {
+                rc->lb_value = newValue;
+            }
+        }
+    }
+
+    return rc;
+}
+
+
+/** Get the best worker and forward to it.
+    Since we don't directly connect to anything, there's no
+    need for an endpoint.
+*/
+static int JK_METHOD jk2_lb_service(jk_env_t *env,
+                                    jk_worker_t *lb, jk_ws_service_t *s)
+{
+    int attempt = 0;
+    jk_workerEnv_t *wEnv = lb->workerEnv;
+    jk_worker_lb_private_t *lb_priv = lb->worker_private;
+    jk_worker_t *rec = NULL;
+
+    if (s == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "lb.service() NullPointerException\n");
+        return JK_ERR;
+    }
+
+    /* you can not recover on another load balancer */
+    s->realWorker = NULL;
+
+
+    /* Check for configuration updates
+     */
+    if (wEnv->shm != NULL && wEnv->shm->head != NULL) {
+        /* We have shm, let's check for updates. This is just checking one
+           memory location, no lock involved. It is possible we'll read it
+           while somebody else is changing - but that's ok, we just check for
+           equality.
+         */
+        /* We should check this periodically
+         */
+        if (wEnv->config->ver != wEnv->shm->head->lbVer) {
+            wEnv->config->update(env, wEnv->config, NULL);
+            wEnv->config->ver = wEnv->shm->head->lbVer;
+        }
+    }
+
+    /* Initialize here the recovery POST buffer */
+    s->reco_buf = jk2_msg_ajp_create(env, s->pool, 0);
+    s->reco_status = RECO_INITED;
+
+    while (1) {
+        int rc;
+
+        /* Since there is potential worker state change
+         * make the find best worker call thread safe 
+         */
+        if (lb_priv->cs != NULL)
+            lb_priv->cs->lock(env, lb_priv->cs);
+        rec = jk2_get_most_suitable_worker(env, lb, s, attempt);
+
+        if (lb_priv->cs != NULL)
+            lb_priv->cs->unLock(env, lb_priv->cs);
+        attempt++;
+
+        s->is_recoverable_error = JK_FALSE;
+
+        if (rec == NULL) {
+            /* NULL record, no more workers left ... */
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "lb_worker.service() all workers in error or disabled state\n");
+            /* set hwBalanceErr status */
+            if (lb->hwBalanceErr != 0) {
+                s->status = lb->hwBalanceErr;
+            }
+            else {
+                s->status = lb->noWorkerCode;   /* SERVICE_UNAVAILABLE is the default */
+            }
+
+            /* Don't touch headers if noErrorHeader set to TRUE, ie ErrorDocument in Apache via mod_alias */
+            if (!lb->noErrorHeader) {
+                /* XXX: I didn't understand the 302, since s->status is lb->hwBalanceErr or lb->noWorkerCode */
+                /* Both could be set in workers2.properties so ..... */
+                if (s->status == 302) {
+                    s->headers_out->put(env, s->headers_out,
+                                        "Location", lb->noWorkerMsg, NULL);
+                    s->head(env, s);
+                }
+                else {
+                    s->headers_out->put(env, s->headers_out,
+                                        "Content-Type", "text/html", NULL);
+                    s->head(env, s);
+                    s->jkprintf(env, s, lb->noWorkerMsg);
+                }
+            }
+            s->afterRequest(env, s);
+            lb_priv->error_time = time(NULL);
+            return JK_ERR;
+        }
+
+        if (lb->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "lb.service() try %s\n", rec->mbean->name);
+
+        if (rec->route == NULL) {
+            rec->route = rec->mbean->localName;
+        }
+
+        s->jvm_route = rec->route;
+
+        s->realWorker = rec;
+
+        rc = rec->service(env, rec, s);
+
+        if (rc == JK_OK) {
+            rec->in_error_state = JK_FALSE;
+            rec->error_time = 0;
+            return JK_OK;
+        }
+
+        /* If this is a browser connection error dont't check other
+         * workers.             
+         */
+        if (rc == JK_HANDLER_ERROR) {
+            rec->in_error_state = JK_FALSE;
+            rec->error_time = 0;
+            return JK_HANDLER_ERROR;
+        }
+
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "lb.service() worker failed %d for %s\n", rc,
+                      rec->mbean->name);
+
+        /*
+         * Service failed !!!
+         *
+         * Time for fault tolerance (if possible)...
+         */
+        rec->in_error_state = JK_TRUE;
+        rec->error_time = time(NULL);
+
+        if (!s->is_recoverable_error) {
+            /* Error is not recoverable - break with an error. */
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "lb.service() unrecoverable error...\n");
+            break;
+        }
+
+        /* 
+         * Error is recoverable by submitting the request to
+         * another worker... Lets try to do that.
+         */
+        if (lb->mbean->debug > 0) {
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "lb_worker.service() try other hosts\n");
+        }
+    }
+    return JK_ERR;
+}
+
+/** Init internal structures.
+    Called any time the config changes
+*/
+static int JK_METHOD jk2_lb_refresh(jk_env_t *env, jk_worker_t *lb)
+{
+    int i;
+    int num_of_workers = lb->lbWorkerMap->size(env, lb->lbWorkerMap);
+
+    for (i = 0; i < num_of_workers; i++) {
+        char *name = lb->lbWorkerMap->nameAt(env, lb->lbWorkerMap, i);
+        jk_worker_t *w = env->getByName(env, name);
+        int level = 0;
+        int pos;
+        int workerCnt;
+
+        if (w == NULL) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                          "lb_worker.init(): no worker found %s\n", name);
+            continue;
+        }
+
+        if (w->mbean->disabled)
+            continue;
+
+        level = w->level;
+
+        /* It's like disabled */
+        if (level >= JK_LB_LEVELS)
+            continue;
+
+        /* check if worker is already in the table */
+        workerCnt = lb->workerCnt[level];
+        for (pos = 0; pos < workerCnt; pos++) {
+            if (lb->workerTables[level][pos] == w) {
+                break;
+            }
+        }
+
+        if (pos == workerCnt) {
+            if (pos == JK_LB_MAX_WORKERS) {
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "lb_worker.init(): maximum lb workers reached %s\n",
+                              name);
+                continue;
+            }
+            pos = lb->workerCnt[level]++;
+
+            lb->workerTables[level][pos] = w;
+
+            w->lb_value = w->lb_factor;
+            w->in_error_state = JK_FALSE;
+        }
+    }
+
+    return JK_OK;
+}
+
+
+static char *jk2_worker_lb_multiValueInfo[] = { "worker", NULL };
+static char *jk2_worker_lb_setAttributeInfo[] =
+    { "attempts", "stickySession", "recovery", "timeout",
+    "hwBalanceErr", "noErrorHeader", "noWorkerMsg", "noWorkerCode", "worker",
+        NULL
+};
+
+static char *jk2_worker_lb_getAttributeInfo[] =
+    { "attempts", "stickySession", "recovery", "timeout",
+    "hwBalanceErr", "noErrorHeader", "noWorkerMsg", "noWorkerCode", "workers",
+        NULL
+};
+
+
+static void *JK_METHOD jk2_lb_getAttribute(jk_env_t *env, jk_bean_t *mbean,
+                                           char *name)
+{
+    jk_worker_t *lb = mbean->object;
+    jk_worker_lb_private_t *lb_priv = lb->worker_private;
+
+    if (strcmp(name, "workers") == 0) {
+        return jk2_map_concatKeys(env, lb->lbWorkerMap, ":");
+    }
+    else if (strcmp(name, "noWorkerMsg") == 0) {
+        return lb->noWorkerMsg;
+    }
+    else if (strcmp(name, "noWorkerCode") == 0) {
+        return jk2_env_itoa(env, lb->noWorkerCode);
+    }
+    else if (strcmp(name, "hwBalanceErr") == 0) {
+        return jk2_env_itoa(env, lb->hwBalanceErr);
+    }
+    else if (strcmp(name, "noErrorHeader") == 0) {
+        return jk2_env_itoa(env, lb->noErrorHeader);
+    }
+    else if (strcmp(name, "timeout") == 0) {
+        return jk2_env_itoa(env, lb_priv->timeout);
+    }
+    else if (strcmp(name, "recovery") == 0) {
+        return jk2_env_itoa(env, lb_priv->recovery);
+    }
+    else if (strcmp(name, "stickySession") == 0) {
+        return jk2_env_itoa(env, lb_priv->sticky_session);
+    }
+    else if (strcmp(name, "attempts") == 0) {
+        return jk2_env_itoa(env, lb_priv->attempts);
+    }
+    return NULL;
+}
+
+static int JK_METHOD jk2_lb_setAttribute(jk_env_t *env, jk_bean_t *mbean,
+                                         char *name, void *valueP)
+{
+    jk_worker_t *lb = mbean->object;
+    char *value = valueP;
+    jk_worker_lb_private_t *lb_priv = lb->worker_private;
+
+    if (strcmp(name, "worker") == 0) {
+        if (lb->lbWorkerMap->get(env, lb->lbWorkerMap, value) != NULL) {
+            /* Already added */
+            return JK_OK;
+        }
+        value = lb->mbean->pool->pstrdup(env, lb->mbean->pool, value);
+        lb->lbWorkerMap->add(env, lb->lbWorkerMap, value, "");
+
+        if (lb->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "lb_worker.setAttribute(): Adding to %s: %s\n",
+                          lb->mbean->localName, value);
+
+        jk2_lb_refresh(env, lb);
+        return JK_OK;
+    }
+    else if (strcmp(name, "noWorkerMsg") == 0) {
+        lb->noWorkerMsg = value;
+    }
+    else if (strcmp(name, "noWorkerCode") == 0) {
+        lb->noWorkerCode = atoi(value);
+    }
+    else if (strcmp(name, "hwBalanceErr") == 0) {
+        lb->hwBalanceErr = atoi(value);
+    }
+    else if (strcmp(name, "noErrorHeader") == 0) {
+        lb->noErrorHeader = atoi(value);
+    }
+    else if (strcmp(name, "timeout") == 0) {
+        lb_priv->timeout = atoi(value);
+    }
+    else if (strcmp(name, "recovery") == 0) {
+        lb_priv->recovery = atoi(value);
+    }
+    else if (strcmp(name, "stickySession") == 0) {
+        lb_priv->sticky_session = atoi(value);
+    }
+    else if (strcmp(name, "attempts") == 0) {
+        lb_priv->attempts = atoi(value);
+    }
+    return JK_ERR;
+}
+
+
+static int JK_METHOD jk2_lb_init(jk_env_t *env, jk_bean_t *bean)
+{
+    jk_worker_t *lb = bean->object;
+    int err;
+    int i = 0;
+    int num_of_workers = lb->lbWorkerMap->size(env, lb->lbWorkerMap);
+
+    err = jk2_lb_refresh(env, lb);
+    if (err != JK_OK)
+        return err;
+
+    /*     if( lb->workerEnv->shm != NULL && lb->workerEnv->shm->head != NULL)  */
+    /*         jk2_lb_updateWorkers(env, lb, lb->workerEnv->shm); */
+    if (lb->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG, "lb.init() %s %d workers\n",
+                      lb->mbean->name, num_of_workers);
+
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_lb_destroy(jk_env_t *env, jk_bean_t *bean)
+{
+    /* Workers are destroyed by the workerEnv. It is possible
+       that a worker is part of more than a lb.
+       Nothing to clean up so far.
+     */
+    return JK_OK;
+}
+
+
+int JK_METHOD jk2_worker_lb_factory(jk_env_t *env, jk_pool_t *pool,
+                                    jk_bean_t *result, char *type, char *name)
+{
+    jk_worker_t *w;
+    int i;
+    jk_bean_t *jkb;
+    jk_worker_lb_private_t *worker_private;
+
+    if (NULL == name) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "lb_worker.factory() NullPointerException\n");
+        return JK_ERR;
+    }
+
+    w = (jk_worker_t *)pool->calloc(env, pool, sizeof(jk_worker_t));
+
+    if (w == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "lb_worker.factory() OutOfMemoryException\n");
+        return JK_ERR;
+    }
+
+    jkb = env->createBean2(env, pool, "threadMutex", NULL);
+    if (jkb != NULL) {
+        w->cs = jkb->object;
+        jkb->init(env, jkb);
+    }
+
+    worker_private = (jk_worker_lb_private_t *) pool->calloc(env,
+                                                             pool,
+                                                             sizeof
+                                                             (jk_worker_lb_private_t));
+
+    jkb = env->createBean2(env, pool, "threadMutex", NULL);
+    if (jkb != NULL) {
+        worker_private->cs = jkb->object;
+        jkb->init(env, jkb);
+    }
+    worker_private->attempts = MAX_ATTEMPTS;
+    worker_private->recovery = WAIT_BEFORE_RECOVER;
+    worker_private->sticky_session = STICKY_SESSION;
+    w->worker_private = worker_private;
+    w->service = jk2_lb_service;
+
+    for (i = 0; i < JK_LB_LEVELS; i++) {
+        w->workerCnt[i] = 0;
+    }
+
+    jk2_map_default_create(env, &w->lbWorkerMap, pool);
+
+    result->init = jk2_lb_init;
+    result->destroy = jk2_lb_destroy;
+    result->setAttribute = jk2_lb_setAttribute;
+    result->multiValueInfo = jk2_worker_lb_multiValueInfo;
+    result->setAttributeInfo = jk2_worker_lb_setAttributeInfo;
+    result->object = w;
+    w->mbean = result;
+
+    w->hwBalanceErr = 0;
+    w->noWorkerCode = 503;
+    w->noWorkerMsg = NO_WORKER_MSG;
+    /* Let Apache handle the error via ErrorDocument and mod_alias */
+    w->noErrorHeader = 1;
+    w->workerEnv = env->getByName(env, "workerEnv");
+    w->workerEnv->addWorker(env, w->workerEnv, w);
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_worker_run.c b/connectors/jk/native2/common/jk_worker_run.c
new file mode 100644
index 0000000..19e22ab
--- /dev/null
+++ b/connectors/jk/native2/common/jk_worker_run.c
@@ -0,0 +1,83 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Run worker. It'll execute a process and monitor it. Derived from 
+ * the good old jserv.
+ *
+ * 
+ *
+ * @author Costin Manolache
+ */
+
+#include "jk_pool.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+#include "jk_logger.h"
+#include "jk_env.h"
+#include "jk_requtil.h"
+#include "jk_registry.h"
+
+static int JK_METHOD jk2_worker_run_service(jk_env_t *env, jk_worker_t *_this,
+                                            jk_ws_service_t *s)
+{
+    /* I should display a status page for the monitored processes
+     */
+    env->l->jkLog(env, env->l, JK_LOG_INFO, "run.service()\n");
+
+    /* Generate the header */
+    s->status = 500;
+    s->msg = "Not supported";
+    s->headers_out->put(env, s->headers_out,
+                        "Content-Type", "text/html", NULL);
+
+    s->head(env, s);
+
+    s->afterRequest(env, s);
+    return JK_OK;
+}
+
+
+int JK_METHOD jk2_worker_run_factory(jk_env_t *env, jk_pool_t *pool,
+                                     jk_bean_t *result,
+                                     const char *type, const char *name)
+{
+    jk_worker_t *_this;
+
+    if (NULL == name) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "run_worker.factory() NullPointerException\n");
+        return JK_ERR;
+    }
+
+    _this = (jk_worker_t *)pool->calloc(env, pool, sizeof(jk_worker_t));
+
+    if (_this == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "run_worker.factory() OutOfMemoryException\n");
+        return JK_ERR;
+    }
+
+    _this->service = jk2_worker_run_service;
+
+    result->object = _this;
+    _this->mbean = result;
+
+    _this->workerEnv = env->getByName(env, "workerEnv");
+    _this->workerEnv->addWorker(env, _this->workerEnv, _this);
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/common/jk_worker_status.c b/connectors/jk/native2/common/jk_worker_status.c
new file mode 100644
index 0000000..4a7651e
--- /dev/null
+++ b/connectors/jk/native2/common/jk_worker_status.c
@@ -0,0 +1,1313 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Status worker. It'll not connect to tomcat, but just generate response
+ * itself, containing a simple xhtml page with the current jk info.
+ *
+ * Note that the html tags are using 'class' attribute. Someone with some
+ * color taste can do a nice CSS and display it nicely, but more important is
+ * that it should be easy to grep/xpath it programmatically.
+ * 
+ * @author Costin Manolache
+ */
+
+#include "jk_pool.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+#include "jk_logger.h"
+#include "jk_env.h"
+#include "jk_requtil.h"
+#include "jk_registry.h"
+#include "jk_endpoint.h"
+
+#define JK_CHECK_NULL( str ) ( ((str)==NULL) ? "null" : (str) )
+
+#define DEFAULT_CSS ("BODY {COLOR: #000000; FONT-STYLE: normal; FONT-FAMILY: \"Times New Roman\", Times, serif; BACKGROUND-COLOR: #ffffff} H1 { COLOR: #0033cc; FONT-FAMILY: Arial, Helvetica, sans-serif} H2 { COLOR: #0033cc; FONT-FAMILY: Arial, Helvetica, sans-serif} H3 {FONT: 110% Arial, Helvetica, sans-serif; COLOR: #0033cc} B {FONT-WEIGHT: bold}" )
+
+/** Display info for one endpoint
+ */
+static void jk2_worker_status_displayStat(jk_env_t *env, jk_ws_service_t *s,
+                                          jk_stat_t *stat,
+                                          int *totalReqP, int *totalErrP,
+                                          unsigned long *totalTimeP,
+                                          unsigned long *maxTimeP)
+{
+    int totalReq = *totalReqP;
+    int totalErr = *totalErrP;
+    unsigned long totalTime = *totalTimeP;
+    unsigned long maxTime = *maxTimeP;
+    char ctimeBuf[APR_CTIME_LEN];
+
+    s->jkprintf(env, s, "<tr><td>%d</td><td>%d</td><td>%d</td>\n",
+                stat->workerId, stat->reqCnt, stat->errCnt);
+    s->jkprintf(env, s, "<td>%s</td>\n", JK_CHECK_NULL(stat->active));
+
+    totalReq += stat->reqCnt;
+    totalErr += stat->errCnt;
+
+    apr_ctime(ctimeBuf, stat->connectedTime);
+    s->jkprintf(env, s, "<td>%s</td>\n", ctimeBuf);
+
+    s->jkprintf(env, s, "<td>%ld</td>\n",
+                (long)apr_time_as_msec(stat->totalTime));
+    s->jkprintf(env, s, "<td>%ld</td>\n",
+                (long)apr_time_as_msec(stat->maxTime));
+
+    if (stat->reqCnt + stat->errCnt > 0)
+        s->jkprintf(env, s, "<td>%ld</td>\n",
+                    (long)
+                    apr_time_as_msec((stat->totalTime /
+                                      (stat->reqCnt + stat->errCnt))));
+    else
+        s->jkprintf(env, s, "<td>-</td>\n");
+
+    apr_ctime(ctimeBuf, stat->startTime);
+    s->jkprintf(env, s, "<td>%s</td>\n", ctimeBuf);
+    s->jkprintf(env, s, "<td>%ld</td>\n",
+                (long)apr_time_as_msec(stat->jkStartTime - stat->startTime));
+    s->jkprintf(env, s, "<td>%ld</td>\n",
+                (long)apr_time_as_msec(stat->endTime - stat->startTime));
+
+    totalTime += (long)stat->totalTime;
+    if (maxTime < stat->maxTime)
+        maxTime = (long)stat->maxTime;
+    s->jkprintf(env, s, "</tr>\n");
+
+    *maxTimeP = maxTime;
+    *totalTimeP = totalTime;
+    *totalReqP = totalReq;
+    *totalErrP = totalErr;
+}
+
+/** Displays the totals
+ */
+static void jk2_worker_status_displayAggregate(jk_env_t *env,
+                                               jk_ws_service_t *s,
+                                               int totalReq, int totalErr,
+                                               unsigned long totalTime,
+                                               unsigned long maxTime)
+{
+    s->jkprintf(env, s, "Totals:\n");
+
+    s->jkprintf(env, s,
+                "<table border><tr><th>Req</th><th>Err</th><th>Max</th><th>Avg</th></tr>");
+
+    s->jkprintf(env, s, "<tr><td>%d</td>\n", totalReq);
+    s->jkprintf(env, s, "<td>%d</td>\n", totalErr);
+
+    s->jkprintf(env, s, "<td>%ld</td>\n", apr_time_as_msec(maxTime));
+
+    if (totalErr + totalReq > 0) {
+        unsigned long avg =
+            apr_time_as_msec(totalTime / (totalReq + totalErr));
+        s->jkprintf(env, s, "<td>%ld</td>\n", avg);
+    }
+    else {
+        s->jkprintf(env, s, "<td>-</td>\n");
+    }
+
+    s->jkprintf(env, s, "</tr></table>\n");
+}
+
+/** Information for the internal endpoints ( in this process ).
+    Duplicates info from the scoreboard. This is used for debugging and for
+    cases where scoreboard is not available. No longer included in the
+    normal display to avoid confusion.
+ */
+static void jk2_worker_status_displayEndpointInfo(jk_env_t *env,
+                                                  jk_ws_service_t *s,
+                                                  jk_workerEnv_t *wenv)
+{
+    int i;
+    int totalReq = 0;
+    int totalErr = 0;
+    unsigned long totalTime = 0;
+    unsigned long maxTime = 0;
+
+    s->jkprintf(env, s, "<h2>Endpoint info ( no shm )</h2>\n");
+
+    s->jkprintf(env, s, "<table border>\n");
+
+    s->jkprintf(env, s, "<tr><th>Worker</th><th>Req</th><th>Err</th>");
+    s->jkprintf(env, s, "<th>LastReq</th>\n");
+    s->jkprintf(env, s,
+                "<th>ConnectionTime</th><th>TotalTime</th><th>MaxTime</th><th>AvgTime</th>");
+    s->jkprintf(env, s, "<th>ReqStart</th><th>+jk</th><th>+end</th>");
+
+    for (i = 0; i < env->_objects->size(env, env->_objects); i++) {
+        jk_bean_t *mbean = env->_objects->valueAt(env, env->_objects, i);
+        jk_endpoint_t *ep;
+
+        if (mbean == NULL)
+            continue;
+
+        if (strncmp("endpoint", mbean->type, 8) != 0)
+            continue;
+
+        ep = mbean->object;
+        if (ep->stats != NULL) {
+            jk2_worker_status_displayStat(env, s, ep->stats,
+                                          &totalReq, &totalErr, &totalTime,
+                                          &maxTime);
+        }
+    }
+    s->jkprintf(env, s, "</table>\n");
+
+    jk2_worker_status_displayAggregate(env, s,
+                                       totalReq, totalErr, totalTime,
+                                       maxTime);
+}
+
+/** Special case for endpoints - the info is stored in a shm segment, to be able
+ *   to access info from all other processes in a multi-process server.
+ *  For single process servers - the scoreboard is just a local char*.
+ */
+static void jk2_worker_status_displayScoreboardInfo(jk_env_t *env,
+                                                    jk_ws_service_t *s,
+                                                    jk_workerEnv_t *wenv)
+{
+    int i;
+    int j;
+    int totalReq = 0;
+    int totalErr = 0;
+    unsigned long totalTime = 0;
+    unsigned long maxTime = 0;
+
+    if (wenv->shm == NULL || wenv->shm->head == NULL) {
+        s->jkprintf(env, s, "<h3>No Scoreboard available</h3>\n");
+        return;
+    }
+
+    s->jkprintf(env, s, "<h2>Scoreboard info (ver=%d slots=%d)</h2>\n",
+                wenv->shm->head->lbVer, wenv->shm->head->lastSlot);
+
+    s->jkprintf(env, s, "<a href='jkstatus?scoreboard.reset'>reset</a>\n");
+
+    s->jkprintf(env, s, "<table border>\n");
+
+    for (i = 0; i < wenv->shm->head->lastSlot; i++) {
+        jk_shm_slot_t *slot = wenv->shm->getSlot(env, wenv->shm, i);
+
+        if (slot == NULL)
+            continue;
+
+        if (strncmp(slot->name, "epStat", 6) == 0) {
+            /* This is an endpoint slot */
+            void *data = slot->data;
+
+            s->jkprintf(env, s, "<tr><th colspan='4'>%s</th>\n",
+                        JK_CHECK_NULL(slot->name));
+            s->jkprintf(env, s, "<th>Cnt=%d</th><th>size=%d</th>\n",
+                        slot->structCnt, slot->structSize);
+
+            s->jkprintf(env, s,
+                        "<tr><th>Worker</th><th>Req</th><th>Err</th>");
+            s->jkprintf(env, s, "<th>LastReq</th>\n");
+            s->jkprintf(env, s,
+                        "<th>ConnectionTime</th><th>TotalTime</th><th>MaxTime</th><th>AvgTime</th>");
+            s->jkprintf(env, s, "<th>ReqStart</th><th>+jk</th><th>+end</th>");
+
+            /* XXX Add info about number of slots */
+            for (j = 0; j < slot->structCnt; j++) {
+                jk_stat_t *statArray = (jk_stat_t *)data;
+                jk_stat_t *stat = statArray + j;
+
+                jk2_worker_status_displayStat(env, s, stat,
+                                              &totalReq, &totalErr,
+                                              &totalTime, &maxTime);
+            }
+
+        }
+    }
+    s->jkprintf(env, s, "</table>\n");
+
+    jk2_worker_status_displayAggregate(env, s,
+                                       totalReq, totalErr, totalTime,
+                                       maxTime);
+}
+
+/** Use 'introspection' data to find what getters an type support,
+ *  and display the information in a table
+ */
+static void jk2_worker_status_displayRuntimeType(jk_env_t *env,
+                                                 jk_ws_service_t *s,
+                                                 jk_workerEnv_t *wenv,
+                                                 char *type)
+{
+    int i;
+    int needHeader = JK_TRUE;
+    int typeLen = strlen(type);
+
+    for (i = 0; i < env->_objects->size(env, env->_objects); i++) {
+        char *name = env->_objects->nameAt(env, env->_objects, i);
+        jk_bean_t *mbean = env->_objects->valueAt(env, env->_objects, i);
+        int j;
+
+        /* Don't display aliases */
+        if (strchr(name, ':') == NULL)
+            continue;
+
+        if (mbean == NULL || mbean->getAttributeInfo == NULL)
+            continue;
+
+        if (mbean->getAttribute == NULL)
+            continue;
+
+        if (strncmp(type, mbean->type, typeLen) != 0)
+            continue;
+
+        if (mbean->localName == NULL || (strlen(mbean->localName) == 0))
+            continue;
+
+        if (needHeader) {
+            s->jkprintf(env, s, "<H3>%s runtime info</H3>\n",
+                        JK_CHECK_NULL(type));
+            s->jkprintf(env, s,
+                        "<p>%s information, using getAttribute() </p>\n",
+                        JK_CHECK_NULL(type));
+
+            s->jkprintf(env, s, "<table border>\n");
+            s->jkprintf(env, s, "<tr><th>id</th>\n");
+            s->jkprintf(env, s, "<th>name</th>\n");
+            for (j = 0; mbean->getAttributeInfo[j] != NULL; j++) {
+                char *pname = mbean->getAttributeInfo[j];
+
+                s->jkprintf(env, s, "<th>%s</th>", JK_CHECK_NULL(pname));
+            }
+            needHeader = JK_FALSE;
+        }
+
+        s->jkprintf(env, s, "</tr><tr><td>%d</td><td>%s</td>\n", mbean->id,
+                    JK_CHECK_NULL(mbean->localName));
+        for (j = 0; mbean->getAttributeInfo[j] != NULL; j++) {
+            char *pname = mbean->getAttributeInfo[j];
+            char *res = mbean->getAttribute(env, mbean, pname);
+
+            s->jkprintf(env, s, "<td>%s</td>", JK_CHECK_NULL(res));
+        }
+    }
+    if (!needHeader) {
+        s->jkprintf(env, s, "</table>\n");
+    }
+}
+
+static void jk2_worker_status_resetScoreboard(jk_env_t *env,
+                                              jk_ws_service_t *s,
+                                              jk_workerEnv_t *wenv)
+{
+    int i, j;
+
+    if (wenv->shm == NULL || wenv->shm->head == NULL) {
+        return;
+    }
+
+    for (i = 0; i < wenv->shm->head->lastSlot; i++) {
+        jk_shm_slot_t *slot = wenv->shm->getSlot(env, wenv->shm, i);
+
+        if (slot == NULL)
+            continue;
+
+        if (strncmp(slot->name, "epStat", 6) == 0) {
+            /* This is an endpoint slot */
+            void *data = slot->data;
+
+            for (j = 0; j < slot->structCnt; j++) {
+                jk_stat_t *statArray = (jk_stat_t *)data;
+                jk_stat_t *stat = statArray + j;
+
+                stat->reqCnt = 0;
+                stat->errCnt = 0;
+                stat->totalTime = 0;
+                stat->maxTime = 0;
+            }
+        }
+    }
+}
+
+/** That's the 'bulk' data - everything that was configured, after substitution
+ */
+static void jk2_worker_status_displayActiveProperties(jk_env_t *env,
+                                                      jk_ws_service_t *s,
+                                                      jk_workerEnv_t *wenv)
+{
+    jk_map_t *map = wenv->initData;
+    int i;
+
+    s->jkprintf(env, s, "<H3>Processed config</H3>\n");
+    s->jkprintf(env, s,
+                "<p>All settings ( automatic and configured ), after substitution</p>\n");
+
+    s->jkprintf(env, s, "<table border>\n");
+    s->jkprintf(env, s, "<tr><th>Name</th><th>Value</td></tr>\n");
+    for (i = 0; i < map->size(env, map); i++) {
+        char *name = map->nameAt(env, map, i);
+        char *value = (char *)map->valueAt(env, map, i);
+        s->jkprintf(env, s, "<tr><td>%s</td><td>%s</td></tr>",
+                    JK_CHECK_NULL(name), JK_CHECK_NULL(value));
+    }
+    s->jkprintf(env, s, "</table>\n");
+}
+
+/** persistent configuration
+ */
+static void jk2_worker_status_displayConfigProperties(jk_env_t *env,
+                                                      jk_ws_service_t *s,
+                                                      jk_workerEnv_t *wenv)
+{
+    int i;
+
+    s->jkprintf(env, s, "<H3>Configured Properties</H3>\n");
+    s->jkprintf(env, s, "<p>Original data set by user</p>\n");
+    s->jkprintf(env, s, "<table border>\n");
+    s->jkprintf(env, s,
+                "<tr><th>Object name</th><th>Property</th><th>Value</td></tr>\n");
+
+    for (i = 0; i < env->_objects->size(env, env->_objects); i++) {
+        char *name = env->_objects->nameAt(env, env->_objects, i);
+        jk_bean_t *mbean = env->_objects->valueAt(env, env->_objects, i);
+        int j;
+        int propCount;
+
+        /* Don't display aliases */
+        if (strchr(name, ':') == NULL)
+            continue;
+
+        if (mbean == NULL || mbean->settings == NULL)
+            continue;
+
+        propCount = mbean->settings->size(env, mbean->settings);
+
+        if (propCount == 0) {
+            s->jkprintf(env, s, "<tr><th>%s</th><td></td></tr>",
+                        JK_CHECK_NULL(mbean->name));
+        }
+        else {
+            s->jkprintf(env, s,
+                        "<tr><th rowspan='%d'>%s</th><td>%s</td><td>%s</td></tr>",
+                        propCount, JK_CHECK_NULL(mbean->name),
+                        JK_CHECK_NULL(mbean->settings->
+                                      nameAt(env, mbean->settings, 0)),
+                        JK_CHECK_NULL(mbean->settings->
+                                      valueAt(env, mbean->settings, 0)));
+            for (j = 1; j < propCount; j++) {
+                char *pname =
+                    mbean->settings->nameAt(env, mbean->settings, j);
+                /* Don't save redundant information */
+                if (strcmp(pname, "name") != 0) {
+                    s->jkprintf(env, s, "<tr><td>%s</td><td>%s</td></tr>",
+                                JK_CHECK_NULL(pname),
+                                JK_CHECK_NULL(mbean->settings->
+                                              valueAt(env, mbean->settings,
+                                                      j)));
+                }
+            }
+        }
+    }
+    s->jkprintf(env, s, "</table>\n");
+}
+
+/** List metadata for endpoints ( from scoreboard ) using the remote-jmx style
+  */
+static void jk2_worker_status_lstEndpoints(jk_env_t *env, jk_ws_service_t *s,
+                                           jk_workerEnv_t *wenv)
+{
+    int i;
+    int j;
+
+    if (wenv->shm == NULL || wenv->shm->head == NULL) {
+        return;
+    }
+
+    for (i = 1; i < wenv->shm->head->lastSlot; i++) {
+        jk_shm_slot_t *slot = wenv->shm->getSlot(env, wenv->shm, i);
+
+        if (slot == NULL)
+            continue;
+
+        if (strncmp(slot->name, "epStat", 6) == 0) {
+            /* This is an endpoint slot */
+            void *data = slot->data;
+            char *name = JK_CHECK_NULL(slot->name);
+
+            for (j = 0; j < slot->structCnt; j++) {
+                jk_stat_t *statArray = (jk_stat_t *)data;
+
+                s->jkprintf(env, s, "[endpoint:%s%d]\n", name, j);
+                s->jkprintf(env, s, "T=endpoint\n");
+
+                s->jkprintf(env, s, "G=id\n");
+                s->jkprintf(env, s, "G=workerId\n");
+                s->jkprintf(env, s, "G=requests\n");
+                s->jkprintf(env, s, "G=errors\n");
+                s->jkprintf(env, s, "G=lastRequest\n");
+                s->jkprintf(env, s, "G=lastConnectionTime\n");
+                s->jkprintf(env, s, "G=totalTime\n");
+                s->jkprintf(env, s, "G=maxTime\n");
+                s->jkprintf(env, s, "G=requestStart\n");
+                s->jkprintf(env, s, "G=jkTime\n");
+                s->jkprintf(env, s, "G=requestEnd\n\n");
+            }
+        }
+    }
+}
+
+
+/** List values for endpoints ( from scoreboard ) using the remote-jmx style
+  */
+static void jk2_worker_status_dmpEndpoints(jk_env_t *env, jk_ws_service_t *s,
+                                           jk_workerEnv_t *wenv)
+{
+    int i;
+    int j;
+
+    if (wenv->shm == NULL || wenv->shm->head == NULL) {
+        return;
+    }
+
+    for (i = 1; i < wenv->shm->head->lastSlot; i++) {
+        jk_shm_slot_t *slot = wenv->shm->getSlot(env, wenv->shm, i);
+
+        if (slot == NULL)
+            continue;
+
+        if (strncmp(slot->name, "epStat", 6) == 0) {
+            /* This is an endpoint slot */
+            void *data = slot->data;
+            char *name = JK_CHECK_NULL(slot->name);
+            char ctimeBuf[APR_CTIME_LEN];
+
+            /* XXX Add info about number of slots */
+            for (j = 0; j < slot->structCnt; j++) {
+                jk_stat_t *statArray = (jk_stat_t *)data;
+
+                jk_stat_t *stat = statArray + j;
+                s->jkprintf(env, s, "[endpoint:%s%d]\n", name, j);
+
+                s->jkprintf(env, s, "workerId=%d\n", stat->workerId);
+                s->jkprintf(env, s, "id=%d\n", stat->workerId);
+                s->jkprintf(env, s, "requests=%d\n", stat->reqCnt);
+                s->jkprintf(env, s, "errors=%d\n", stat->errCnt);
+                s->jkprintf(env, s, "lastRequest=%s\n",
+                            JK_CHECK_NULL(stat->active));
+                apr_ctime(ctimeBuf, stat->connectedTime);
+                s->jkprintf(env, s, "lastConnectionTime=%s\n", ctimeBuf);
+
+                s->jkprintf(env, s, "totalTime=%ld\n",
+                            (long)apr_time_as_msec(stat->totalTime));
+                s->jkprintf(env, s, "maxTime=%ld\n",
+                            (long)apr_time_as_msec(stat->maxTime));
+
+                s->jkprintf(env, s, "requestStart=%lu\n",
+                            (long)apr_time_as_msec(stat->startTime));
+                s->jkprintf(env, s, "jkTime=%ld\n",
+                            (long)
+                            apr_time_as_msec((stat->jkStartTime -
+                                              stat->startTime)));
+                s->jkprintf(env, s, "requestEnd=%ld\n",
+                            (long)
+                            apr_time_as_msec((stat->endTime -
+                                              stat->startTime)));
+                s->jkprintf(env, s, "\n");
+            }
+
+        }
+    }
+}
+
+static int JK_METHOD jk2_worker_status_list(jk_env_t *env,
+                                            jk_worker_t *w,
+                                            jk_ws_service_t *s)
+{
+    char *cName = s->query_string + 4;
+    int i;
+    int qryLen = 0;
+    int exact = 1;
+
+    /* Dump all attributes for the beans */
+    if (strcmp(cName, "*") == 0) {
+        *cName = '\0';
+        qryLen = 0;
+    }
+    else {
+        qryLen = strlen(cName);
+    }
+    if (qryLen > 0) {
+        if (cName[strlen(cName) - 1] == '*') {
+/*             printf("Exact match off %s\n", cName ); */
+            cName[strlen(cName) - 1] = '\0';
+            exact = 0;
+            qryLen--;
+        }
+    }
+    for (i = 0; i < env->_objects->size(env, env->_objects); i++) {
+        char *name = env->_objects->nameAt(env, env->_objects, i);
+        jk_bean_t *mbean = env->_objects->valueAt(env, env->_objects, i);
+        char **getAtt = mbean->getAttributeInfo;
+        char **setAtt = mbean->setAttributeInfo;
+
+        /* That's a bad name, created for backward compat. It should be deprecated.. */
+        if (strchr(name, ':') == NULL)
+            continue;
+
+        /* Prefix */
+        if ((!exact) && qryLen != 0 && strncmp(name, cName, qryLen) != 0)
+            continue;
+
+        /* Endpoints are treated specially ( scoreboard to get the ep from other
+           processes */
+        if (strncmp("endpoint", mbean->type, 8) == 0)
+            continue;
+
+        if (strncmp("threadMutex", mbean->type, 11) == 0)
+            continue;
+
+        /* Exact */
+        if (exact && qryLen != 0 && strcmp(name, cName) != 0)
+            continue;
+
+        if (mbean == NULL)
+            continue;
+        s->jkprintf(env, s, "[%s]\n", name);
+        s->jkprintf(env, s, "T=%s\n", mbean->type);
+
+        s->jkprintf(env, s, "G=ver\n");
+        s->jkprintf(env, s, "G=disabled\n");
+        s->jkprintf(env, s, "G=debug\n");
+        while (getAtt != NULL && *getAtt != NULL && **getAtt != '\0') {
+            if (strcmp(*getAtt, "ver") == 0 ||
+                strcmp(*getAtt, "debug") == 0 ||
+                strcmp(*getAtt, "disabled") == 0) {
+                getAtt++;
+                continue;
+            }
+            s->jkprintf(env, s, "G=%s\n", *getAtt);
+            getAtt++;
+        }
+
+        s->jkprintf(env, s, "S=ver\n");
+        s->jkprintf(env, s, "S=disabled\n");
+        s->jkprintf(env, s, "S=debug\n");
+        while (setAtt != NULL && *setAtt != NULL && **setAtt != '\0') {
+            s->jkprintf(env, s, "S=%s\n", *setAtt);
+            setAtt++;
+        }
+        s->jkprintf(env, s, "M=init\n");
+        s->jkprintf(env, s, "M=destroy\n");
+
+        s->jkprintf(env, s, "\n", name);
+    }
+    jk2_worker_status_lstEndpoints(env, s, s->workerEnv);
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_worker_status_dmp(jk_env_t *env,
+                                           jk_worker_t *w, jk_ws_service_t *s)
+{
+    char *cName = s->query_string + 4;
+    int i;
+    int qryLen = 0;
+    int exact = 1;
+
+    /* Dump all attributes for the beans */
+    if (strcmp(cName, "*") == 0) {
+        *cName = '\0';
+        qryLen = 0;
+    }
+    else {
+        qryLen = strlen(cName);
+    }
+    if (qryLen > 0) {
+        if (cName[strlen(cName) - 1] == '*') {
+/*             printf("Exact match off %s\n", cName ); */
+            cName[strlen(cName) - 1] = '\0';
+            exact = 0;
+            qryLen--;
+        }
+    }
+    for (i = 0; i < env->_objects->size(env, env->_objects); i++) {
+        char *name = env->_objects->nameAt(env, env->_objects, i);
+        jk_bean_t *mbean = env->_objects->valueAt(env, env->_objects, i);
+        char **getAtt = mbean->getAttributeInfo;
+        char **setAtt = mbean->setAttributeInfo;
+
+        /* That's a bad name, created for backward compat. It should be deprecated.. */
+        if (strchr(name, ':') == NULL)
+            continue;
+
+        /* Endpoints are treated specially ( scoreboard to get the ep from other
+           processes */
+        if (strncmp("endpoint", mbean->type, 8) == 0)
+            continue;
+
+        if (strncmp("threadMutex", mbean->type, 11) == 0)
+            continue;
+
+        /* Prefix */
+        if (!exact && qryLen != 0 && strncmp(name, cName, qryLen) != 0)
+            continue;
+
+        /* Exact */
+        if (exact && qryLen != 0 && strcmp(name, cName) != 0)
+            continue;
+
+        if (mbean == NULL)
+            continue;
+        s->jkprintf(env, s, "[%s]\n", name);
+
+        s->jkprintf(env, s, "Id=%lp\n", mbean->object);
+
+        s->jkprintf(env, s, "ver=%d\n", mbean->ver);
+        s->jkprintf(env, s, "debug=%d\n", mbean->debug);
+        s->jkprintf(env, s, "disabled=%d\n", mbean->disabled);
+        while (getAtt != NULL && *getAtt != NULL && **getAtt != '\0') {
+            char *attName = *getAtt;
+            char *val = mbean->getAttribute(env, mbean, *getAtt);
+            if (strcmp(attName, "ver") == 0 ||
+                strcmp(attName, "debug") == 0 ||
+                strcmp(attName, "disabled") == 0) {
+                getAtt++;
+                continue;
+            }
+            s->jkprintf(env, s, "%s=%s\n", *getAtt,
+                        (val == NULL) ? "NULL" : val);
+            getAtt++;
+        }
+        s->jkprintf(env, s, "\n");
+    }
+    jk2_worker_status_dmpEndpoints(env, s, s->workerEnv);
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_worker_status_qry(jk_env_t *env,
+                                           jk_worker_t *w, jk_ws_service_t *s)
+{
+    char *cName = s->query_string + 4;
+    int i;
+    int qryLen = 0;
+    int exact = 1;
+    char localName[256];
+    int needQuote = 0;
+
+    /* Dump all attributes for the beans */
+    if (strcmp(cName, "*") == 0) {
+        *cName = '\0';
+        qryLen = 0;
+    }
+    else {
+        qryLen = strlen(cName);
+    }
+    if (qryLen > 0) {
+        if (cName[strlen(cName) - 1] == '*') {
+            cName[strlen(cName) - 1] = '\0';
+            exact = 0;
+            qryLen--;
+        }
+    }
+    /* Create a top section - not used currently */
+    s->jkprintf(env, s, "MXAgent: mod_jk2\n");
+    s->jkprintf(env, s, "\n");
+
+    for (i = 0; i < env->_objects->size(env, env->_objects); i++) {
+        char *name = env->_objects->nameAt(env, env->_objects, i);
+        jk_bean_t *mbean = env->_objects->valueAt(env, env->_objects, i);
+        char **getAtt = mbean->getAttributeInfo;
+        int j, k;
+
+        /* That's a bad name, created for backward compat. It should be deprecated.. */
+        if (strchr(name, ':') == NULL)
+            continue;
+
+        /* Endpoints are treated specially ( scoreboard to get the ep from other
+           processes */
+        if (strncmp("endpoint", mbean->type, 8) == 0)
+            continue;
+
+        if (strncmp("threadMutex", mbean->type, 11) == 0)
+            continue;
+
+        /* Prefix */
+        if (!exact && qryLen != 0 && strncmp(name, cName, qryLen) != 0)
+            continue;
+
+        /* Exact */
+        if (exact && qryLen != 0 && strcmp(name, cName) != 0)
+            continue;
+
+        if (mbean == NULL)
+            continue;
+
+        if (mbean->localName == NULL) {
+            s->jkprintf(env, s, "Name: modjk:type=%s\n", mbean->type);
+        }
+        else if (mbean->localName[0] == '\0') {
+            s->jkprintf(env, s, "Name: modjk:type=%s\n", mbean->type);
+        }
+        else {
+            needQuote = 0;
+            localName[0] = '\0';
+            for (j = 0, k = 0; k < 255; j++, k++) {
+                char c;
+                c = mbean->localName[j];
+                if (c == '\n') {
+                    localName[k++] = '\\';
+                    localName[k] = 'n';
+                }
+                else if (c == '*' || c == '"' || c == '\\' || c == '?') {
+                    localName[k++] = '\\';
+                    localName[k] = c;
+                    needQuote = 1;
+                }
+                else {
+                    localName[k] = c;
+                }
+            }
+            localName[k] = '\0';
+            if (needQuote) {
+                s->jkprintf(env, s, "Name: modjk:type=%s,name=\"%s\"\n",
+                            mbean->type, localName);
+            }
+            else {
+                s->jkprintf(env, s, "Name: modjk:type=%s,name=%s\n",
+                            mbean->type, localName);
+            }
+        }
+
+
+        /** Will be matched against modeler-mbeans.xml in that dir */
+        s->jkprintf(env, s, "modelerType: org.apache.jk.modjk.%s\n",
+                    mbean->type);
+
+        s->jkprintf(env, s, "Id: %lp\n", mbean->object);
+        s->jkprintf(env, s, "ver: %d\n", mbean->ver);
+        s->jkprintf(env, s, "debug: %d\n", mbean->debug);
+        s->jkprintf(env, s, "disabled: %d\n", mbean->disabled);
+
+        while (getAtt != NULL && *getAtt != NULL && **getAtt != '\0') {
+            char *attName = *getAtt;
+            char *val = mbean->getAttribute(env, mbean, *getAtt);
+            if (strcmp(attName, "ver") == 0 ||
+                strcmp(attName, "debug") == 0 ||
+                strcmp(attName, "disabled") == 0) {
+                getAtt++;
+                continue;
+            }
+
+            if (val != NULL && strchr(val, '\n') != NULL) {
+                /* XXX escape ? */
+                continue;
+            }
+            s->jkprintf(env, s, "%s: %s\n", *getAtt,
+                        (val == NULL) ? "NULL" : val);
+            getAtt++;
+        }
+        s->jkprintf(env, s, "\n");
+    }
+    jk2_worker_status_dmpEndpoints(env, s, s->workerEnv);
+    return JK_OK;
+}
+
+
+static int JK_METHOD jk2_worker_status_get(jk_env_t *env,
+                                           jk_worker_t *w, jk_ws_service_t *s)
+{
+    char *cName = s->query_string + 4;
+    char *attName = strrchr(cName, '|');
+    int i;
+
+    if (attName == NULL) {
+        s->jkprintf(env, s, "ERROR: no attribute value found %s\n", cName);
+        return JK_OK;
+    }
+    *attName = '\0';
+    attName++;
+
+    for (i = 0; i < env->_objects->size(env, env->_objects); i++) {
+        char *name = env->_objects->nameAt(env, env->_objects, i);
+        jk_bean_t *mbean = env->_objects->valueAt(env, env->_objects, i);
+
+        if (mbean == NULL)
+            continue;
+
+        if (strcmp(name, cName) == 0 && mbean->getAttribute != NULL) {
+            void *result = mbean->getAttribute(env, mbean, attName);
+            s->jkprintf(env, s, "OK|%s|%s", cName, attName);
+            s->jkprintf(env, s, "%s", result);
+            return JK_OK;
+        }
+    }
+    s->jkprintf(env, s, "ERROR|mbean not found|%s\n", cName);
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_worker_status_set(jk_env_t *env,
+                                           jk_worker_t *w, jk_ws_service_t *s)
+{
+    char *cName = s->query_string + 4;
+    char *attVal = strrchr(cName, '|');
+    char *attName;
+    int i;
+
+    if (attVal == NULL) {
+        s->jkprintf(env, s, "ERROR: no attribute value found %s\n", cName);
+        return JK_OK;
+    }
+    *attVal = '\0';
+    attVal++;
+
+    attName = strrchr(cName, '|');
+    if (attName == NULL) {
+        s->jkprintf(env, s, "ERROR: attribute name not found\n", cName);
+        return JK_OK;
+    }
+    *attName = '\0';
+    attName++;
+    for (i = 0; i < env->_objects->size(env, env->_objects); i++) {
+        char *name = env->_objects->nameAt(env, env->_objects, i);
+        jk_bean_t *mbean = env->_objects->valueAt(env, env->_objects, i);
+
+        if (mbean == NULL)
+            continue;
+
+        if (strcmp(name, cName) == 0 && mbean->setAttribute != NULL) {
+            int res;
+
+            jk_shm_t *shm = w->workerEnv->shm;
+
+            /* Found the object */
+/*             int res=mbean->setAttribute( env, mbean, attName, attVal ); */
+/*             if( w->mbean->debug > 0 )  */
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG, "status.set() %s %s\n",
+                          cName, attName);
+
+            res = jk2_config_setProperty(env, w->workerEnv->config,
+                                         mbean, attName, attVal);
+
+            /* Increment the version */
+/*             jk2_config_setProperty(env, w->workerEnv->config, */
+            mbean->ver++;
+
+            /* Save */
+            w->workerEnv->config->save(env, w->workerEnv->config, NULL);
+
+
+            /* Update the scoreboard's version - all other
+               jk2 processes will see this and update
+             */
+            if (shm != NULL && shm->head != NULL)
+                shm->head->lbVer++;
+
+            s->jkprintf(env, s, "OK|%s|%s|%d", cName, attName, res);
+            return JK_OK;
+        }
+    }
+    s->jkprintf(env, s, "ERROR|not found|%s|%s|%s\n", cName, attName, attVal);
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_worker_status_invoke(jk_env_t *env,
+                                              jk_worker_t *w,
+                                              jk_ws_service_t *s)
+{
+    char *cName = s->query_string + 4;
+    char *attName;
+    int i;
+
+    attName = strrchr(cName, '|');
+    if (attName == NULL) {
+        s->jkprintf(env, s, "ERROR: attribute name not found\n", cName);
+        return JK_OK;
+    }
+    *attName = '\0';
+    attName++;
+
+    for (i = 0; i < env->_objects->size(env, env->_objects); i++) {
+        char *name = env->_objects->nameAt(env, env->_objects, i);
+        jk_bean_t *mbean = env->_objects->valueAt(env, env->_objects, i);
+        int res = 0;
+
+        if (mbean == NULL)
+            continue;
+
+        if (strcmp(name, cName) == 0) {
+
+            if (strcmp(attName, "init")) {
+                if (mbean->init != NULL)
+                    res = mbean->init(env, mbean);
+            }
+            if (strcmp(attName, "destroy")) {
+                if (mbean->destroy != NULL)
+                    res = mbean->destroy(env, mbean);
+            }
+
+            s->jkprintf(env, s, "OK|%s|%s|%d", cName, attName, res);
+            return JK_OK;
+        }
+    }
+    s->jkprintf(env, s, "ERROR|not found|%s|%s\n", cName, attName);
+    return JK_OK;
+}
+
+/*
+ * Properties available to Set.
+ */
+static char *jk2_worker_status_setAttributeInfo[] = {
+      "stylePath", "styleMode", "debug", "disabled", NULL
+};
+
+/*
+ * Set status worker properties.
+ */
+static int JK_METHOD jk2_worker_status_setAttribute(jk_env_t *env, jk_bean_t *mbean, char *name, void *valueP)
+{
+    jk_worker_t *w = (jk_worker_t *)mbean->object;
+    char *value = (char *)valueP;
+
+    if (strcmp(name, "stylePath") == 0) {
+
+        if(value != NULL) {
+
+            w->stylePath = value;
+        }
+        else {
+
+            w->stylePath = NULL;
+        }
+    }
+    else if (strcmp(name, "styleMode") == 0) {
+
+        w->styleMode = atoi(value);
+    }
+    else if (strcmp(name, "debug") == 0) {
+
+        mbean->debug = atoi(value);
+    }
+    else if (strcmp(name, "disabled") == 0) {
+
+        mbean->disabled = atoi(value);
+    }
+    else {
+
+        return JK_ERR;
+    }
+    return JK_OK;
+}
+
+/*
+ * Properties available to Get.
+ */
+static char *jk2_worker_status_getAttributeInfo[] = {
+      "stylePath", "styleMode", "debug", "disabled", NULL
+};
+
+/*
+ * Get status worker properties.
+ */
+static void *JK_METHOD jk2_worker_status_getAttribute(jk_env_t *env, jk_bean_t *bean, char *name)
+{
+    jk_worker_t *w = (jk_worker_t *)bean->object;
+
+    if (strcmp(name, "stylePath") == 0) {
+
+        if (w->stylePath != NULL) {
+
+            return w->stylePath;
+        }
+        else {
+
+            return ("");
+        }
+    }
+    else if (strcmp(name, "styleMode") == 0) {
+
+        return jk2_env_itoa(env, w->styleMode);
+    }
+    else if (strcmp(name, "debug") == 0) {
+
+        return jk2_env_itoa(env, bean->debug);
+    }
+    else if (strcmp(name, "disabled") == 0) {
+
+        return jk2_env_itoa(env, bean->disabled);
+    }
+    return NULL;
+}
+
+/*
+ * Send a (stylesheet) file to the user.
+ */
+static int jk2_worker_status_sendFile(jk_env_t *env, jk_ws_service_t *s, char *file)
+{
+#define LENGTH_OF_LINE    (1020)
+
+    FILE *fp;
+    char buff[LENGTH_OF_LINE + 1];
+    int  cbuf = 0;
+
+    if( file == NULL ) {
+
+        return JK_ERR;
+    }
+
+#ifdef AS400
+    fp = fopen(file, "r, o_ccsid=0");
+#else
+    fp = fopen(file, "r");
+#endif
+
+    if( fp == NULL ) {
+
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, "status_worker.sendFile() Error openning file: %s\n", file);
+
+        return JK_ERR;
+    }
+
+    while ((cbuf = fread(buff, 1, LENGTH_OF_LINE, fp))) {
+
+        buff[cbuf] = '\0';
+
+        s->jkprintf(env, s, "%s", buff);
+    }
+
+    fclose(fp);
+
+    return JK_OK;
+}
+
+/*
+ *  Main entry point for all Status Worker output.
+ *  Optional commands are passed in as a HTML query string.
+ */
+static int JK_METHOD jk2_worker_status_service(jk_env_t *env,
+                                               jk_worker_t *w,
+                                               jk_ws_service_t *s)
+{
+    char *uri = s->req_uri;
+    int didUpdate;
+
+    if (w->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG, "status.service() %s %s\n",
+                      JK_CHECK_NULL(uri), JK_CHECK_NULL(s->query_string));
+
+    /** Settle the query string. */
+
+    if (s->query_string == NULL) {
+
+        s->query_string = "";
+    }
+
+    /* Generate the header */
+
+    s->status = 200;
+    s->msg = "OK";
+
+    s->headers_out->put(env, s->headers_out, "Cache-Control", "no-cache", NULL);
+    s->headers_out->put(env, s->headers_out, "Pragma", "no-cache", NULL);
+
+    /* Decide if output is text or HTML. */
+
+    if (strncmp(s->query_string, "dmp=", 4) == 0 ||
+        strncmp(s->query_string, "lst=", 4) == 0 ||
+        strncmp(s->query_string, "qry=", 4) == 0) {
+
+        /* Text */
+        s->headers_out->put(env, s->headers_out, "Content-Type", "text/plain", NULL);
+        s->head(env, s);
+    }
+    else {
+
+        /* HTML */
+        s->headers_out->put(env, s->headers_out, "Content-Type", "text/html", NULL);
+        s->head(env, s);
+
+        /** A stylesheet if defined. **/
+
+        /** Mode 0 - Style Sheet Off - default. **/
+        /** Mode 1 - Int Style Sheet - default values. **/
+        /** Mode 2 - Ext Style Sheet - ext file, documentRoot relative. **/
+        /** Mode 3 - Int Style Sheet - ext file, f/system or serverRoot relative. **/
+
+        if (w->styleMode == 1) {
+
+            s->jkprintf(env, s, "<style>%s</style>\n", DEFAULT_CSS);
+        }
+        else if (w->stylePath != NULL) {
+
+            if (w->styleMode == 2) {
+
+                s->jkprintf(env, s, "<LINK REL=stylesheet TYPE='text/css' HREF='%s'>\n", w->stylePath);
+            }
+            else if (w->styleMode == 3) {
+
+                jk2_worker_status_sendFile(env, s, w->stylePath);
+            }
+        }
+    }
+
+    /** Check for Scoreboard Reset. **/
+
+    if (strcmp(s->query_string, "scoreboard.reset") == 0) {
+        jk2_worker_status_resetScoreboard(env, s, s->workerEnv);
+        s->jkprintf(env, s, "Scoreboard reset\n");
+    }
+
+    /** Updating the config is the first thing we do.
+        All other operations will happen on the update config.
+      */
+    w->workerEnv->config->update(env, w->workerEnv->config, &didUpdate);
+    env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                  "status.update check %d\n", didUpdate);
+    if (didUpdate) {
+        jk_shm_t *shm = w->workerEnv->shm;
+
+        /* Update the scoreboard's version - all other
+           jk2 processes will see this and update
+         */
+        if (shm != NULL && shm->head != NULL) {
+            shm->head->lbVer++;
+            s->jkprintf(env, s, "Updated config version to %d\n",
+                        shm->head->lbVer);
+        }
+        else {
+            s->jkprintf(env, s, "Update detected. No scoreboard.\n");
+        }
+    }
+
+
+    /* Increment the scoreboard version counter, reload */
+    if (strncmp(s->query_string, "rld=", 4) == 0) {
+        jk_shm_t *shm = w->workerEnv->shm;
+
+        /* Update the scoreboard's version - all other
+           jk2 processes will see this and update
+         */
+        if (shm != NULL && shm->head != NULL) {
+            shm->head->lbVer++;
+            s->jkprintf(env, s, "Updated config version to %d\n",
+                        shm->head->lbVer);
+        }
+        else {
+            s->jkprintf(env, s, "Reload requested. No scoreboard.\n");
+        }
+        return JK_OK;
+    }
+
+    /** list all mbeans and their attributes  */
+    if (strncmp(s->query_string, "lst=", 4) == 0) {
+        return jk2_worker_status_list(env, w, s);
+    }
+
+    /** Dump multiple attributes  */
+    if (strncmp(s->query_string, "dmp=", 4) == 0) {
+        return jk2_worker_status_dmp(env, w, s);
+    }
+
+    /** Commons-modeler  */
+    if (strncmp(s->query_string, "qry=", 4) == 0) {
+        return jk2_worker_status_qry(env, w, s);
+    }
+
+    /** Get a single attribute. This also works for attributes that are not listed in getAtt **/
+    if (strncmp(s->query_string, "get=", 4) == 0) {
+        return jk2_worker_status_get(env, w, s);
+    }
+
+    /** Set an attribute. Works for attributes that are not listed in setAtt **/
+    if (strncmp(s->query_string, "set=", 4) == 0) {
+        return jk2_worker_status_set(env, w, s);
+    }
+
+    if (strncmp(s->query_string, "inv=", 4) == 0) {
+        return jk2_worker_status_invoke(env, w, s);
+    }
+
+    s->jkprintf(env, s, "Status information for child %d<br>",
+                s->workerEnv->childId);
+    s->jkprintf(env, s,
+                " <a href='jkstatus?cfgOrig=1'>[Original config]</a>\n");
+    s->jkprintf(env, s,
+                " <a href='jkstatus?cfgParsed=1'>[Processed config]</a>\n");
+    s->jkprintf(env, s,
+                " <a href='jkstatus?scoreboard=1'>[Scoreboard info]</a>\n");
+    s->jkprintf(env, s,
+                " <a href='jkstatus'>[Workers, Channels and URIs]</a>\n");
+
+    if (strncmp(s->query_string, "cfgOrig=", 8) == 0) {
+        jk2_worker_status_displayConfigProperties(env, s, s->workerEnv);
+        return JK_OK;
+    }
+
+    if (strncmp(s->query_string, "cfgParsed=", 10) == 0) {
+        jk2_worker_status_displayActiveProperties(env, s, s->workerEnv);
+        return JK_OK;
+    }
+
+    if (strncmp(s->query_string, "scoreboard=", 11) == 0) {
+        jk2_worker_status_displayScoreboardInfo(env, s, s->workerEnv);
+        return JK_OK;
+    }
+    if (strncmp(s->query_string, "endpoints=", 10) == 0) {
+        jk2_worker_status_displayEndpointInfo(env, s, s->workerEnv);
+        return JK_OK;
+    }
+
+    /* Body */
+    jk2_worker_status_displayRuntimeType(env, s, s->workerEnv, "ajp13");
+    jk2_worker_status_displayRuntimeType(env, s, s->workerEnv,
+                                         "channel.socket");
+    jk2_worker_status_displayRuntimeType(env, s, s->workerEnv, "channel.un");
+    jk2_worker_status_displayRuntimeType(env, s, s->workerEnv, "channel.jni");
+    jk2_worker_status_displayRuntimeType(env, s, s->workerEnv, "uri");
+
+    s->afterRequest(env, s);
+    return JK_OK;
+}
+
+/* The Factory is called during server start-up to create the status-worker object(s).
+ */
+int JK_METHOD jk2_worker_status_factory(jk_env_t *env, jk_pool_t *pool,
+                                        jk_bean_t *result,
+                                        const char *type, const char *name)
+{
+    jk_worker_t *_this;
+
+    _this = (jk_worker_t *)pool->calloc(env, pool, sizeof(jk_worker_t));
+
+    if (_this == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "status_worker.factory() OutOfMemoryException\n");
+        return JK_ERR;
+    }
+
+    _this->service   = jk2_worker_status_service;
+
+    _this->stylePath = NULL;
+    _this->styleMode = 0;
+
+    result->multiValueInfo   = NULL;
+    result->setAttributeInfo = jk2_worker_status_setAttributeInfo;
+    result->setAttribute     = jk2_worker_status_setAttribute;
+    result->getAttributeInfo = jk2_worker_status_getAttributeInfo;
+    result->getAttribute     = jk2_worker_status_getAttribute;
+
+    result->object = _this;
+    _this->mbean = result;
+
+    _this->workerEnv = env->getByName(env, "workerEnv");
+    _this->workerEnv->addWorker(env, _this->workerEnv, _this);
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/configure.in b/connectors/jk/native2/configure.in
new file mode 100755
index 0000000..1f3b3d4
--- /dev/null
+++ b/connectors/jk/native2/configure.in
@@ -0,0 +1,233 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl Author Henri Gomez <hgomez@apache.org>
+dnl 
+dnl Version $Id$
+dnl --------------------------------------------------------------------------
+
+dnl sinclude(../support/jk_apache_static.m4)
+sinclude(../support/jk_apxs.m4)
+sinclude(../support/jk_ws.m4)
+sinclude(../support/jk_exec.m4)
+sinclude(../support/jk_apr.m4)
+sinclude(../support/jk_tchome.m4)
+sinclude(../support/jk_dominohome.m4)
+sinclude(../support/jk_java.m4)
+sinclude(../support/jk_pcre.m4)
+
+dnl
+dnl Process this file with autoconf to produce a configure script
+dnl
+AC_REVISION($Id$)dnl
+
+AC_PREREQ(2.13)
+AC_INIT(common/jk_config.c)
+AC_CONFIG_AUX_DIR(scripts/build/unix)
+AC_CANONICAL_SYSTEM
+
+dnl package and version. (synchronization with common/jk_version.h ?)
+PACKAGE=mod_jk2
+VERSION=2.0.2
+
+AM_INIT_AUTOMAKE(${PACKAGE}, ${VERSION})
+
+AC_PROG_CC
+AC_PROG_LD
+
+dnl Not sure what it does, but the libtool manual seems to require this
+dnl It should use the native platform dlopen ( if available )
+AC_LIBTOOL_DLOPEN
+
+dnl AM_PROG_LIBTOOL often causes problems.
+dnl I have solved them once using aclocal --acdir=/usr/local/share/aclocal/
+AM_PROG_LIBTOOL
+
+AC_PATH_PROG(TEST,test,$PATH)dnl
+AC_SUBST(TEST)
+
+AC_PATH_PROG(RM,rm,$PATH)dnl
+AC_SUBST(RM)
+
+AC_PATH_PROG(GREP,grep,$PATH)dnl
+AC_SUBST(GREP)
+
+AC_PATH_PROG(ECHO,echo,echo,$PATH)dnl
+AC_SUBST(ECHO)
+
+AC_PATH_PROG(SED,sed,$PATH)dnl
+AC_SUBST(SED)
+
+AC_PATH_PROG(CP,cp,$PATH)dnl
+AC_SUBST(CP)
+
+AC_PATH_PROG(MKDIR,mkdir,$PATH)dnl
+AC_SUBST(MKDIR)
+
+dnl APXS settings
+APR_LIBDIR_LA=""
+AC_SUBST(APR_LIBDIR_LA)
+
+JK_APXS([], [location of apxs for Apache 1.3])
+JK_APXS([2], [location of apxs for Apache 2.0])
+
+dnl APACHE13 settings
+AC_MSG_CHECKING([checking for apache13...])
+JK_WS_DIR([apache13], [APACHE], [src/include/httpd.h], [server/apache13])
+JK_WS_INCDIR([apache13], [APACHE], [httpd.h])
+JK_WS_LIBDIR([apache13], [APACHE])
+
+
+dnl APACHE2 settings
+AC_MSG_CHECKING([checking for apache20...])
+JK_WS_DIR([apache2], [APACHE2], [include/httpd.h], [server/apache2])
+JK_WS_INCDIR([apache2], [APACHE2], [httpd.h])
+JK_WS_LIBDIR([apache2], [APACHE2])
+
+dnl IIS settings
+AC_MSG_CHECKING([checking for iis...])
+JK_WS_DIR([iis], [IIS], [.], [server/isapi])
+JK_WS_INCDIR([iis], [IIS], [.])
+JK_WS_LIBDIR([iis], [IIS])
+
+dnl iPlanet settings
+AC_MSG_CHECKING([checking for iPlanet...])
+JK_WS_DIR([iplanet], [IPLANET], [.], [server/aolserver])
+JK_WS_INCDIR([iplanet], [IPLANET], [.])
+JK_WS_LIBDIR([iplanet], [IPLANET])
+
+dnl DSAPI settings
+AC_MSG_CHECKING([checking for dsapi...])
+JK_WS_DIR([dsapi], [DOMINO], [server/dsapi/jk_dsapi_plugin.c], [server/dsapi])
+JK_WS_INCDIR([dsapi], [DOMINO], [.])
+JK_WS_LIBDIR([dsapi], [DOMINO])
+
+dnl TOMCATs settings
+
+JK_TCHOME([tomcat33], [TOMCAT33_HOME], [lib/tomcat.jar])
+JK_TCHOME([tomcat40], [TOMCAT40_HOME], [server/lib/catalina.jar])
+JK_TCHOME([tomcat41], [TOMCAT41_HOME], [server/lib/catalina.jar])
+
+AC_SUBST(TOMCAT33_HOME)
+AC_SUBST(TOMCAT40_HOME)
+AC_SUBST(TOMCAT41_HOME)
+
+dnl Domino settings
+JK_DOMHOME([domino], [DOMINO_HOME], [libnotes.so])
+JK_DOMHOME([notesapi], [NOTESAPI], [include/global.h])
+
+AC_SUBST(DOMINO_HOME)
+AC_SUBST(NOTESAPI)
+
+dnl APR settings
+
+JK_APR_THREADS()
+JK_APR([include/apr.h.in])
+JK_APR_UTIL([include/apu.h.in])
+JK_APR_INCDIR([apr.h])
+JK_APR_LIBDIR()
+
+dnl Java settings
+
+JK_JNI()
+JK_JDK()
+JK_JDK_OS()
+JK_PCRE()
+
+AC_SUBST(JAVA_HOME)
+AC_SUBST(JAVA_PLATFORM)
+AC_SUBST(OS)
+AC_SUBST(HAVE_JNI)
+AC_SUBST(JNI_BUILD)
+AC_SUBST(HAS_PCRE)
+AC_SUBST(PCRE_LIBS)
+
+dnl Check that at least one WEBSERVER has been given
+if ${TEST} -z "$WEBSERVERS" ; then
+	AC_MSG_ERROR(Cannot find any WebServer)
+fi
+
+AC_SUBST(WEBSERVERS)
+
+dnl if --with-apr is specified, --with-apr-util must be too
+if ${TEST} ! -z "$APR_BUILD" -a -z "$APR_UTIL_DIR"; then
+  AC_MSG_ERROR([--with-apr and --with-apr-util must be used together])
+fi
+
+dnl apache 1.3 consistancy checks
+if ! ${TEST} -z "$APACHE_HOME" ; then
+dnl check if apache 1.3 was selected without apr sources
+        if ${TEST} -z "$APR_BUILD"; then
+                AC_MSG_ERROR([Apache 1.3 requires apr to built from source, use --with-apr and --with-apr-util])
+        fi
+dnl make sure compiler matchs apxs
+        if ${TEST} -n "$APACHE_CC" -a "$APACHE_CC" != "$CC"; then
+                AC_MSG_RESULT([error])
+                AC_MSG_RESULT([compiler discovered by configure: ${CC}])
+                AC_MSG_RESULT([compiler used by apache: ${APACHE_CC}])
+                AC_MSG_RESULT([delete config.cache and try CC=${APACHE_CC} ./configure])
+                AC_MSG_ERROR([jk2 and apache compilers must be the same])
+        fi
+fi
+
+dnl apache 2 consistancy checks
+if ${TEST} ! -z "$APACHE2_HOME" ; then
+dnl check if apache 2 was selected with apr sources
+        if ${TEST} ! -z "$APR_BUILD"; then
+                AC_MSG_ERROR([Use apr that comes with Apache 2, remove --with-apr])
+        fi
+dnl make sure compiler matchs apxs
+        if ${TEST} -n "$APACHE2_CC" -a "$APACHE2_CC" != "$CC"; then
+                AC_MSG_RESULT([error])
+                AC_MSG_RESULT([compiler discovered by configure: ${CC}])
+                AC_MSG_RESULT([compiler used by apache: ${APACHE2_CC}])
+                AC_MSG_RESULT([delete config.cache and try CC=${APACHE2_CC} ./configure])
+                AC_MSG_ERROR([jk2 and apache compilers must be the same])
+        fi
+fi
+AC_SUBST(APACHE20_OEXT)
+AC_SUBST(LIB_JK_TYPE)
+AC_SUBST(INSTALL_TYPE)
+
+dnl set APR defs
+AC_SUBST(APR_BUILD)
+AC_SUBST(APR_CFLAGS)
+AC_SUBST(APR_CLEAN)
+AC_SUBST(APR_DIR)
+AC_SUBST(APR_UTIL_DIR)
+AC_SUBST(APR_HOME)
+AC_SUBST(APR_INCDIR)
+AC_SUBST(APR_UTIL_INCDIR)
+AC_SUBST(APR_LIBDIR)
+AC_SUBST(APR_UTIL_LIBDIR)
+AC_SUBST(APR_CONFIGURE_ARGS)
+AC_SUBST(APR_LDFLAGS)
+AC_SUBST(COMMON_APR_OBJECTS)
+AC_SUBST(LIBTOOL)
+
+dnl automake needs the path it does not work with $WEBSERVER
+dnl that why useless Makefiles are build.
+AC_OUTPUT([
+    Makefile
+	server/apache13/Makefile
+	server/apache13/Makefile.apxs
+	server/apache2/Makefile
+	server/apache2/Makefile.apxs
+	server/dsapi/Makefile
+	../build.properties:../build.properties.autoconf
+        scripts/build/unix/dummy
+	])
diff --git a/connectors/jk/native2/include/jk_bean.h b/connectors/jk/native2/include/jk_bean.h
new file mode 100644
index 0000000..0ddebbb
--- /dev/null
+++ b/connectors/jk/native2/include/jk_bean.h
@@ -0,0 +1,203 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef JK_BEAN_H
+#define JK_BEAN_H
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+#include "jk_global.h"
+#include "jk_env.h"
+#include "jk_logger.h"
+#include "jk_pool.h"
+#include "jk_map.h"
+#include "jk_worker.h"
+
+    struct jk_pool;
+    struct jk_env;
+    struct jk_logger;
+    struct jk_map;
+    struct jk_bean;
+    typedef struct jk_bean jk_bean_t;
+
+#define JK_STATE_DISABLED 0
+#define JK_STATE_NEW 1
+#define JK_STATE_INIT 2
+
+#define JK_INVOKE_WITH_RESPONSE 1
+
+/**
+ * Factory used to create all jk objects. Factories are registered with 
+ * jk2_env_registerFactory. The 'core' components are registered in
+ * jk_registry.c
+ *
+ * Each jk component must be configurable using the setAttribute methods
+ * in jk_bean. The factory is responsible to set up the config methods.
+ *
+ * The mechanism provide modularity and manageability to jk.
+ */
+    typedef int (JK_METHOD * jk_env_objectFactory_t) (struct jk_env * env,
+                                                      struct jk_pool * pool,
+                                                      struct jk_bean * mbean,
+                                                      const char *type,
+                                                      const char *name);
+
+/** Each jk object will use this mechanism for configuration
+ *
+ *  Lifecycle:
+ *  - object is created using env->createBean() or by jk_config ( if you want
+ *    the object config to be saved )
+ *
+ *  - the name is parsed and the 'type' and 'localName' extracted.
+ * 
+ *  - 'type' will be looked up in registry, to find jk_env_objectFactory_t
+ *
+ *  - the factory method is called. It will create the object ( and eventually look
+ *    up other objects )
+ *
+ *  - setAttribute() is called for each configured property.
+ *
+ *  - init() is called, after this the component is operational.
+ *
+ *  - destroy() should clean up any resources ( the pool and all objects allocated
+ *    in the pool can be cleaned up automatically )
+ */
+    struct jk_bean
+    {
+        /* Type of this object ( "channel.socket", "workerEnv", etc )
+         */
+        char *type;
+
+    /** The index in the workerEnv table */
+        int id;
+
+    /** The index in the env object table */
+        int objId;
+
+        /* Full name of the object ( "channel.socket:localhost:8080" ).
+         * Used to construct the object.
+         */
+        char *name;
+
+        /* Local part of the name ( localhost:8080 )
+         */
+        char *localName;
+
+        /* The wrapped object ( points to the real struct: jk_worker_t *, jk_channel_t *, etc )
+         */
+        void *object;
+
+    /** Common information - if not 0 the component should print
+     *  verbose information about its operation
+    */
+        int debug;
+
+        int state;
+
+        /* Common information - if set the component will not be
+         * initialized or used
+         */
+        int disabled;
+
+    /** Object 'version'. Used to detect changes in config.
+     *  XXX will be set to the timestamp of the last config modification.
+     */
+        long ver;
+
+    /** Unprocessed settings that are set on this bean by the config
+        apis ( i.e. with $() in it ).
+
+        It'll be != NULL for each component that was created or set using
+        jk_config.
+
+        This is what jk_config will save.
+    */
+        struct jk_map *settings;
+
+        /* Object pool. The jk_bean and the object itself are created in this
+         * pool. If this pool is destroyed or recycled, the object and all its
+         * data are destroyed as well ( assuming the pool corectly cleans child pools
+         * and object data are not created explicitely in a different pool ).
+         *
+         * Object should create sub-pools if they want to create/destroy long-lived
+         * data, and env->tmpPool for data that is valid during the transaction.
+         */
+        struct jk_pool *pool;
+
+        /* Temp - will change ! */
+
+        /* Multi-value attributes. Must be declared so config knows how
+           to save them. XXX we'll have to use a special syntax for that,
+           the Preferences API and registry don't seem to support them.
+         */
+        char **multiValueInfo;
+
+        /* Attributes supported by getAttribute method */
+        char **getAttributeInfo;
+
+        /* Attributes supported by setAttribute method */
+        char **setAttributeInfo;
+
+    /** Set a jk property. This is similar with the mechanism
+     *  used by java side ( with individual setters for
+     *  various properties ), except we use a single method
+     *  and a big switch
+     *
+     *  As in java beans, setting a property may have side effects
+     *  like changing the log level or reading a secondary
+     *  properties file.
+     *
+     *  Changing a property at runtime will also be supported for
+     *  some properties.
+     *  XXX Document supported properties as part of
+     *  workers.properties doc.
+     *  XXX Implement run-time change in the status/ctl workers.
+     */
+        int (JK_METHOD * setAttribute) (struct jk_env * env,
+                                        struct jk_bean * bean, char *name,
+                                        void *value);
+
+        void *(JK_METHOD * getAttribute) (struct jk_env * env,
+                                          struct jk_bean * bean, char *name);
+
+        /* Init the component
+         */
+        int (JK_METHOD * init) (struct jk_env * env, struct jk_bean * bean);
+
+        int (JK_METHOD * destroy) (struct jk_env * env,
+                                   struct jk_bean * bean);
+
+    /** Called by the RPC-like protocol or the JNI bridge. Will unmarshal the arguments 
+     *  and dispatch to the real method. This is similar with 'dynamic invocation'/introspection/
+     *  'scripting support', etc. 
+     */
+        int (JK_METHOD * invoke) (struct jk_env * env,
+                                  struct jk_bean * target,
+                                  struct jk_endpoint * ae, int code,
+                                  struct jk_msg * msg, int raw);
+
+    };
+
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif
diff --git a/connectors/jk/native2/include/jk_channel.h b/connectors/jk/native2/include/jk_channel.h
new file mode 100644
index 0000000..b421a60
--- /dev/null
+++ b/connectors/jk/native2/include/jk_channel.h
@@ -0,0 +1,157 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef JK_CHANNEL_H
+#define JK_CHANNEL_H
+
+#include "jk_global.h"
+#include "jk_env.h"
+#include "jk_logger.h"
+#include "jk_pool.h"
+#include "jk_msg.h"
+#include "jk_service.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+    struct jk_env;
+    struct jk_worker;
+    struct jk_endpoint;
+    struct jk_channel;
+
+#define CH_OPEN 4
+#define CH_CLOSE 5
+#define CH_READ 6
+#define CH_WRITE 7
+#define CH_HASINPUT 8
+
+    typedef struct jk_channel jk_channel_t;
+
+/**
+ * Abstraction (interface) for sending/receiving blocks of data ( packets ).
+ * This will be used to replace the current TCP/socket code and allow other
+ * transports. JNI, shmem, doors and other mechanisms can be tryed out, and 
+ * we can also have a gradual transition to APR. The current tcp-based transport
+ * will be refactored to this interface.
+ * 
+ * XXX Experimental
+ * 
+ * Issues:
+ *  - Should the transport also check the packet integrity ( envelope ) ?
+ *  - This is supposed to work for send/receive sequences, and the buffer
+ *   shouldn't be modified until a send/receive is completed ( we want to
+ *   avoid memcpy )
+ *  - We need to extend/generalize the mechanisms in 'worker' to support other
+ *   types of internal interfaces.
+ *  - this interface is using get/setProperty model, like in Java beans ( well, 
+ *    just a generic get/set method, no individual setters ). This is different
+ *    from worker, which gets a map at startup. This model should also allow 
+ *    run-time queries for status, management, etc - but it may be too complex.
+ * 
+ * @author Costin Manolache
+ */
+    struct jk_channel
+    {
+        struct jk_bean *mbean;
+
+        /* JK_TRUE if the channel is 'stream' based, i.e. it works using
+           send() followed by blocking reads().
+           XXX make it type and define an enum of supported types ?
+
+           The only alternative right now is JNI ( and doors ), where
+           a single thread is used. After the first packet is sent the
+           java side takes control and directly dispatch messages using the
+           jni  ( XXX review - it would be simple with continuations, but
+           send/receive flow is hard to replicate on jni ) 
+         */
+        int is_stream;
+
+        struct jk_workerEnv *workerEnv;
+        struct jk_worker *worker;
+        char *workerName;
+
+        int serverSide;
+
+    /** Open the communication channel
+     */
+        int (JK_METHOD * open) (struct jk_env * env, jk_channel_t *_this,
+                                struct jk_endpoint * endpoint);
+
+    /** Close the communication channel
+     */
+        int (JK_METHOD * close) (struct jk_env * env, jk_channel_t *_this,
+                                 struct jk_endpoint * endpoint);
+
+  /** Send a packet
+   */
+        int (JK_METHOD * send) (struct jk_env * env, jk_channel_t *_this,
+                                struct jk_endpoint * endpoint,
+                                struct jk_msg * msg);
+
+    /** Receive a packet
+     */
+        int (JK_METHOD * recv) (struct jk_env * env, jk_channel_t *_this,
+                                struct jk_endpoint * endpoint,
+                                struct jk_msg * msg);
+
+    /** Check if something is available in input on the communication channel
+     */
+        int (JK_METHOD * hasinput) (struct jk_env * env, jk_channel_t *_this,
+                                    struct jk_endpoint * endpoint,
+                                    int timeout);
+
+    /** Called before request processing, to initialize resources.
+        All following calls will be in the same thread.
+     */
+        int (JK_METHOD * beforeRequest) (struct jk_env * env,
+                                         jk_channel_t *_this,
+                                         struct jk_worker * worker,
+                                         struct jk_endpoint * endpoint,
+                                         struct jk_ws_service * r);
+
+    /** Called after request processing. Used to be worker.done()
+     */
+        int (JK_METHOD * afterRequest) (struct jk_env * env,
+                                        jk_channel_t *_this,
+                                        struct jk_worker * worker,
+                                        struct jk_endpoint * endpoint,
+                                        struct jk_ws_service * r);
+
+    /** Obtain the channel status code
+     */
+        int (JK_METHOD * status) (struct jk_env * env,
+                                  struct jk_worker * worker,
+                                  jk_channel_t *_this);
+
+        void *_privatePtr;
+    };
+
+    int JK_METHOD jk2_channel_invoke(struct jk_env *env, struct jk_bean *bean,
+                                     struct jk_endpoint *ep, int code,
+                                     struct jk_msg *msg, int raw);
+
+
+    int JK_METHOD jk2_channel_setAttribute(struct jk_env *env,
+                                           struct jk_bean *mbean, char *name,
+                                           void *valueP);
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif
diff --git a/connectors/jk/native2/include/jk_config.h b/connectors/jk/native2/include/jk_config.h
new file mode 100644
index 0000000..9130ce7
--- /dev/null
+++ b/connectors/jk/native2/include/jk_config.h
@@ -0,0 +1,120 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/** Config object. It's more-or-less independent of the config source
+    or representation. 
+ */
+
+#ifndef JK_CONFIG_H
+#define JK_CONFIG_H
+
+#include "jk_global.h"
+#include "jk_pool.h"
+#include "jk_env.h"
+#include "jk_logger.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+    struct jk_pool;
+    struct jk_map;
+    struct jk_env;
+    struct jk_config;
+    typedef struct jk_config jk_config_t;
+
+/**
+ *
+ */
+    struct jk_config
+    {
+        struct jk_bean *mbean;
+        int ver;
+
+        /* Parse and process a property. It'll locate the object and call the
+         * setAttribute on it.
+         */
+        int (*setPropertyString) (struct jk_env * env, struct jk_config * cfg,
+                                  char *name, char *value);
+
+        /* Set an attribute for a jk object. This should be the
+         * only method called to configure objects. The implementation
+         * will update the underlying repository in addition to setting
+         * the runtime value. Calling setAttribute on the object directly
+         * will only set the runtime value.
+         */
+        int (*setProperty) (struct jk_env * env, struct jk_config * cfg,
+                            struct jk_bean * target, char *name, char *value);
+
+    /** Write the config file. If targetFile is NULL, it'll override the
+     *  file that was used for reading
+    */
+        int (*save) (struct jk_env * env, struct jk_config * cfg,
+                     char *targetFile);
+
+    /** Check if the config changed, and update the workers.
+     */
+        int (*update) (struct jk_env * env, struct jk_config * cfg,
+                       int *didReload);
+
+    /** Process a node in config data
+     */
+        int (*processNode) (struct jk_env * env, struct jk_config * cfg,
+                            char *node, int didReload);
+
+        /* Private data */
+        struct jk_pool *pool;
+        void *_private;
+        struct jk_workerEnv *workerEnv;
+        struct jk_map *map;
+
+        char *file;
+
+        char *section;
+        struct jk_map *cfgData;
+        /* Only one thread can update the config
+         */
+        struct jk_mutex *cs;
+        time_t mtime;
+    };
+
+    int jk2_config_setProperty(struct jk_env *env, struct jk_config *cfg,
+                               struct jk_bean *mbean, char *name, char *val);
+
+    int jk2_config_setPropertyString(struct jk_env *env,
+                                     struct jk_config *cfg, char *name,
+                                     char *value);
+
+    int jk2_config_processConfigData(struct jk_env *env,
+                                     struct jk_config *cfg, int firstTime);
+
+
+    char *jk2_config_replaceProperties(struct jk_env *env, struct jk_map *m,
+                                       struct jk_pool *resultPool,
+                                       char *value);
+
+    int jk2_config_file_read(struct jk_env *env, struct jk_map *m,
+                             const char *file);
+
+    int jk2_config_processNode(struct jk_env *env, struct jk_config *cfg,
+                               char *name, int firstTime);
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif                          /* JK_CONFIG_H */
diff --git a/connectors/jk/native2/include/jk_endpoint.h b/connectors/jk/native2/include/jk_endpoint.h
new file mode 100644
index 0000000..dc9f776
--- /dev/null
+++ b/connectors/jk/native2/include/jk_endpoint.h
@@ -0,0 +1,186 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Definitions of the endpoint.
+ *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           
+ * Author:      Dan Milstein <danmil@shore.net>                            
+ * Author:      Henri Gomez <hgomez@apache.org>                               
+ * Version:     $Revision$                                          
+ ***************************************************************************/
+
+#ifndef JK_ENDPOINT_H
+#define JK_ENDPOINT_H
+
+#include "jk_global.h"
+#include "jk_env.h"
+#include "jk_map.h"
+#include "jk_service.h"
+#include "jk_logger.h"
+#include "jk_pool.h"
+#include "jk_uriMap.h"
+#include "jk_msg.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+    struct jk_endpoint;
+    struct jk_stat;
+    struct jk_ws_service;
+    struct jk_logger;
+    struct jk_map;
+    typedef struct jk_endpoint jk_endpoint_t;
+    typedef struct jk_stat jk_stat_t;
+
+/* XXX replace worker with channel, endpoints are specific to channels not workers */
+
+/*
+ * The endpoint 'class', which represents one end of a connection to the
+ * servlet engine.  Basically, supports nothing other than forwarding the
+ * request to the servlet engine.  Endpoints can be persistent (as with
+ * ajp13/ajp14, where a single connection is reused many times), or can last for a
+ * single request (as with ajp12, where a new connection is created for
+ * every request).
+ *
+ * An endpoint for a given protocol is obtained by the web server plugin
+ * from a worker object for that protocol.  See below for details.
+ *
+ * As with all the core jk classes, this is essentially an abstract base
+ * class which is implemented/extended by classes which are specific to a
+ * particular protocol.  By using an abstract base class in this manner,
+ * plugins can be written for different servers (e.g. IIS, Apache) without
+ * the plugins having to worry about which protocol they are talking.
+ *
+ * This particular OO-in-C system uses a 'endpoint_private' pointer to
+ * point to the protocol-specific data/functions.  So in the subclasses, the
+ * methods do most of their work by getting their hands on the
+ * endpoint_private pointer and then using that to get at the functions for
+ * their protocol.
+ *
+ * Try imagining this as a 'public abstract class', and the
+ * endpoint_private pointer as a sort of extra 'this' reference.  Or
+ * imagine that you are seeing the internal vtables of your favorite OO
+ * language.  Whatever works for you.
+ *
+ * See jk_ajp13_worker.c/jk_ajp14_worker.c and jk_ajp12_worker.c for examples.  
+ */
+    struct jk_endpoint
+    {
+        struct jk_bean *mbean;
+
+        /* Parent
+         */
+        struct jk_worker *worker;
+
+        struct jk_workerEnv *workerEnv;
+
+    /** Data specific to a channel connection. Usually a struct
+     * containing info about the active connection ( sd, jniEnv, etc ).
+     */
+        void *channelData;
+
+        /* Channel-specific data. Usually a file descriptor. ( avoids
+           using a struct for typical socket channels )
+         */
+        int sd;
+
+    /** Current request beeing processed.
+     *  Used by JNI worker mostly ( XXX remove it, pass it explicitely )
+     */
+        struct jk_ws_service *currentRequest;
+
+    /** Connection pool. Used to store temporary data. It'll be
+     *  recycled after each transaction.
+     *  XXX Do we need it ? env->tmpPool can be used as well.
+     */
+        struct jk_pool *cPool;
+
+        /* Buffers */
+
+        /* Incoming messages ( from tomcat ). Will be overriten after each
+           message, you must save any data you want to keep.
+         */
+        struct jk_msg *reply;
+
+        /* Outgoing messages ( from server ). If the handler will return
+           JK_HANDLER_RESPONSE this message will be sent to tomcat
+         */
+        struct jk_msg *post;
+
+        /* original request storage ( XXX do we need it ? )
+         */
+        struct jk_msg *request;
+
+        char *readBuf;
+        int bufPos;
+
+        /* JK_TRUE if we can recover by sending the request to a different
+         * worker. This happens if only the request header and initial body
+         * chunk has been set. 
+         * 
+         * JK_FALSE if we already received data from a tomcat instance. In
+         * this case there is no point in retrying the current request and
+         * we must fail.
+         *
+         * The connection with the current tomcat is closed in any case.
+         */
+        int recoverable;
+
+        /* The struct will be created in shm if available
+         */
+        struct jk_stat *stats;
+    };
+
+/** Statistics collected per endpoint
+ */
+    struct jk_stat
+    {
+        /* Number of requests served by this worker and the number of errors */
+        int reqCnt;
+        int errCnt;
+
+        int connected;
+
+        int workerId;
+        /* Active request
+         */
+        char active[64];
+
+        /* Time when this endpoint has opened a connection to
+           tomcat
+         */
+        apr_time_t connectedTime;
+        /* Total time ( for average - divide by reqCnt )
+           and maxTime for requests.
+         */
+        apr_time_t totalTime;
+        apr_time_t maxTime;
+        /* Last request - time of start, time when we start the ajp protocol, end time
+         */
+        apr_time_t startTime;
+        apr_time_t jkStartTime;
+        apr_time_t endTime;
+    };
+
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif                          /* JK_ENDPOINT_H */
diff --git a/connectors/jk/native2/include/jk_env.h b/connectors/jk/native2/include/jk_env.h
new file mode 100644
index 0000000..947eba8
--- /dev/null
+++ b/connectors/jk/native2/include/jk_env.h
@@ -0,0 +1,213 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef JK_ENV_H
+#define JK_ENV_H
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+#include "jk_logger.h"
+#include "jk_pool.h"
+#include "jk_map.h"
+#include "jk_worker.h"
+#include "jk_bean.h"
+#include "jk_mutex.h"
+
+#define JK_LINE __FILE__,__LINE__
+
+
+/** 
+ *  Common environment for all jk functions. Provide uniform
+ *  access to pools, logging, factories and other 'system' services.
+ * 
+ * 
+ *
+ * ( based on jk_worker.c, jk_worker_list.c )
+ * @author Gal Shachor <shachor@il.ibm.com>                           
+ * @author Henri Gomez <hgomez@apache.org>                               
+ * @author Costin Manolache
+ * 
+ */
+    struct jk_pool;
+    struct jk_env;
+    struct jk_logger;
+    struct jk_map;
+    struct jk_bean;
+    typedef struct jk_env jk_env_t;
+
+    extern struct jk_env *jk_env_globalEnv;
+
+/** Get a pointer to the jk_env. We could support multiple 
+ *  env 'instances' in future - for now it's a singleton.
+ */
+    jk_env_t *JK_METHOD jk2_env_getEnv(char *id, struct jk_pool *pool);
+
+    struct jk_exception
+    {
+        char *file;
+        int line;
+
+        char *type;
+        char *msg;
+
+        struct jk_exception *next;
+    };
+
+    typedef struct jk_exception jk_exception_t;
+
+/**
+ *  The env will be used in a similar way with the JniEnv, to provide 
+ *  access to various low level services ( pool, logging, system properties )
+ *  in a consistent way. In time we should have all methods follow 
+ *  the same pattern, with env as a first parameter, then the object ( this )
+ *  and the other methods parameters.  
+ */
+    struct jk_env
+    {
+        struct jk_logger *l;
+        struct jk_pool *globalPool;
+
+    /** Pool used for local allocations. It'll be reset when the
+        env is released ( the equivalent of 'detach' ). Can be
+        used for temp. allocation of small objects.
+    */
+        struct jk_pool *tmpPool;
+
+        /* -------------------- Get/release ent -------------------- */
+
+    /** Get an env instance. Must be called from each thread. The object
+     *  can be reused in the thread, or it can be get/released on each used.
+     *
+     *  The env will store the exception status and the tmp pool - the pool will
+     *  be recycled when the env is released, use it only for tmp things.
+     */
+        struct jk_env *(JK_METHOD * getEnv) (struct jk_env * parent);
+
+    /** Release the env instance. The tmpPool will be recycled.
+     */
+        int (JK_METHOD * releaseEnv) (struct jk_env * parent,
+                                      struct jk_env * chld);
+
+        int (JK_METHOD * recycleEnv) (struct jk_env * env);
+
+        /* -------------------- Exceptions -------------------- */
+
+        /* Exceptions.
+         *   TODO: create a 'stack trace' (i.e. a stack of errors )
+         *   TODO: set 'error state'
+         *  XXX Not implemented/not used
+         */
+        void (JK_METHOD * jkThrow) (jk_env_t *env,
+                                    const char *file, int line,
+                                    const char *type, const char *fmt, ...);
+
+    /** re-throw the exception and record the current pos.
+     *  in the stack trace
+     *  XXX Not implemented/not used
+     */
+        void (JK_METHOD * jkReThrow) (jk_env_t *env,
+                                      const char *file, int line);
+
+        /* Last exception that occured
+         *  XXX Not implemented/not used
+         */
+        struct jk_exception *(JK_METHOD * jkException) (jk_env_t *env);
+
+    /** Clear the exception state
+     *  XXX Not implemented/not used
+     */
+        void (JK_METHOD * jkClearException) (jk_env_t *env);
+
+        /* -------------------- Object management -------------------- */
+        /* Register types, create instances, get by name */
+
+    /** Create an object using the name. Use the : separated prefix as
+     *  type. XXX This should probably replace createInstance.
+     *
+     *  @param parentPool  The pool of the parent. The object is created in its own pool,
+     *                     but if the parent is removed all childs will be removed as well. Use a long
+     *                     lived pool ( env->globalPool, workerEnv->pool ) if you don't want this.
+     *  @param objName. It must follow the documented convention, with the type as prefix, then ':'
+     */
+        struct jk_bean *(*createBean) (struct jk_env * env,
+                                       struct jk_pool * parentPool,
+                                       char *objName);
+
+    /** Same as createBean, but pass the split name
+     */
+        struct jk_bean *(*createBean2) (struct jk_env * env,
+                                        struct jk_pool * parentPool,
+                                        char *type, char *localName);
+
+    /** Register an alias for a name ( like the local part, etc ), for simpler config.
+     */
+        void (JK_METHOD * alias) (struct jk_env * env, const char *name,
+                                  const char *alias);
+
+    /** Get an object by name, using the full name
+     */
+        void *(JK_METHOD * getByName) (struct jk_env * env, const char *name);
+
+    /** Get an object by name, using the split name ( type + localName )
+     */
+        void *
+            (JK_METHOD * getByName2) (struct jk_env * env, const char *type,
+                                      const char *localName);
+
+    /** Return the configuration object
+     */
+        struct jk_bean *
+            (JK_METHOD * getBean) (struct jk_env * env, const char *name);
+
+    /** Return the configuration object
+     */
+        struct jk_bean *
+            (JK_METHOD * getBean2) (struct jk_env * env, const char *type,
+                                    const char *localName);
+
+    /** Register a factory for a type ( channel, worker ).
+     */
+        void (JK_METHOD * registerFactory) (jk_env_t *env, const char *type,
+                                            jk_env_objectFactory_t factory);
+
+    /** If APR is used, return a global pool
+     */
+        void *(JK_METHOD * getAprPool) (jk_env_t *env);
+        void (JK_METHOD * setAprPool) (jk_env_t *env, void *aprPool);
+
+        /* private */
+        struct jk_map *_registry;
+        struct jk_map *_objects;
+        struct jk_objCache *envCache;
+        struct jk_exception *lastException;
+        int id;
+        int debug;
+        char *soName;
+    };
+
+    void JK_METHOD jk2_registry_init(jk_env_t *env);
+
+    char *JK_METHOD jk2_env_itoa(jk_env_t *env, int i);
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif
diff --git a/connectors/jk/native2/include/jk_global.h b/connectors/jk/native2/include/jk_global.h
new file mode 100644
index 0000000..6206250
--- /dev/null
+++ b/connectors/jk/native2/include/jk_global.h
@@ -0,0 +1,307 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Global definitions and include files that should exist     *
+ *              anywhere                                                   *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                               *
+ ***************************************************************************/
+
+#ifndef JK_GLOBAL_H
+#define JK_GLOBAL_H
+
+#include "apr.h"
+#include "apr_lib.h"
+#include "apr_general.h"
+#include "apr_strings.h"
+#include "apr_network_io.h"
+#include "apr_errno.h"
+#include "apr_version.h"
+#include "apr_time.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+#include <ctype.h>
+
+#ifdef AS400
+/*XXX: why is this include here in common? */
+#include "ap_config.h"
+extern char *strdup(const char *str);
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/************** START OF AREA TO MODIFY BEFORE RELEASING *************/
+#define JK_VERMAJOR     2
+#define JK_VERMINOR     0
+#define JK_VERFIX       5
+#define JK_VERSTRING    "2.0.5"
+
+/* Beta number */
+#define JK_VERBETA      0
+#define JK_BETASTRING   "1"
+/* set JK_VERISRELEASE to 1 when release (do not forget to commit!) */
+#define JK_VERISRELEASE 0
+/************** END OF AREA TO MODIFY BEFORE RELEASING *************/
+
+#define PACKAGE "mod_jk2/"
+/* Build JK_EXPOSED_VERSION and JK_VERSION */
+#define JK_EXPOSED_VERSION_INT PACKAGE JK_VERSTRING
+
+
+#if ( JK_VERISRELEASE == 1 )
+#define JK_RELEASE_STR  JK_EXPOSED_VERSION_INT
+#else
+#define JK_RELEASE_STR  JK_EXPOSED_VERSION_INT "-dev"
+#endif
+
+#if ( JK_VERBETA == 0 )
+#define JK_EXPOSED_VERSION JK_RELEASE_STR
+#undef JK_VERBETA
+#define JK_VERBETA 255
+#else
+#define JK_EXPOSED_VERSION JK_RELEASE_STR "-beta-" JK_BETASTRING
+#endif
+
+#define JK_MAKEVERSION(major, minor, fix, beta) (((major) << 24) + ((minor) << 16) + ((fix) << 8) + (beta))
+
+#define JK_VERSION JK_MAKEVERSION(JK_VERMAJOR, JK_VERMINOR, JK_VERFIX, JK_VERBETA)
+
+
+#define AJP_DEF_RETRY_ATTEMPTS    (2)
+#define AJP13_PROTO 13
+
+#define AJP13_DEF_HOST "127.0.0.1"
+#ifdef NETWARE
+#define AJP13_DEF_PORT 9009     /* default to 9009 since 8009 is used by OS */
+#else
+#define AJP13_DEF_PORT 8009
+#endif
+
+#ifdef WIN32
+#include <windows.h>
+#include <winsock.h>
+#else
+#include <unistd.h>
+#ifdef __NOVELL_LIBC__
+#include <novsock2.h>
+#else
+#include <netdb.h>
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#ifndef NETWARE
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#if !defined(_OSD_POSIX) && !defined(AS400) && !defined(CYGWIN)
+#include <sys/socketvar.h>
+#endif
+#if !defined(HPUX11) && !defined(AS400)
+#include <sys/select.h>
+#endif
+#endif
+
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#endif
+#endif
+
+#ifdef WIN32
+/* define snprint to match windows version */
+#define snprintf _snprintf
+#endif
+
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+/* We'll use APR whenever it's possible. However for a transition period and
+   for essential components we can build a minimal mod_jk without APR.
+*/
+
+#define JK_OK APR_SUCCESS
+#define JK_ERR APR_OS_START_USEERR
+/* Individual jk errors */
+
+#define JK_
+
+/* Some compileers support 'inline'. How to guess ?
+   #define INLINE inline
+ */
+
+/* For VC the __inline keyword is available in both C and C++.*/
+#if defined(_WIN32) && defined(_MSC_VER)
+#define INLINE __inline
+#else
+/* XXX: Other compilers? */
+#define INLINE
+#endif
+
+#define JK_WORKER_FILE_TAG      ("worker_file")
+#define JK_MOUNT_FILE_TAG       ("worker_mount_file")
+#define JK_LOG_LEVEL_TAG        ("log_level")
+#define JK_LOG_FILE_TAG         ("log_file")
+#define JK_WORKER_NAME_TAG      ("worker")
+
+#define JK_WORKER_FILE_DEF  ("${serverRoot}/conf/workers2.properties")
+#define JK_LOG_LEVEL_DEF    ("emerg")
+
+#define JK_TRUE  (1)
+#define JK_FALSE (0)
+
+#define JK_LF (10)
+#define JK_CR (13)
+
+#define JK_SESSION_IDENTIFIER "JSESSIONID"
+#define JK_PATH_SESSION_IDENTIFIER ";jsessionid"
+
+#if defined(WIN32)
+#define SO_EXTENSION "dll"
+#else
+#if defined(NETWARE)
+#define SO_EXTENSION "nlm"
+#else
+#define SO_EXTENSION "so"
+#endif
+#endif
+
+#ifndef ARCH
+#define ARCH "i386"
+#endif
+
+#if defined(WIN32) || defined(NETWARE)
+#ifdef __GNUC__
+#define JK_METHOD
+#define C_LEVEL_TRY_START
+#define C_LEVEL_TRY_END
+#define C_LEVEL_FINALLY_START
+#define C_LEVEL_FINALLY_END
+#else
+#define JK_METHOD __stdcall
+#define C_LEVEL_TRY_START       __try {
+#define C_LEVEL_TRY_END         }
+#define C_LEVEL_FINALLY_START   __finally {
+#define C_LEVEL_FINALLY_END     }
+#endif
+#define PATH_SEPERATOR          (';')
+#define PATH_SEPARATOR_STR      (";")
+#define FILE_SEPERATOR          ('\\')
+#define FILE_SEPARATOR_STR      ("\\")
+#define PATH_ENV_VARIABLE       ("PATH")
+
+    /* incompatible names... */
+#ifndef strcasecmp
+#define strcasecmp stricmp
+#endif
+#ifndef strncasecmp
+#define strncasecmp strnicmp
+#endif
+
+#ifndef __NOVELL_LIBC__
+#ifndef vsnprintf
+#define vsnprintf _vsnprintf
+#endif
+#endif
+#else
+#define JK_METHOD
+#define C_LEVEL_TRY_START
+#define C_LEVEL_TRY_END
+#define C_LEVEL_FINALLY_START
+#define C_LEVEL_FINALLY_END
+#define PATH_SEPERATOR          (':')
+#define FILE_SEPERATOR          ('/')
+#define PATH_SEPARATOR_STR      (":")
+#define FILE_SEPARATOR_STR      ("/")
+#define PATH_ENV_VARIABLE       ("LD_LIBRARY_PATH")
+#define HAVE_UNIXSOCKETS
+#endif
+
+/*
+ * JK options
+ */
+
+#define JK_OPT_FWDURIMASK           0x0003
+
+#define JK_OPT_FWDURICOMPAT         0x0001
+#define JK_OPT_FWDURICOMPATUNPARSED 0x0002
+#define JK_OPT_FWDURIESCAPED        0x0003
+
+#define JK_OPT_FWDURIDEFAULT        JK_OPT_FWDURICOMPAT
+
+#define JK_OPT_FWDKEYSIZE           0x0004
+
+
+/*
+ * RECOVERY STRATEGY
+ *
+ * The recovery strategy determine how web-server will handle tomcat crash after POST error.
+ * By default, we use the current strategy, which is to resend request to next tomcat.
+ * To abort if tomcat failed after receiving request, recovers_opts should be 1 or 3
+ * To abort if tomcat failed after sending headers to client, recovers_opts should be 2 or 3
+ */
+
+#define JK_OPT_RECOSTRATEGYMASK			0x0030
+
+#define JK_OPT_RECO_ABORTIFTCGETREQUEST	0x0010  /* DONT RECOVER IF TOMCAT FAIL AFTER RECEIVING REQUEST */
+#define JK_OPT_RECO_ABORTIFTCSENDHEADER	0x0020  /* DONT RECOVER IF TOMCAT FAIL AFTER SENDING HEADERS */
+
+#define JK_OPT_RECOSTRATEGYDEFAULT		0x0000
+
+
+/* Check for EBCDIC systems */
+
+/* Check for Apache 2.0 running on an EBCDIC system */
+#if APR_CHARSET_EBCDIC
+
+#define USE_CHARSET_EBCDIC
+#define jk_xlate_to_ascii(b, l) ap_xlate_proto_to_ascii(b, l)
+#define jk_xlate_from_ascii(b, l) ap_xlate_proto_from_ascii(b, l)
+
+#else                           /* APR_CHARSET_EBCDIC */
+
+/* Check for Apache 1.3 running on an EBCDIC system */
+#ifdef CHARSET_EBCDIC
+
+#define USE_CHARSET_EBCDIC
+#define jk_xlate_to_ascii(b, l) ebcdic2ascii(b, b, l)
+#define jk_xlate_from_ascii(b, l) ascii2ebcdic(b, b, l)
+
+#else                           /* CHARSET_EBCDIC */
+
+/* We're in on an ASCII system */
+
+#define jk_xlate_to_ascii(b, l) /* NOOP */
+#define jk_xlate_from_ascii(b, l)       /* NOOP */
+
+#endif                          /* CHARSET_EBCDIC */
+
+#endif                          /* APR_CHARSET_EBCDIC */
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif                          /* JK_GLOBAL_H */
diff --git a/connectors/jk/native2/include/jk_handler.h b/connectors/jk/native2/include/jk_handler.h
new file mode 100644
index 0000000..f2f5db9
--- /dev/null
+++ b/connectors/jk/native2/include/jk_handler.h
@@ -0,0 +1,112 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Handle jk messages. Each handler will register a number of
+ * callbacks for certain message types. 
+ *
+ * This is based on a simple generalization of the code in ajp13,
+ * with the goal of making the system extensible for ajp14.
+ *
+ * @author Costin Manolache
+ */
+
+#ifndef JK_HANDLER_H
+#define JK_HANDLER_H
+
+#include "jk_global.h"
+#include "jk_env.h"
+#include "jk_map.h"
+#include "jk_workerEnv.h"
+#include "jk_logger.h"
+#include "jk_pool.h"
+#include "jk_uriMap.h"
+#include "jk_worker.h"
+#include "jk_endpoint.h"
+#include "jk_msg.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+/* Return codes from the handler method
+ */
+
+/**
+ * Message does not have a response, jk will continue
+ * to wait.
+ */
+#define JK_HANDLER_OK        0
+
+/** Message requires a response. The handler will prepare
+ *  the response in e->post message.
+ */
+#define JK_HANDLER_RESPONSE  1
+
+/** This is the last message ( in a sequence ). The original
+ *  transaction is now completed.
+ */
+#define JK_HANDLER_LAST      2
+
+/** An error ocurred during handler execution. The processing
+ *  will be interrupted, but the connection remains open.
+ *  After an error handler we should continue receiving messages,
+ *  but ignore them and send an error message on the first ocassion.
+ */
+#define JK_HANDLER_ERROR     3
+
+/** A fatal error ocurred, we should close the channel
+ *  and report the error. This should be used if something unexpected,
+ *  from which we can't recover happens. ( for example an unexpected packet,
+ *  an invalid code, etc ).
+ */
+#define JK_HANDLER_FATAL     4
+
+    struct jk_msg;
+    struct jk_ws_service;
+    struct jk_endpoint;
+    struct jk_logger;
+    struct jk_workerEnv;
+    struct jk_env;
+
+    typedef int (JK_METHOD * jk_handler_callback) (struct jk_env * env,
+                                                   void *target,
+                                                   struct jk_endpoint * ae,
+                                                   struct jk_msg * msg);
+
+    struct jk_handler;
+    typedef struct jk_handler jk_handler_t;
+
+    struct jk_handler
+    {
+        struct jk_workerEnv *workerEnv;
+
+        char *name;
+        int messageId;
+
+        jk_handler_callback callback;
+
+        int (JK_METHOD * init) (struct jk_env * env,
+                                struct jk_handler * handler,
+                                struct jk_workerEnv * workerEnv);
+    };
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif
diff --git a/connectors/jk/native2/include/jk_logger.h b/connectors/jk/native2/include/jk_logger.h
new file mode 100644
index 0000000..3796432
--- /dev/null
+++ b/connectors/jk/native2/include/jk_logger.h
@@ -0,0 +1,94 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Logger object definitions                                  *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef JK_LOGGER_H
+#define JK_LOGGER_H
+
+#include "jk_env.h"
+#include "jk_global.h"
+#include "jk_map.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+    struct jk_map;
+    struct jk_env;
+    struct jk_logger;
+    typedef struct jk_logger jk_logger_t;
+
+/* Logger object.
+ *  XXX level should be moved per component ( to control the generation of messages ),
+ *  the level param in the param should be used only as information ( to be displayed
+ *  in the log ).
+ */
+    struct jk_logger
+    {
+        struct jk_bean *mbean;
+        char *name;
+        void *logger_private;
+        int level;
+
+        int (JK_METHOD * init) (struct jk_env * env, jk_logger_t *_this);
+
+        void (JK_METHOD * close) (struct jk_env * env, jk_logger_t *_this);
+
+        int (JK_METHOD * log) (struct jk_env * env,
+                               jk_logger_t *_this,
+                               int level, const char *what);
+
+        int (JK_METHOD * jkLog) (struct jk_env * env,
+                                 jk_logger_t *_this,
+                                 const char *file,
+                                 int line, int level, const char *fmt, ...);
+
+        int (JK_METHOD * jkVLog) (struct jk_env * env,
+                                  jk_logger_t *_this,
+                                  const char *file,
+                                  int line,
+                                  int level, const char *fmt, va_list msg);
+
+    };
+
+#define JK_LOG_DEBUG_LEVEL 0
+#define JK_LOG_INFO_LEVEL  1
+#define JK_LOG_ERROR_LEVEL 2
+#define JK_LOG_EMERG_LEVEL 3
+
+#define JK_LOG_DEBUG_VERB   "debug"
+#define JK_LOG_INFO_VERB    "info"
+#define JK_LOG_ERROR_VERB   "error"
+#define JK_LOG_EMERG_VERB   "emerg"
+
+#define JK_LOG_DEBUG __FILE__,__LINE__,JK_LOG_DEBUG_LEVEL
+#define JK_LOG_INFO  __FILE__,__LINE__,JK_LOG_INFO_LEVEL
+#define JK_LOG_ERROR __FILE__,__LINE__,JK_LOG_ERROR_LEVEL
+#define JK_LOG_EMERG __FILE__,__LINE__,JK_LOG_EMERG_LEVEL
+
+    int jk2_logger_file_parseLogLevel(struct jk_env *env, const char *level);
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif                          /* JK_LOGGER_H */
diff --git a/connectors/jk/native2/include/jk_map.h b/connectors/jk/native2/include/jk_map.h
new file mode 100644
index 0000000..a99b57e
--- /dev/null
+++ b/connectors/jk/native2/include/jk_map.h
@@ -0,0 +1,102 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Map object header file                                     *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef JK_MAP_H
+#define JK_MAP_H
+
+#include "jk_pool.h"
+#include "jk_env.h"
+#include "jk_logger.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+    struct jk_logger;
+    struct jk_pool;
+    struct jk_map;
+    struct jk_env;
+    typedef struct jk_map jk_map_t;
+
+/** Map interface. This include only the basic operations, and
+ *   supports both name and index access.
+ */
+    struct jk_map
+    {
+
+        void *(*get) (struct jk_env * env, struct jk_map * _this,
+                      const char *name);
+
+    /** Set the value, overriding previous values */
+        int (*put) (struct jk_env * env, struct jk_map * _this,
+                    const char *name, void *value, void **oldValue);
+
+    /** Multi-value support */
+        int (*add) (struct jk_env * env, struct jk_map * _this,
+                    const char *name, void *value);
+
+        /* Similar with apr_table, elts can be accessed by id
+         */
+
+        int (*size) (struct jk_env * env, struct jk_map * _this);
+
+        char *(*nameAt) (struct jk_env * env, struct jk_map * m, int pos);
+
+        void *(*valueAt) (struct jk_env * env, struct jk_map * m, int pos);
+
+        /* Admin operations */
+        void (*init) (struct jk_env * env, struct jk_map * m,
+                      int initialSize, void *wrappedNativeObj);
+
+
+        /* Empty the map, remove all values ( but it can keep
+           allocated storage for [] )
+         */
+        void (*clear) (struct jk_env * env, struct jk_map * m);
+
+        /* Sort the map, 
+         */
+        void (*sort) (struct jk_env * env, struct jk_map * m);
+
+        struct jk_pool *pool;
+        void *_private;
+
+        /* For debuging purpose. NULL if not supported.
+           The default impl will set them to the content
+         */
+        char **keys;
+        void **values;
+    };
+
+    int jk2_map_default_create(struct jk_env *env, jk_map_t **m,
+                               struct jk_pool *pool);
+
+    int jk2_map_read(struct jk_env *env, jk_map_t *m, const char *file);
+
+    char *jk2_map_concatKeys(struct jk_env *env, jk_map_t *map, char *delim);
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif                          /* JK_MAP_H */
diff --git a/connectors/jk/native2/include/jk_md5.h b/connectors/jk/native2/include/jk_md5.h
new file mode 100644
index 0000000..204cb5c
--- /dev/null
+++ b/connectors/jk/native2/include/jk_md5.h
@@ -0,0 +1,86 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/*
+ * This is work is derived from material Copyright RSA Data Security, Inc.
+ *
+ * The RSA copyright statement and Licence for that original material is
+ * included below. This is followed by the Apache copyright statement and
+ * licence for the modifications made to that material.
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+   rights reserved.
+
+   License to copy and use this software is granted provided that it
+   is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+   Algorithm" in all material mentioning or referencing this software
+   or this function.
+
+   License is also granted to make and use derivative works provided
+   that such works are identified as "derived from the RSA Data
+   Security, Inc. MD5 Message-Digest Algorithm" in all material
+   mentioning or referencing the derived work.
+
+   RSA Data Security, Inc. makes no representations concerning either
+   the merchantability of this software or the suitability of this
+   software for any particular purpose. It is provided "as is"
+   without express or implied warranty of any kind.
+
+   These notices must be retained in any copies of any part of this
+   documentation and/or software.
+ */
+
+#ifndef JK_APACHE_MD5_H
+#define JK_APACHE_MD5_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* MD5.H - header file for MD5.C */
+
+#define JK_MD5_DIGESTSIZE 16
+
+/* JK_UINT4 defines a four byte word */
+    typedef unsigned int JK_UINT4;
+
+/* MD5 context. */
+    typedef struct
+    {
+        JK_UINT4 state[4];      /* state (ABCD) */
+        JK_UINT4 count[2];      /* number of bits, modulo 2^64 (lsb first) */
+        unsigned char buffer[64];       /* input buffer */
+    } JK_MD5_CTX;
+
+/*
+ * Define the Magic String prefix that identifies a password as being
+ * hashed using our algorithm.
+ */
+#define JK_MD5PW_ID "$apr1$"
+#define JK_MD5PW_IDLEN 6
+
+    char *JK_METHOD jk2_hextocstr(unsigned char *org, char *dst, int n);
+    char *JK_METHOD jk2_md5(const unsigned char *org,
+                            const unsigned char *org2, char *dst);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif                          /* !JK_APACHE_MD5_H */
diff --git a/connectors/jk/native2/include/jk_msg.h b/connectors/jk/native2/include/jk_msg.h
new file mode 100644
index 0000000..1d57bd0
--- /dev/null
+++ b/connectors/jk/native2/include/jk_msg.h
@@ -0,0 +1,193 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef JK_REQ_H
+#define JK_REQ_H
+
+#include "jk_global.h"
+#include "jk_env.h"
+#include "jk_logger.h"
+#include "jk_pool.h"
+#include "jk_endpoint.h"
+#include "jk_service.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+    struct jk_env;
+    struct jk_msg;
+    typedef struct jk_msg jk_msg_t;
+
+    struct jk_endpoint;
+    struct jk_ws_service;
+    struct jk_logger;
+
+#define DEF_BUFFER_SZ (8*1024)
+#define AJP13_MAX_SEND_BODY_SZ      (DEF_BUFFER_SZ - 6)
+
+/**
+ * Abstract interface to marshalling. Different encodings and
+ * communication mechanisms can be supported.
+ *
+ * This object is recyclable, but is not thread safe - it can
+ * handle a single message at a time.
+ *
+ * It is created by a channel( XXX endpoint ? )
+ * and can be sent/received only on that channel.
+ *
+ * XXX Lifecycle: on send the buffer will be reused after send
+ *     On receive - it will be recycled after reset() or equiv.
+ *     Same as on the java side.
+ *
+ * @author Costin Manolache
+ */
+    struct jk_msg
+    {
+    /** Human-readable method name */
+        char *name;
+
+    /** Method id - to be sent in the packet
+     */
+        int id;
+
+    /** Header length for this message
+     */
+        int headerLength;
+
+        /*
+         * Prepare the buffer for a new invocation 
+         */
+        void (*reset) (struct jk_env * env, struct jk_msg * _this);
+
+        /*
+         * Finalize the buffer before sending - set length fields, etc
+         */
+        void (*end) (struct jk_env * env, struct jk_msg * _this);
+
+        int (*checkHeader) (struct jk_env * env, struct jk_msg * _this,
+                            struct jk_endpoint * e);
+
+        /*
+         * Dump the buffer header
+         *   @param err Message text
+         */
+        void (*dump) (struct jk_env * env, struct jk_msg * _this, char *err);
+
+        int (*appendByte) (struct jk_env * env, struct jk_msg * _this,
+                           unsigned char val);
+
+        int (*appendBytes) (struct jk_env * env, struct jk_msg * _this,
+                            const unsigned char *param, const int len);
+
+        int (*appendInt) (struct jk_env * env, struct jk_msg * _this,
+                          const unsigned short val);
+
+        int (*appendLong) (struct jk_env * env, struct jk_msg * _this,
+                           const unsigned long val);
+
+        int (*appendString) (struct jk_env * env, struct jk_msg * _this,
+                             const char *param);
+
+        int (*appendAsciiString) (struct jk_env * env, struct jk_msg * _this,
+                                  const char *param);
+
+        int (*appendMap) (struct jk_env * env, struct jk_msg * _this,
+                          struct jk_map * map);
+
+        unsigned char (*getByte) (struct jk_env * env, struct jk_msg * _this);
+
+        unsigned short (*getInt) (struct jk_env * env, struct jk_msg * _this);
+
+    /** Look at the next int, without reading it
+     */
+        unsigned short (*peekInt) (struct jk_env * env,
+                                   struct jk_msg * _this);
+
+        unsigned long (*getLong) (struct jk_env * env, struct jk_msg * _this);
+
+    /** Return a string. 
+        The buffer is internal to the message, you must save
+        or make sure the message lives long enough.
+     */
+        char *(*getString) (struct jk_env * env, struct jk_msg * _this);
+
+    /** Return a byte[] and it's length.
+     *  The buffer is internal to the message, you must save
+     * or make sure the message lives long enough.
+     */
+        unsigned char *(*getBytes) (struct jk_env * env,
+                                    struct jk_msg * _this, int *len);
+
+    /** Read a map structure from the message. The map is encoded
+     *  as an int count and then the NV pairs.
+     *
+     *  The content will not be copied - but point to the msg's buffer.
+     *  If you want to use the map after the msg becomes invalid, you need
+     *  to copy it.
+     */
+        int (*getMap) (struct jk_env * env, struct jk_msg * _this,
+                       struct jk_map * map);
+
+    /** 
+     * Special method. Will read data from the server and add them as
+     * bytes. It is equivalent with jk2_requtil_readFully() in a buffer
+     * and then jk_msg_appendBytes(), except that we use directly the
+     * internal buffer.
+     *
+     * Returns -1 on error, else number of bytes read
+     */
+        int (*appendFromServer) (struct jk_env * env,
+                                 struct jk_msg * _this,
+                                 struct jk_ws_service * r,
+                                 struct jk_endpoint * ae, int len);
+
+    /** 
+     * Clone the msg buf into another one, sort of clone.
+     *
+     * Returns -1 on error, else number of bytes copied
+     */
+        int (*copy) (struct jk_env * env,
+                     struct jk_msg * _this, struct jk_msg * dst);
+
+        void *_privatePtr;
+
+        /* Temporary, don't use */
+        struct jk_pool *pool;
+
+        unsigned char *buf;
+        int pos;
+        int len;
+        int maxlen;
+
+        /* JK_TRUE if the message is sent/received by the server ( tomcat ).
+         */
+        int serverSide;
+    };
+
+/* Temp */
+    jk_msg_t *jk2_msg_ajp_create(struct jk_env *env, struct jk_pool *p,
+                                 int buffSize);
+
+    jk_msg_t *jk2_msg_ajp_create2(struct jk_env *env, struct jk_pool *pool,
+                                  char *buf, int buffSize);
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif
diff --git a/connectors/jk/native2/include/jk_mutex.h b/connectors/jk/native2/include/jk_mutex.h
new file mode 100644
index 0000000..646a8c0
--- /dev/null
+++ b/connectors/jk/native2/include/jk_mutex.h
@@ -0,0 +1,104 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef JK_MUTEX_H
+#define JK_MUTEX_H
+
+#include "jk_global.h"
+#include "jk_env.h"
+#include "jk_logger.h"
+#include "jk_pool.h"
+#include "jk_msg.h"
+#include "jk_service.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+    struct jk_env;
+    struct jk_mutex;
+
+#if APR_HAS_THREADS
+#include "apr_thread_mutex.h"
+#elif defined( WIN32 )
+#include <windows.h>
+#elif defined( _REENTRANT )
+#include <pthread.h>
+#endif
+
+
+    typedef struct jk_mutex jk_mutex_t;
+
+#define MUTEX_LOCK 4
+#define MUTEX_TRYLOCK 5
+#define MUTEX_UNLOCK 6
+
+/**
+ *  Interprocess mutex support. This is a wrapper to APR.
+ *
+ * @author Costin Manolache
+ */
+    struct jk_mutex
+    {
+        struct jk_bean *mbean;
+
+        struct jk_pool *pool;
+
+    /** Name of the mutex */
+        char *fname;
+
+    /** APR mechanism */
+        int mechanism;
+
+    /** 
+     */
+        int (JK_METHOD * lock) (struct jk_env * env, struct jk_mutex * mutex);
+
+    /** 
+     */
+        int (JK_METHOD * tryLock) (struct jk_env * env,
+                                   struct jk_mutex * mutex);
+
+    /** 
+     */
+        int (JK_METHOD * unLock) (struct jk_env * env,
+                                  struct jk_mutex * mutex);
+
+        /* Private data */
+        void *privateData;
+
+#if APR_HAS_THREADS
+        apr_thread_mutex_t *threadMutex;
+#elif defined( WIN32 )
+        CRITICAL_SECTION threadMutex;
+#elif defined( _REENTRANT )
+        pthread_mutex_t threadMutex;
+#else
+        void *threadMutex;
+#endif
+    };
+
+    int JK_METHOD jk2_mutex_invoke(struct jk_env *env, struct jk_bean *bean,
+                                   struct jk_endpoint *ep, int code,
+                                   struct jk_msg *msg, int raw);
+
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif
diff --git a/connectors/jk/native2/include/jk_objCache.h b/connectors/jk/native2/include/jk_objCache.h
new file mode 100644
index 0000000..151d8b9
--- /dev/null
+++ b/connectors/jk/native2/include/jk_objCache.h
@@ -0,0 +1,94 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef JK_OBJCACHE_H
+#define JK_OBJCACHE_H
+
+#include "jk_global.h"
+#include "jk_env.h"
+#include "jk_logger.h"
+#include "jk_pool.h"
+#include "jk_msg.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+    struct jk_env;
+    struct jk_objCache;
+    struct jk_logger;
+    struct jk_pool;
+    typedef struct jk_objCache jk_objCache_t;
+
+#define JK_OBJCACHE_DEFAULT_SZ          (128)
+
+
+    jk_objCache_t *jk2_objCache_create(struct jk_env *env,
+                                       struct jk_pool *pool);
+
+/**
+ * Simple object cache ( or pool for java people - don't confuse with the
+ *  mem pool ).
+ *
+ * Used to avoid creating expensive objects, like endpoints ( which would
+ * require TCP connection on startup ).
+ *
+ * This is very simple - only one object kind, no expiry.
+ *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           
+ * Author:      Henri Gomez <hgomez@apache.org>
+ * Author:      Costin Manolache
+*/
+    struct jk_objCache
+    {
+    /** Return an object to the pool.
+     *  @return JK_FALSE the object can't be taken back, caller must free it.
+     */
+        int (*put) (struct jk_env * env, jk_objCache_t *_this, void *obj);
+
+        void *(*get) (struct jk_env * env, jk_objCache_t *_this);
+
+        int (*init) (struct jk_env * env, jk_objCache_t *_this,
+                     int cacheSize);
+
+        int (*destroy) (struct jk_env * env, jk_objCache_t *_this);
+
+    /** Cache max size. -1 for unbound ( i.e. growing ). */
+        int maxSize;
+
+        /* Current size of the table */
+        int size;
+
+    /** Number of elements in the cache.
+     *  Postition where next element will be inserted.
+     */
+        int count;
+
+        /* Sync.
+         */
+        struct jk_mutex *cs;
+
+    /** Objects in the cache */
+        void **data;
+        struct jk_pool *pool;
+    };
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif
diff --git a/connectors/jk/native2/include/jk_pool.h b/connectors/jk/native2/include/jk_pool.h
new file mode 100644
index 0000000..66951c6
--- /dev/null
+++ b/connectors/jk/native2/include/jk_pool.h
@@ -0,0 +1,146 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Memory Pool object header file                             *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+#ifndef _JK_POOL_H
+#define _JK_POOL_H
+
+#include "jk_global.h"
+#include "jk_env.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+/**
+ * @file jk_pool.h
+ * @brief Jk memory allocation
+ *
+ * Similar with apr_pools, but completely unsynchronized.
+ * XXX use same names
+ * 
+ */
+
+/*
+ * The pool atom (basic pool alocation unit) is an 8 byte long. 
+ * Each allocation (even for 1 byte) will return a round up to the 
+ * number of atoms. 
+ * 
+ * This is to help in alignment of 32/64 bit machines ...
+ * G.S
+ */
+#ifdef WIN32
+    typedef __int64 jk_pool_atom_t;
+#elif defined(AIX)
+    typedef long long jk_pool_atom_t;
+#elif defined(SOLARIS)
+    typedef long long jk_pool_atom_t;
+#elif defined(LINUX)
+    typedef long long jk_pool_atom_t;
+#elif defined(FREEBSD)
+    typedef long long jk_pool_atom_t;
+#elif defined(OS2)
+    typedef long long jk_pool_atom_t;
+#elif defined(NETWARE)
+    typedef long long jk_pool_atom_t;
+#elif defined(HPUX11)
+    typedef long long jk_pool_atom_t;
+#elif defined(IRIX)
+    typedef long long jk_pool_atom_t;
+#elif defined(AS400)
+    typedef void *jk_pool_atom_t;
+#else
+    typedef long long jk_pool_atom_t;
+#endif
+
+/* 
+ * Pool size in number of pool atoms.
+ */
+#define TINY_POOL_SIZE 256      /* Tiny 1/4K atom pool. */
+#define SMALL_POOL_SIZE 512     /* Small 1/2K atom pool. */
+#define BIG_POOL_SIZE   2*SMALL_POOL_SIZE       /* Bigger 1K atom pool. */
+#define HUGE_POOL_SIZE  2*BIG_POOL_SIZE /* Huge 2K atom pool. */
+
+    struct jk_pool;
+    struct jk_env;
+    typedef struct jk_pool jk_pool_t;
+
+
+/** The pool can grow by (m)allocating more storage, but it'll first use buf.
+*/
+    struct jk_pool
+    {
+    /** Create a child pool. Size is a hint for the
+     *  estimated needs of the child.
+     */
+        jk_pool_t *(*create) (struct jk_env * env, jk_pool_t *_this,
+                              int size);
+
+        void (*close) (struct jk_env * env, jk_pool_t *_this);
+
+    /** Empty the pool using the same space.
+     *  XXX should we rename it to clear() - consistent with apr ?
+     */
+        void (*reset) (struct jk_env * env, jk_pool_t *_this);
+
+        /* Memory allocation */
+
+        void *(*alloc) (struct jk_env * env, jk_pool_t *_this, size_t size);
+
+        void *(*realloc) (struct jk_env * env, jk_pool_t *_this, size_t size,
+                          const void *old, size_t old_sz);
+
+        void *(*calloc) (struct jk_env * env, jk_pool_t *_this, size_t size);
+
+        void *(*pstrdup) (struct jk_env * env, jk_pool_t *_this,
+                          const char *s);
+
+        void *(*pstrcat) (struct jk_env * env, jk_pool_t *_this, ...);
+
+        void *(*pstrdup2ascii) (struct jk_env * env, jk_pool_t *_this,
+                                const char *s);
+
+        void *(*pstrdup2ebcdic) (struct jk_env * env, jk_pool_t *_this,
+                                 const char *s);
+
+    /** Points to the private data. In the case of APR,
+        it's a apr_pool you can use directly */
+        void *_private;
+    };
+
+/** Create a pool. Use it for the initial allocation ( in mod_jk ). All other
+    pools should be created using the virtual method.
+
+    XXX move this to the factory
+ */
+    int jk2_pool_create(struct jk_env *env, jk_pool_t **newPool,
+                        jk_pool_t *parent, int size);
+
+    int JK_METHOD jk2_pool_apr_create(struct jk_env *env,
+                                      struct jk_pool **newPool,
+                                      struct jk_pool *parent, void *aprPool);
+
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif                          /* _JK_POOL_H */
diff --git a/connectors/jk/native2/include/jk_requtil.h b/connectors/jk/native2/include/jk_requtil.h
new file mode 100644
index 0000000..5efd801
--- /dev/null
+++ b/connectors/jk/native2/include/jk_requtil.h
@@ -0,0 +1,164 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Utils for processing various request components
+ *
+ * @author: Gal Shachor <shachor@il.ibm.com>                           
+ * @author: Henri Gomez <hgomez@apache.org>
+ * @author: Costin Manolache
+ */
+
+#ifndef JK_REQUTIL_H
+#define JK_REQUTIL_H
+
+#include "jk_global.h"
+#include "jk_channel.h"
+#include "jk_env.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+/*
+ * Frequent request headers, these headers are coded as numbers
+ * instead of strings.
+ */
+#define SC_ACCEPT               (unsigned short)0xA001
+#define SC_ACCEPT_CHARSET       (unsigned short)0xA002
+#define SC_ACCEPT_ENCODING      (unsigned short)0xA003
+#define SC_ACCEPT_LANGUAGE      (unsigned short)0xA004
+#define SC_AUTHORIZATION        (unsigned short)0xA005
+#define SC_CONNECTION           (unsigned short)0xA006
+#define SC_CONTENT_TYPE         (unsigned short)0xA007
+#define SC_CONTENT_LENGTH       (unsigned short)0xA008
+#define SC_COOKIE               (unsigned short)0xA009
+#define SC_COOKIE2              (unsigned short)0xA00A
+#define SC_HOST                 (unsigned short)0xA00B
+#define SC_PRAGMA               (unsigned short)0xA00C
+#define SC_REFERER              (unsigned short)0xA00D
+#define SC_USER_AGENT           (unsigned short)0xA00E
+
+/*
+ * Frequent response headers, these headers are coded as numbers
+ * instead of strings.
+ * 
+ * Content-Type
+ * Content-Language
+ * Content-Length
+ * Date
+ * Last-Modified
+ * Location
+ * Set-Cookie
+ * Servlet-Engine
+ * Status
+ * WWW-Authenticate
+ * 
+ */
+#define SC_RESP_CONTENT_TYPE        (unsigned short)0xA001
+#define SC_RESP_CONTENT_LANGUAGE    (unsigned short)0xA002
+#define SC_RESP_CONTENT_LENGTH      (unsigned short)0xA003
+#define SC_RESP_DATE                (unsigned short)0xA004
+#define SC_RESP_LAST_MODIFIED       (unsigned short)0xA005
+#define SC_RESP_LOCATION            (unsigned short)0xA006
+#define SC_RESP_SET_COOKIE          (unsigned short)0xA007
+#define SC_RESP_SET_COOKIE2         (unsigned short)0xA008
+#define SC_RESP_SERVLET_ENGINE      (unsigned short)0xA009
+#define SC_RESP_STATUS              (unsigned short)0xA00A
+#define SC_RESP_WWW_AUTHENTICATE    (unsigned short)0xA00B
+#define SC_RES_HEADERS_NUM          11
+
+
+
+
+/** Get header value using a lookup table. 
+ */
+    const char *jk2_requtil_getHeaderById(struct jk_env *env, int sc);
+
+/**
+ * Get method id. 
+ */
+    int jk2_requtil_getMethodId(struct jk_env *env, const char *method,
+                                unsigned char *sc);
+
+/**
+ * Get header id.
+ */
+    int jk2_requtil_getHeaderId(struct jk_env *env, const char *header_name,
+                                unsigned short *sc);
+
+/** Retrieve session id from the cookie or the parameter                      
+ * (parameter first)
+ */
+    char *jk2_requtil_getSessionId(struct jk_env *env, jk_ws_service_t *s);
+
+/** Retrieve the cookie with the given name
+ */
+    char *jk2_requtil_getCookieByName(struct jk_env *env, jk_ws_service_t *s,
+                                      const char *name);
+
+/* Retrieve the parameter with the given name
+ */
+    char *jk2_requtil_getPathParam(struct jk_env *env, jk_ws_service_t *s,
+                                   const char *name);
+
+
+/** Extract the 'route' from the session id. The route is
+ *  the id of the worker that generated the session and where all
+ *  further requests in that session will be sent.
+*/
+    char *jk2_requtil_getSessionRoute(struct jk_env *env, jk_ws_service_t *s);
+
+
+/** Initialize the request 
+ * 
+ * jk_init_ws_service
+ */
+    void jk2_requtil_initRequest(struct jk_env *env, jk_ws_service_t *s);
+
+
+    int jk2_requtil_readFully(struct jk_env *env, jk_ws_service_t *s,
+                              unsigned char *buf, unsigned len);
+
+    int jk_requtil_escapeUrl(const char *path, char *dest, int destsize);
+
+    int jk_requtil_unescapeUrl(char *url);
+
+    int jk_requtil_uriIsWebInf(char *uri);
+
+    void jk_requtil_getParents(char *name);
+
+/** return the size of the encoding of a certificate */
+    int jk_requtil_base64CertLen(int len);
+
+/** Do a base-64 encoding of the certificate */
+    int jk_requtil_base64EncodeCert(char *encoded,
+                                    const unsigned char *string, int len);
+
+    int jk2_serialize_postHead(jk_env_t *env, jk_msg_t *msg,
+                               jk_ws_service_t *r, jk_endpoint_t *ae);
+
+    int jk2_serialize_request13(jk_env_t *env, jk_msg_t *msg,
+                                jk_ws_service_t *s, jk_endpoint_t *ae);
+
+
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif
diff --git a/connectors/jk/native2/include/jk_service.h b/connectors/jk/native2/include/jk_service.h
new file mode 100644
index 0000000..a47a748
--- /dev/null
+++ b/connectors/jk/native2/include/jk_service.h
@@ -0,0 +1,344 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Definitions of the objects used during the service step.   *
+ *              These are the web server (ws) the worker and the connection*
+ *              JVM connection point                                       *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Dan Milstein <danmil@shore.net>                            *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+#ifndef JK_SERVICE_H
+#define JK_SERVICE_H
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_env.h"
+#include "jk_workerEnv.h"
+#include "jk_logger.h"
+#include "jk_pool.h"
+#include "jk_uriMap.h"
+#include "jk_worker.h"
+#include "jk_endpoint.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+    struct jk_ws_service;
+    struct jk_endpoint;
+    struct jk_worker;
+    struct jk_workerEnv;
+    struct jk_channel;
+    struct jk_pool;
+    struct jk_env;
+    typedef struct jk_ws_service jk_ws_service_t;
+
+/*
+ * Request methods, coded as numbers instead of strings.
+ * The list of methods was taken from Section 5.1.1 of RFC 2616,
+ * RFC 2518, the ACL IETF draft, and the DeltaV IESG Proposed Standard.
+ */
+#define SC_M_OPTIONS            (unsigned char)1
+#define SC_M_GET                (unsigned char)2
+#define SC_M_HEAD               (unsigned char)3
+#define SC_M_POST               (unsigned char)4
+#define SC_M_PUT                (unsigned char)5
+#define SC_M_DELETE             (unsigned char)6
+#define SC_M_TRACE              (unsigned char)7
+#define SC_M_PROPFIND           (unsigned char)8
+#define SC_M_PROPPATCH          (unsigned char)9
+#define SC_M_MKCOL              (unsigned char)10
+#define SC_M_COPY               (unsigned char)11
+#define SC_M_MOVE               (unsigned char)12
+#define SC_M_LOCK               (unsigned char)13
+#define SC_M_UNLOCK             (unsigned char)14
+#define SC_M_ACL                (unsigned char)15
+#define SC_M_REPORT             (unsigned char)16
+#define SC_M_VERSION_CONTROL    (unsigned char)17
+#define SC_M_CHECKIN            (unsigned char)18
+#define SC_M_CHECKOUT           (unsigned char)19
+#define SC_M_UNCHECKOUT         (unsigned char)20
+#define SC_M_SEARCH             (unsigned char)21
+#define SC_M_MKWORKSPACE        (unsigned char)22
+#define SC_M_UPDATE             (unsigned char)23
+#define SC_M_LABEL              (unsigned char)24
+#define SC_M_MERGE              (unsigned char)25
+#define SC_M_BASELINE_CONTROL   (unsigned char)26
+#define SC_M_MKACTIVITY         (unsigned char)27
+#define SC_M_JK_STORED          (unsigned char)0xFF
+
+
+/*
+ * RECOVERY STATUS
+ *
+ * The recovery buffer status is RECO_NONE unless we're using a LB service.
+ * In such case, the status is RECO_INITED. 
+ * The first real worker will detect this special status and will fill the 
+ * recovery buffer with initial POST data. In case of failure, the next worker
+ * will use this recovery buffer to feed tomcat with the saved initial POST data.
+ */
+
+#define RECO_NONE	0x00
+#define RECO_INITED	0x01
+#define RECO_FILLED	0x02
+
+/*
+ * The web server service 'class'.  An instance of this class is created
+ * for each request which is forwarded from the web server to the servlet
+ * container.  Contains the basic information about the request
+ * (e.g. protocol, req_uri, etc), and also contains a series of methods
+ * which provide access to core web server functionality (start_response,
+ * read, write).  This class might be more accurately called ws_request.
+ *
+ * As with all the core jk classes, this is essentially an abstract base
+ * class which is implemented/extended by classes which are specific to a
+ * particular web server.  By using an abstract base class in this manner,
+ * workers can be written for different protocols (e.g. ajp12, ajp13, ajp14)
+ * without the workers having to worry about which web server they are
+ * talking to.
+ *
+ * This particular OO-in-C system uses a 'ws_private' pointer to point to
+ * the platform-specific data.  So in the subclasses, the methods do most
+ * of their work by getting their hands on the ws_private pointer and then
+ * using that to get at the correctly formatted data and functions for
+ * their platform.
+ *
+ * Try imagining this as a 'public abstract class', and the ws_private
+ * pointer as a sort of extra 'this' reference.  Or imagine that you are
+ * seeing the internal vtables of your favorite OO language.  Whatever
+ * works for you.
+ *
+ * See apache1.3/mod_jk2.c and iis/jk_isapi_plugin.c for examples.  
+ */
+    struct jk_ws_service
+    {
+        struct jk_workerEnv *workerEnv;
+
+        /* JK_TRUE if a 'recoverable' error happened. That means a
+         * lb worker can retry on a different worker, without
+         * loosing any information. If JK_FALSE, an error will be reported
+         * to the client
+         */
+        int is_recoverable_error;
+
+        struct jk_worker *realWorker;
+
+        /* 
+         * A 'this' pointer which is used by the subclasses of this class to
+         * point to data which is specific to a given web server platform
+         * (e.g. Apache or IIS).  
+         */
+        void *ws_private;
+
+        int response_started;
+        int read_body_started;
+
+        /*
+         * Provides memory management.  All data specific to this request is
+         * allocated within this pool, which can then be reclaimed at the end
+         * of the request handling cycle. 
+         *
+         * Alive as long as the request is alive.
+         * You can use endpoint pool for communication - it is recycled.
+         */
+        struct jk_pool *pool;
+
+        /* Result of the mapping
+         */
+
+        struct jk_uriEnv *uriEnv;
+        /* 
+         * CGI Environment needed by servlets
+         */
+        char *method;
+        char *protocol;
+        char *req_uri;
+        char *remote_addr;
+        char *remote_host;
+        char *remote_user;
+        char *auth_type;
+        char *query_string;
+        char *server_name;
+        unsigned server_port;
+        char *server_software;
+        long content_length;    /* long that represents the content  */
+        /* length should be 0 if unknown.        */
+        unsigned is_chunked;    /* 1 if content length is unknown (chunked rq) */
+        unsigned no_more_chunks;        /* 1 if last chunk has been read */
+        long content_read;      /* number of bytes read */
+        int end_of_stream;      /* For IIS avoids blocking calls to lpEcb->ReadClient */
+
+        /*
+         * SSL information
+         *
+         * is_ssl       - True if request is in ssl connection
+         * ssl_cert     - If available, base64 ASN.1 encoded client certificates.
+         * ssl_cert_len - Length of ssl_cert, 0 if certificates are not available.
+         * ssl_cipher   - The ssl cipher suite in use.
+         * ssl_session  - The ssl session string
+         *
+         * In some servers it is impossible to extract all this information, in this 
+         * case, we are passing NULL.
+         */
+        int is_ssl;
+        char *ssl_cert;
+        unsigned ssl_cert_len;
+        char *ssl_cipher;
+        char *ssl_session;
+
+        /*
+         * SSL extra information for Servlet 2.3 API
+         * 
+         * ssl_key_size - ssl key size in use
+         */
+        int ssl_key_size;
+
+    /** Incoming headers */
+        struct jk_map *headers_in;
+
+        /*
+         * Request attributes. 
+         *
+         * These attributes that were extracted from the web server and are 
+         * sent to Tomcat.
+         *
+         * The developer should be able to read them from the ServletRequest
+         * attributes. Tomcat is required to append org.apache.tomcat. to 
+         * these attrinbute names.
+         */
+        struct jk_map *attributes;
+
+        /*
+         * The jvm route is in use when the adapter load balance among
+         * several JVMs. It is the ID of a specific JVM in the load balance
+         * group. We are using this variable to implement JVM session 
+         * affinity
+         */
+        char *jvm_route;
+
+        /* Response informations. As in apache, we don't use a separate
+           structure for response.
+         */
+        int status;
+        const char *msg;
+        struct jk_map *headers_out;
+
+        /* Count remaining bytes ( original content length minus what was sent */
+        int left_bytes_to_send;
+
+        /* Experimental - support for helper workers and buffering
+         */
+        int outPos;
+        int outSize;
+        char *outBuf;
+        apr_time_t startTime;
+
+        /*
+         * Area to get POST data for fail-over recovery in POST (used in LB mode)
+         */
+        struct jk_msg *reco_buf;
+        int reco_status;
+
+    /** printf style output. Formats in the tmp buf, then calls write
+     */
+        void (JK_METHOD * jkprintf) (struct jk_env * env,
+                                     struct jk_ws_service * s, char *frm,
+                                     ...);
+
+        /*
+         * Callbacks into the web server.  For each, the first argument is
+         * essentially a 'this' pointer.  All return JK_TRUE on success
+         * and JK_FALSE on failure.
+         */
+
+        /* Initialize the service structure
+         */
+        int (JK_METHOD * init) (struct jk_env * env, jk_ws_service_t *_this,
+                                struct jk_worker * w, void *serverObj);
+
+        /* Post request cleanup.
+         */
+        void (JK_METHOD * afterRequest) (struct jk_env * env,
+                                         jk_ws_service_t *_this);
+
+        /*
+         * Set the response head in the server structures. This will be called
+         * before the first write.
+         */
+        int (JK_METHOD * head) (struct jk_env * env, jk_ws_service_t *s);
+
+        /*
+         * Read a chunk of the request body into a buffer.  Attempt to read len
+         * bytes into the buffer.  Write the number of bytes actually read into
+         * actually_read.  
+         */
+        int (JK_METHOD * read) (struct jk_env * env, jk_ws_service_t *s,
+                                void *buffer,
+                                unsigned len, unsigned *actually_read);
+
+        /*
+         * Write a chunk of response data back to the browser.
+         */
+        int (JK_METHOD * write) (struct jk_env * env, jk_ws_service_t *s,
+                                 const void *buffer, unsigned len);
+
+        /*
+         * Flush the output buffers.
+         */
+        int (JK_METHOD * flush) (struct jk_env * env, jk_ws_service_t *s);
+
+    /** Get the method id. SC_M_* fields are the known types.
+     */
+        /* int (JK_METHOD *getMethodId)(struct jk_env *env, jk_ws_service_t *s); */
+
+    /** Get a cookie value by name. 
+     */
+        /*     char *(JK_METHOD *getCookie)(struct jk_env *env, jk_ws_service_t *s, */
+        /*                                  const char *name ); */
+
+    /** Get a path param ( ;foo=bar )
+     */
+        /*     char *(JK_METHOD *getPathParam)(struct jk_env *env, jk_ws_service_t *s, */
+        /*                                      const char *name ); */
+
+    /** Extract the session id. It should use the servlet spec mechanism
+     *  by default and as first choice, but if a separate module is doing
+     *  user tracking we can reuse that.
+     */
+        /*     char *(JK_METHOD *getSessionId)(struct jk_env *env, jk_ws_service_t *s); */
+
+    /** Extract the 'route' id, for sticky lb.
+     */
+        /*     char *(JK_METHOD *getRoute)(struct jk_env *env, struct jk_ws_service *s); */
+
+    /** Serialize the request in a buffer.
+     */
+        /* int (JK_METHOD *serialize)(struct jk_env *env, struct jk_ws_service *s,
+           int protocol, struct jk_msg *msg ); */
+
+
+    };
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif                          /* JK_SERVICE_H */
diff --git a/connectors/jk/native2/include/jk_shm.h b/connectors/jk/native2/include/jk_shm.h
new file mode 100644
index 0000000..5eeea5a
--- /dev/null
+++ b/connectors/jk/native2/include/jk_shm.h
@@ -0,0 +1,166 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef JK_SHM_H
+#define JK_SHM_H
+
+#include "jk_global.h"
+#include "jk_env.h"
+#include "jk_logger.h"
+#include "jk_pool.h"
+#include "jk_msg.h"
+#include "jk_service.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+    struct jk_env;
+    struct jk_shm;
+
+    typedef struct jk_shm jk_shm_t;
+    typedef struct jk_shm_slot jk_shm_slot_t;
+    typedef struct jk_shm_head jk_shm_head_t;
+
+
+/* That's the default - should be enough for most cases.
+   8k is what we use as the default packet size in ajp13, and 256
+   slots should be enough for now ( == 256 workers )
+   XXX implement the setters to override this
+*/
+#define DEFAULT_SLOT_SIZE 1024 * 8
+#define DEFAULT_SLOT_COUNT 256
+
+
+
+/** Each shared memory slot has at least the following components.
+ */
+    struct jk_shm_slot
+    {
+    /** Version of the segment. Whoever writes it must change the
+        version after writing. Various components will check the version
+        and refresh if needed
+    */
+        int ver;
+
+    /** Size of the segment */
+        int size;
+
+        int structSize;
+        int structCnt;
+
+    /** Full name of the segment. type:localName convention.
+     */
+        char name[64];
+
+        char data[1];
+    };
+
+
+    struct jk_shm_head
+    {
+
+        int structSize;
+
+        int slotSize;
+
+        int slotMaxCount;
+
+        /* Last used position. Increase ( at least atomically, eventually with mutexes )
+           each time a slot is created */
+        int lastSlot;
+
+        /* XXX Need a more generic mechanism */
+        int lbVer;
+
+        /* Array of used slots set to nonzero if used */
+        char slots[1];
+
+    };
+
+
+/**
+ *  Shared memory support. This is similar with the scoreboard or jserv's worker shm, but
+ *  organized a bit more generic to support use of shm as a channel and to support config
+ *  changes.
+ * 
+ *  The structure is organized as an array of 'slots'. Each slot has a name and data. Slots are long lived -
+ *  they are never destroyed.
+ *  Each slot has it's own rules for content and synchronization - but typically they are 'owned'
+ *  by a process or thread and use various conventions to avoid the need for sync.
+  *
+ * @author Costin Manolache
+ */
+    struct jk_shm
+    {
+        struct jk_bean *mbean;
+
+        struct jk_pool *pool;
+
+        char *fname;
+
+    /** Initialize the shared memory area. It'll map the shared memory 
+     *  segment if it exists, or create and init it if not.
+     */
+        int (JK_METHOD * init) (struct jk_env * env, struct jk_shm * shm);
+
+    /** Reset the shm area
+     */
+        int (JK_METHOD * reset) (struct jk_env * env, struct jk_shm * shm);
+
+
+    /** Detach from the shared memory segment
+     */
+        int (JK_METHOD * destroy) (struct jk_env * env, struct jk_shm * shm);
+
+    /** Get a shm slot. Each slot has different rules for synchronization, based on type. 
+     */
+        struct jk_shm_slot *(JK_METHOD * getSlot) (struct jk_env * env,
+                                                   struct jk_shm * shm,
+                                                   int pos);
+
+    /** Create a slot. This typically involves inter-process synchronization.
+     */
+        struct jk_shm_slot *(JK_METHOD * createSlot) (struct jk_env * env,
+                                                      struct jk_shm * shm,
+                                                      char *name, int size);
+
+        int size;
+
+        int slotSize;
+
+        int slotMaxCount;
+
+        struct jk_shm_head *head;
+
+        /* Memory image (the data after head) */
+        void *image;
+
+        /* Is the shmem attached or created */
+        int attached;
+
+        /* Use the main memory instead */
+        int inmem;
+        /* Private data (apr_shm_t) */
+        void *privateData;
+    };
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif
diff --git a/connectors/jk/native2/include/jk_uriEnv.h b/connectors/jk/native2/include/jk_uriEnv.h
new file mode 100644
index 0000000..f8d93eb
--- /dev/null
+++ b/connectors/jk/native2/include/jk_uriEnv.h
@@ -0,0 +1,204 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * URI enviroment. All properties associated with a particular URI will be
+ * stored here. This coresponds to a per_dir structure in Apache. 
+ *
+ * Replaces uri_worker_map, etc.
+ *
+ * Author:      Costin Manolache
+ */
+
+#ifndef JK_URIENV_H
+#define JK_URIENV_H
+
+#include "jk_logger.h"
+#include "jk_endpoint.h"
+#include "jk_worker.h"
+#include "jk_map.h"
+#include "jk_uriMap.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+    struct jk_worker;
+    struct jk_endpoint;
+    struct jk_env;
+    struct jk_uri_worker_map;
+    struct jk_map;
+    struct jk_webapp;
+
+    struct jk_uriEnv;
+    typedef struct jk_uriEnv jk_uriEnv_t;
+
+/* Standard exact mapping */
+#define MATCH_TYPE_EXACT    (0)
+
+/* Standard prefix mapping */
+#define MATCH_TYPE_PREFIX  (1)
+
+/* Standard suffix mapping ( *.jsp ) */
+#define MATCH_TYPE_SUFFIX   (2)
+
+/* Special: match all URIs of the form *ext */
+#define MATCH_TYPE_GENERAL_SUFFIX (3)
+
+/* Special: match all context path URIs with a path component suffix */
+#define MATCH_TYPE_CONTEXT_PATH (4)
+
+/* The uriEnv corresponds to a virtual host */
+#define MATCH_TYPE_HOST  (5)
+
+/* Top level context mapping. WEB-INF and META-INF will be checked,
+   and the information will be passed to tomcat
+*/
+#define MATCH_TYPE_CONTEXT  (6)
+
+/* Regular Expression match */
+#define MATCH_TYPE_REGEXP  (7)
+
+    struct jk_uriEnv
+    {
+        struct jk_bean *mbean;
+
+        struct jk_pool *pool;
+
+        struct jk_workerEnv *workerEnv;
+
+        struct jk_uriMap *uriMap;
+
+        /* Generic name/value properties. 
+         */
+        struct jk_map *properties;
+
+        /* -------------------- Properties extracted from the URI name ---------- */
+    /** Full name */
+        char *name;
+
+        /* Virtual server handled - '*' means 'global' ( visible in all
+         * virtual servers ). Part of the uri name.
+         */
+        char *virtual;
+
+        /* Virtual server port - '0' means 'all' ( visible in all
+         * ports on the virtual servers ). Part of the uri name.
+         */
+        int port;
+
+        /* Original uri ( unparsed ). Part of the uri name.
+         */
+        char *uri;
+
+        /* -------------------- Properties set using setAttribute ---------- */
+    /** ContextPath. Set with 'context' attribute.
+     */
+        char *contextPath;
+        int ctxt_len;
+
+    /** ServletName. Set with 'servlet' attribute.
+     */
+        char *servlet;
+        int servletId;
+
+    /** Group, set with 'group' attribute. Defaults to 'lb'.
+     */
+        char *workerName;
+        struct jk_worker *worker;
+
+    /** For MATCH_TYPE_HOST, the list of aliases for the virtual host.
+     *  Set using (multi-value ) 'alias' attribute on vhost uris.
+    */
+        struct jk_map *aliases;
+
+        /* If set we'll use apr_time to get the request time in microseconds and update
+           the scoreboard to reflect that. 
+         */
+        int timing;
+
+        /* -------------------- Properties extracted from the uri, at init() -------------------- */
+        /* Extracted suffix, for extension-based mathces */
+        char *suffix;
+        int suffix_len;
+
+        /* Prefix based mapping. Same a contextPath for MATCH_TYPE_CONTEXT
+         */
+        char *prefix;
+        int prefix_len;
+
+        int match_type;
+
+        /* Regular Expression structure
+         */
+        void *regexp;
+    /** For MATCH_TYPE_HOST, the list of webapps in that host
+     */
+        struct jk_map *webapps;
+
+    /** For MATCH_TYPE_CONTEXT, the list of local mappings
+     */
+        struct jk_map *exactMatch;
+        struct jk_map *prefixMatch;
+        struct jk_map *suffixMatch;
+        struct jk_map *regexpMatch;
+
+    /** For MATCH_TYPE_CONTEXT, the config used to read properties
+        for that context.
+        For MATCH_TYPE_HOST, the config used to read contexts 
+        For MATCH_TYPE_HOST/default it also contains all vhosts
+
+        If NULL - no config was attached.
+        ( this will be used in future for run-time deployment )
+     */
+
+        struct jk_config *config;
+
+        /* -------------------- Other properties -------------------- */
+
+
+    /** Different apps can have different loggers.
+     */
+        struct jk_logger *l;
+
+        /* Environment variables support
+         */
+        int envvars_in_use;
+        struct jk_map *envvars;
+
+        int merged;
+
+        int inherit_globals;
+
+    /** XXX .
+     */
+/*     int status; */
+/*     int virtualPort; */
+
+        /* -------------------- Methods -------------------- */
+
+        int (*init) (struct jk_env * env, struct jk_uriEnv * _this);
+
+    };
+
+
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif                          /* JK_URIENV_H */
diff --git a/connectors/jk/native2/include/jk_uriMap.h b/connectors/jk/native2/include/jk_uriMap.h
new file mode 100644
index 0000000..193ad3f
--- /dev/null
+++ b/connectors/jk/native2/include/jk_uriMap.h
@@ -0,0 +1,125 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Manages the request mappings. It includes the internal mapper and all
+ * properties associated with a location ( or virtual host ). The information
+ * is set using:
+ *   - various autoconfiguration mechanisms.
+ *   - uriworkers.properties
+ *   - JkMount directives
+ *   - <SetHandler> and apache specific directives.
+ *   - XXX workers.properties-like directives ( for a single config file )
+ *   - other server-specific directives
+ *
+ * The intention is to allow the user to use whatever is more comfortable
+ * and fits his needs. For 'basic' configuration the autoconf will be enough,
+ * server-specific configs are the best for fine-tunning, properties are
+ * easy to generate and edit.
+ *
+ *
+ * Author: Gal Shachor <shachor@il.ibm.com>
+ * author: Costin Manolache
+ */
+#ifndef JK_URIMAP_H
+#define JK_URIMAP_H
+
+#include "jk_global.h"
+#include "jk_env.h"
+#include "jk_logger.h"
+#include "jk_uriEnv.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+    struct jk_uriMap;
+    struct jk_map;
+    struct jk_env;
+    struct jk_pool;
+
+    typedef struct jk_uriMap jk_uriMap_t;
+
+    struct jk_uriMap
+    {
+        struct jk_bean *mbean;
+
+        /* All mappings */
+        struct jk_map *maps;
+
+        struct jk_workerEnv *workerEnv;
+
+        /* Virtual host map. For each host and alias there is one
+         * entry, the value is a uriEnv that corresponds to the vhost top
+         * level.
+         */
+        struct jk_map *vhosts;
+
+        /* Virtual host map cache. Once processed the mapped host
+         * will be cached for performance reasons.
+         */
+        struct jk_map *vhcache;
+
+        /* ---------- Methods ---------- */
+
+    /** Initialize the map. This should be called after all workers
+        were added. It'll check if mappings have valid workers.
+    */
+        int (*init) (struct jk_env * env, jk_uriMap_t *_this);
+
+        void (*destroy) (struct jk_env * env, jk_uriMap_t *_this);
+
+        int (*addUriEnv) (struct jk_env * env,
+                          struct jk_uriMap * uriMap,
+                          struct jk_uriEnv * uriEnv);
+
+    /** Check the uri for potential security problems
+     */
+        int (*checkUri) (struct jk_env * env, jk_uriMap_t *_this,
+                         const char *uri);
+
+    /** Mapping the uri. To be thread safe, we need to pass a pool.
+        Or even better, create the jk_service structure already.
+        mapUri() can set informations on it as well.
+        
+        MapUri() method should behave exactly like the native apache2
+        mapper - we need it since the mapping rules for servlets are
+        different ( or we don't know yet how to 'tweak' apache config
+        to do what we need ). Even when we'll know, uriMap will be needed
+        for other servers. 
+    */
+
+        struct jk_uriEnv *(*mapUri) (struct jk_env * env, jk_uriMap_t *_this,
+                                     const char *vhost,
+                                     int port, const char *uri);
+
+        /* -------------------- @deprecated -------------------- */
+        /* used by the mapper, temp storage ( ??? ) */
+
+        /* pool for mappings. Mappings will change at runtime, we can't
+         * recycle the main pool.
+         */
+        struct jk_pool *pool;
+    };
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif                          /* JK_URI_MAP_H */
diff --git a/connectors/jk/native2/include/jk_vm.h b/connectors/jk/native2/include/jk_vm.h
new file mode 100644
index 0000000..b8b1f62
--- /dev/null
+++ b/connectors/jk/native2/include/jk_vm.h
@@ -0,0 +1,78 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Tools to encapsulate the VM
+ *
+ * @author:  Gal Shachor <shachor@il.ibm.com>
+ * @author: Costin Manolache
+ */
+#ifndef JK_VM_H
+#define JK_VM_H
+
+#include "jk_pool.h"
+#include "jk_env.h"
+#include "jk_logger.h"
+#include "jk_service.h"
+#include "jk_map.h"
+
+#define JK2_MAXOPTIONS  64
+struct jk_vm
+{
+    struct jk_bean *mbean;
+
+    /* General name/value properties
+     */
+    struct jk_map *properties;
+
+    struct jk_pool *pool;
+    /** Should be JavaVM *, but we want this to be compilable
+        without jni.h ( it'll be used to start out-of-process as
+        well, same options and config
+    */
+    void *jvm;
+
+    /* Full path to the jni javai/jvm dll
+     */
+    char *jvm_dll_path;
+
+    /*
+     * All initialization options
+     */
+    char *options[JK2_MAXOPTIONS];
+
+    /*
+     * -Djava.class.path options
+     */
+    char *classpath[JK2_MAXOPTIONS];
+
+    int nOptions;
+
+    int nClasspath;
+    /** Create the VM, attach - don't execute anything
+     */
+    int (*init) (struct jk_env * env, struct jk_vm * p);
+
+    void *(*attach) (struct jk_env * env, struct jk_vm * p);
+
+    void (*detach) (struct jk_env * env, struct jk_vm * p);
+
+    void (*destroy) (struct jk_env * env, struct jk_vm * p);
+};
+
+typedef struct jk_vm jk_vm_t;
+
+#endif
diff --git a/connectors/jk/native2/include/jk_worker.h b/connectors/jk/native2/include/jk_worker.h
new file mode 100644
index 0000000..639bd96
--- /dev/null
+++ b/connectors/jk/native2/include/jk_worker.h
@@ -0,0 +1,268 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Workers controller header file                             *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           * 
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef JK_WORKER_H
+#define JK_WORKER_H
+
+#include "jk_env.h"
+#include "jk_pool.h"
+#include "jk_logger.h"
+#include "jk_service.h"
+#include "jk_endpoint.h"
+#include "jk_map.h"
+#include "jk_uriMap.h"
+#include "jk_objCache.h"
+#include "jk_msg.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+    struct jk_worker;
+    struct jk_endpoint;
+    struct jk_env;
+    struct jk_objCache;
+    struct jk_msg;
+    struct jk_map;
+    typedef struct jk_worker jk_worker_t;
+
+/* Number of lb levels/priorities. Workers are grouped by the level,
+   lower levels will allways be prefered. If all workers in a level are
+   in error state, we move to the next leve.
+*/
+#define JK_LB_LEVELS 4
+#define JK_LB_MAX_WORKERS 256
+
+/* XXX Separate this in 2 structures: jk_lb.h and jk_ajp.h.
+   Using 'worker' as a generic term is confusing, the objects are very different.
+ */
+
+/*
+ * The worker 'class', which represents something to which the web server
+ * can delegate requests. 
+ *
+ * This can mean communicating with a particular servlet engine instance,
+ * using a particular protocol.  A single web server instance may have
+ * multiple workers communicating with a single servlet engine (it could be
+ * using ajp12 for some requests and ajp13/ajp14 for others).  Or, a single web
+ * server instance could have multiple workers communicating with different
+ * servlet engines using the same protocol (it could be load balancing
+ * among many engines, using ajp13/ajp14 for all communication).
+ *
+ * There is also a load balancing worker (jk_lb_worker.c), which itself
+ * manages a group of workers.
+ *
+ * As with all the core jk classes, this is essentially an abstract base
+ * class which is implemented/extended by classes which are specific to a
+ * particular protocol (or request-handling system).  By using an abstract
+ * base class in this manner, plugins can be written for different servers
+ * (e.g. IIS, Apache) without the plugins having to worry about which
+ * protocol they are talking.
+ *
+ * This particular OO-in-C system uses a 'worker_private' pointer to
+ * point to the protocol-specific data/functions.  So in the subclasses, the
+ * methods do most of their work by getting their hands on the
+ * worker_private pointer and then using that to get at the functions for
+ * their protocol.
+ *
+ * Try imagining this as a 'public abstract class', and the
+ * worker_private pointer as a sort of extra 'this' reference.  Or
+ * imagine that you are seeing the internal vtables of your favorite OO
+ * language.  Whatever works for you.
+ *
+ * See jk_ajp14_worker.c, jk_worker_status for examples.  
+ *
+ */
+    struct jk_worker
+    {
+        struct jk_bean *mbean;
+
+        struct jk_workerEnv *workerEnv;
+
+        /* 
+         * A 'this' pointer which is used by the subclasses of this class to
+         * point to data/functions which are specific to a given protocol 
+         * (e.g. ajp12 or ajp13 or ajp14).  
+         */
+        void *worker_private;
+
+    /** Communication channel used by the worker 
+     */
+        struct jk_channel *channel;
+        char *channelName;
+
+    /** Reuse the endpoint and it's connection. The cache will contain
+        the 'unused' workers. It's size may be used to select
+        an worker by the lb.
+     */
+        struct jk_objCache *endpointCache;
+
+        /* All endpoints for this worker. Each endpoint is long-lived,
+           the size of the map will represent the maximum number of connections
+           active so far.
+         */
+        struct jk_map *endpointMap;
+
+        /* Maximum number of endpoints per worker.
+           The default value is 0, meaning unlimited.
+         */
+        int maxEndpoints;
+
+        /* Indicates that worker has reached maximum number of 'used'
+           connections.  
+         */
+        int in_max_epcount;
+
+    /** Request pool cache. XXX We may use a pool of requests.
+     */
+        struct jk_objCache *rPoolCache;
+
+        /* Private key used to connect to the remote side2. */
+        char *secret;
+
+        struct jk_mutex *cs;
+
+        /* ------------ Information used for load balancing ajp workers ------------ */
+
+    /** The id of the tomcat instance we connect to. We may have multiple
+        workers connecting to a single tomcat. If no route is defined,
+        the worker name will be the route name. The route can be the
+        name of another worker. 
+     */
+        char *route;
+        char *routeRedirect;
+
+    /** lb groups in which this worker belongs */
+        struct jk_map *groups;
+
+        /* Each worker can be part of a load-balancer scheme.
+         * The information can be accessed by other components -
+         * for example to report status, etc.
+         */
+        int lb_factor;
+        int lb_value;
+        /* If set then the worker doesn't participate in the
+         * load-balancer scheme. This is used for non-Tomcat workers.
+         */
+        int lb_disabled;
+
+        /* Time when the last error occured on this worker */
+        time_t error_time;
+
+        /* In error state. Will return to normal state after a timeout
+         *  ( number of requests or time ), if no other worker is active
+         *  or when the configuration changes.
+         */
+        int in_error_state;
+
+    /** Explicit field for gracefull state. In this mode only requests with 
+        session IDs matching the worker will be forwarded, nothing else */
+        int graceful;
+
+    /** Delay in ms at connect time for Tomcat to respond to a PING request 
+     *  at connect time (ensure that Tomcat is not HOLDED)
+     */
+        int connect_timeout;
+
+    /** When set, indicate delay in ms to wait a reply.
+     *  Warning it will mark as invalid long running request, should be set with
+     *  care but could be usefull to detect an HOLDED Tomcat.
+     */
+        int reply_timeout;
+
+    /** Delay in ms for Tomcat to respond to a PING request before 
+     *  webserver start sending the request (ensure that Tomcat is not HOLDED)
+     */
+        int prepost_timeout;
+
+        /* Worker priority.
+         * Workers with lower priority are allways preffered - regardless of lb_value
+         * This is user to represent 'local' workers ( we can call it 'proximity' or 'distance' )
+         */
+        int level;
+
+        /* ----------------- Information specific to the lb worker ----------------- */
+
+    /** Load balanced workers. Maps name->worker, used at config time.
+     *  When the worker is initialized or refreshed it'll set the runtime
+     *  tables.
+     */
+        struct jk_map *lbWorkerMap;
+
+    /** Support for some hardware load-balancing schemes.
+        If this is set, jk2 will only forward new requests to local
+        workers ( level=0 ). All other workers will get requests
+        with a session id.
+
+        The value of this field will be used as a status code if
+        all local workers are down. This way the front load-balancers
+        will know this server shouldn't be used for new requests.
+    */
+        int hwBalanceErr;
+
+        /* Message to display when no tomcat instance is available
+         * if status=302, this is a redirect location.
+         */
+        char *noWorkerMsg;
+        int noWorkerCode;
+
+        /* jk2 shouldn't set headers if noErrorHeader (true by default) 
+         * It will allow Apache 2.0 to handle correctly ErrorDocument
+         */
+        int noErrorHeader;
+
+        int workerCnt[JK_LB_LEVELS];
+        jk_worker_t *workerTables[JK_LB_LEVELS][JK_LB_MAX_WORKERS];
+
+        /* ---------------- Information specific to the status worker --------------- */
+
+        /* Path to the Status Page Style Sheet.
+         */
+        char *stylePath;
+
+        /* Access Mode to the Status Page Style Sheet.
+         *  Mode 0 - Style Sheet Off - default.
+         *  Mode 1 - Int Style Sheet - default values.
+         *  Mode 2 - Ext Style Sheet - ext file, documentRoot relative.
+         *  Mode 3 - Int Style Sheet - ext file, f/system or serverRoot relative.
+         */
+        int   styleMode;
+
+        /* -------------------- Methods supported by all workers -------------------- */
+
+        /*
+         * Forward a request to the servlet engine.  The request is described
+         * by the jk_ws_service_t object.  I'm not sure exactly how
+         * is_recoverable_error is being used.  
+         */
+        int (JK_METHOD * service) (struct jk_env * env,
+                                   struct jk_worker * _this,
+                                   struct jk_ws_service * s);
+
+    };
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif                          /* JK_WORKER_H */
diff --git a/connectors/jk/native2/include/jk_workerEnv.h b/connectors/jk/native2/include/jk_workerEnv.h
new file mode 100644
index 0000000..62bd11b
--- /dev/null
+++ b/connectors/jk/native2/include/jk_workerEnv.h
@@ -0,0 +1,306 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Workers controller header file                             *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           * 
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef JK_WORKERENV_H
+#define JK_WORKERENV_H
+
+#include "jk_logger.h"
+#include "jk_endpoint.h"
+#include "jk_config.h"
+#include "jk_worker.h"
+#include "jk_map.h"
+#include "jk_uriMap.h"
+#include "jk_uriEnv.h"
+#include "jk_handler.h"
+#include "jk_service.h"
+#include "jk_shm.h"
+#include "jk_vm.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+    struct jk_worker;
+    struct jk_channel;
+    struct jk_endpoint;
+    struct jk_env;
+    struct jk_config;
+    struct jk_uriMap;
+    struct jk_uriEnv;
+    struct jk_map;
+    struct jk_logger;
+    struct jk_handler;
+    struct jk_ws_service;
+
+/* Temporary hardcoded handler IDs. Will be replaced with a name based dynamic mechanism */
+
+/* Write a body chunk from the servlet container to the web server */
+#define JK_HANDLE_AJP13_SEND_BODY_CHUNK    3
+
+/* Send response headers from the servlet container to the web server. */
+#define JK_HANDLE_AJP13_SEND_HEADERS       4
+
+/* Marks the end of response. */
+#define JK_HANDLE_AJP13_GET_BODY_CHUNK     6
+
+/*  Marks the end of response. */
+#define JK_HANDLE_AJP13_END_RESPONSE       5
+
+/* Get a PONG reply from the servlet container. */
+#define JK_HANDLE_AJP13_PONG_REPLY       9
+
+/* Second Login Phase (servlet engine -> web server), md5 seed is received */
+#define JK_HANDLE_LOGON_SEED	0x11
+
+/* Login Accepted (servlet engine -> web server) */
+#define JK_HANDLE_LOGON_OK	0x13
+
+/* Login Rejected (servlet engine -> web server) */
+#define JK_HANDLE_LOGON_ERR	0x14
+
+/* Dispatcher for jni channel ( java->C ) */
+#define JK_HANDLE_JNI_DISPATCH 0x15
+
+/* Dispatcher for shm object ( java->C) */
+#define JK_HANDLE_SHM_DISPATCH 0x16
+
+/* Dispatcher for channel components ( java->C )*/
+#define JK_HANDLE_CH_DISPATCH 0x17
+
+/* Dispatcher for mutex object  ( java->C ) */
+#define JK_HANDLE_MUTEX_DISPATCH 0x18
+
+
+/*
+ * Jk configuration and global methods. 
+ * 
+ * Env Information to be provided to worker at init time
+ * With AJP14 support we need to have access to many informations
+ * about web-server, uri to worker map....
+ */
+    struct jk_workerEnv
+    {
+        struct jk_bean *mbean;
+
+        /* Use this pool for all global settings
+         */
+        struct jk_pool *pool;
+
+        /* Active workers hashtable. 
+         */
+        struct jk_map *worker_map;
+
+        /* Channels
+         */
+        struct jk_map *channel_map;
+
+        struct jk_map *endpointMap;
+
+        /* In a multi-process server, like Apache, stores the child
+           id in the scoreboard ( if the scoreboard is used ).
+           This in turn is used in the jk scoreboard to store informations
+           about each instance.
+           If -1 - shm is disabled.
+         */
+        int childId;
+        int childProcessId;
+        /* maximum configured number of child processes */
+        int maxDaemons;
+
+        struct jk_env *globalEnv;
+
+    /** Worker that will be used by default, if no other
+        worker is specified. Usefull for SetHandler or
+        to avoid the lookup
+        XXX no need - lb is the default, easy to get it.
+    */
+        struct jk_worker *defaultWorker;
+
+        /* Web-Server we're running on (Apache/IIS/NES).
+         */
+        char *server_name;
+
+        /* XXX Virtual server handled - "*" is all virtual
+         */
+        char *virtual;
+
+    /** Initialization properties, set via native options or workers.properties.
+     */
+        /* XXX renamed from init_data to force all code to use setProperty
+           This is private property !
+         */
+        struct jk_map *initData;
+
+        /*
+         * Log options. XXX move it to uriEnv, make it configurable per webapp.
+         * XXX What about apache native logger ?
+         */
+        char *logger_name;
+
+        struct jk_uriMap *uriMap;
+
+        int was_initialized;
+
+        /*
+         * SSL Support
+         */
+        int ssl_enable;
+        char *https_indicator;
+        char *certs_indicator;
+        char *cipher_indicator;
+        char *session_indicator;        /* Servlet API 2.3 requirement */
+        char *key_size_indicator;       /* Servlet API 2.3 requirement */
+
+        /*
+         * Jk Options
+         */
+        int options;
+
+    /** Old REUSE_WORKER define. Instead of using an object pool, use
+        thread data to recycle the connection. */
+        int perThreadWorker;
+
+        /*
+         * Environment variables support
+         */
+        int envvars_in_use;
+        struct jk_map *envvars;
+
+        struct jk_config *config;
+
+        struct jk_shm *shm;
+
+        /* Slot for endpoint stats
+         */
+        struct jk_shm_slot *epStat;
+
+        /* Handlers. This is a dispatch table for messages, for
+         * each message id we have an entry containing the jk_handler_t.
+         * lastMessageId is the size of the table.
+         */
+        struct jk_handler **handlerTable;
+        int lastMessageId;
+
+        /* The vm - we support a single instance per process
+         * ( i.e can't have both jdk1.1 and jdk1.2 at the same time,
+         *  or 2 instances of the same vm. )
+         */
+        struct jk_vm *vm;
+
+    /** Private data, associated with the 'real' server
+     *  server_rec * in apache
+     */
+        void *_private;
+
+        struct jk_mutex *cs;
+
+        /* Global setting to enable counters on all requests.
+         *  That adds about 2-3 ms per request ( at least on linux ),
+         *  and will store average and max processing time per endpoint
+         *  ( that can be agregated per worker or per server ).
+         * Note that we can't collect per request times - it's not
+         * thread safe and sync is expensive. As workaround you
+         * can enable timers only on a specific request and/or
+         * use a dedicated worker/channel for that request.
+         */
+        int timing;
+
+        /* -------------------- Methods -------------------- */
+
+        /* Register a callback handler, for methods from java to C
+         */
+        int (*registerHandler) (struct jk_env * env,
+                                struct jk_workerEnv * _this,
+                                const char *type, const char *name, int id,
+                                jk_handler_callback callback,
+                                char *signature);
+
+
+        int (*addWorker) (struct jk_env * env,
+                          struct jk_workerEnv * _this, struct jk_worker * w);
+
+        int (*addChannel) (struct jk_env * env,
+                           struct jk_workerEnv * _this,
+                           struct jk_channel * w);
+
+    /** Add an endpoint. Endpoints are long lived and used to store
+        statistics. The endpoint can be in used in only one thread
+        at a time, it's a good way to avoid synchronization.
+     */
+        int (*addEndpoint) (struct jk_env * env, struct jk_workerEnv * wEnv,
+                            struct jk_endpoint * ep);
+
+        int (*initChannel) (struct jk_env * env,
+                            struct jk_workerEnv * wEnv,
+                            struct jk_channel * ch);
+
+    /** Call the handler associated with the message type.
+     */
+        int (*dispatch) (struct jk_env * env, struct jk_workerEnv * _this,
+                         void *target, struct jk_endpoint * ep, int code,
+                         struct jk_msg * msg);
+
+    /** Utility method for stream-based workers. It'll read
+     *  messages, dispatch, send the response if any until
+     *  done. This assumes one native server thread talking
+     *  with a different client thread ( on the java side ).
+     *  It does not work for jni or doors or other transports
+     *  where a single thread is used for the whole processing.
+     */
+        int (*processCallbacks) (struct jk_env * env,
+                                 struct jk_workerEnv * _this,
+                                 struct jk_endpoint * e,
+                                 struct jk_ws_service * r);
+
+    /**
+     * Special init function to be called from the parent process
+     * ( with root privs ? ).
+     * Used to create the scoreboard ( workaround required at least for HPUX )
+     *
+     * init() will be called for each child, parentInit() only once.
+     */
+        int (*parentInit) (struct jk_env * env, struct jk_workerEnv * _this);
+
+    /**
+     *  Init the workers, prepare the worker environment. Will read
+     *  all properties and set the jk acordignly.
+     * 
+     *  Replaces wc_open
+     */
+        int (*init) (struct jk_env * env, struct jk_workerEnv * _this);
+
+    /** Close all workers, clean up
+     *
+     */
+        void (*close) (struct jk_env * env, struct jk_workerEnv * _this);
+    };
+
+
+    typedef struct jk_workerEnv jk_workerEnv_t;
+
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif                          /* JK_WORKERENV_H */
diff --git a/connectors/jk/native2/jni/Makefile.in b/connectors/jk/native2/jni/Makefile.in
new file mode 100644
index 0000000..05b9695
--- /dev/null
+++ b/connectors/jk/native2/jni/Makefile.in
@@ -0,0 +1,29 @@
+OEXT=.lo
+include @APACHE_CONFIG_VARS@
+
+JK=../common/
+COMMON_OBJECTS=${JK}jk_map${OEXT} ${JK}jk_util${OEXT} ${JK}jk_pool${OEXT}
+JNI_OBJECTS=jk_jnicb${OEXT} ${COMMON_OBJECTS}
+
+
+JAVA_INCL=-I @JAVA_HOME@/include -I @JAVA_HOME@/include/@OS@ -I../common
+CFLAGS=@apache_include@ @CFLAGS@ ${JAVA_INCL} -D_REENTRANT -Wall
+
+include ../scripts/build/rules.mk
+
+all: Makefile jk_jni_aprImpl.c
+
+Makefile: Makefile.in
+	echo Regenerating Makefile
+	( cd ..; ./config.status )
+
+jk_jnicb.la: ${JNI_OBJECTS}
+	$(LIBTOOL) --mode=link $(COMPILE) -module -o $@ -rpath `pwd` -g -O2 -avoid-version ${JNI_OBJECTS} 
+
+jni_connect.so: jk_jnicb.la
+	$(LIBTOOL) --mode=install cp $< `pwd`/jni_connect.so
+
+install:
+
+clean:
+	rm -f *.o *.slo *.lo *.so *.la .libs/*
diff --git a/connectors/jk/native2/jni/jk_jni_aprImpl.c b/connectors/jk/native2/jni/jk_jni_aprImpl.c
new file mode 100644
index 0000000..a37da1c
--- /dev/null
+++ b/connectors/jk/native2/jni/jk_jni_aprImpl.c
@@ -0,0 +1,545 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Implementation for org.apache.jk.apr.AprImpl
+ *
+ * @author Costin Manolache
+ */
+
+#ifdef HAVE_JNI
+
+#include <jni.h>
+#include "apr.h"
+#include "apr_pools.h"
+
+#include "apr_network_io.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_strings.h"
+#include "apr_portable.h"
+#include "apr_lib.h"
+
+#include "org_apache_jk_apr_AprImpl.h"
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_logger.h"
+
+#if APR_HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#define P2J(jk_pointer) ((jlong)(long)(void *)jk_pointer)
+#define J2P(p, jktype) ((jktype)(void *)(long)p)
+
+/** Access to the jk workerEnv. This field and jk_env_globalEnv are used
+    to interact with the jk components 
+ */
+static jk_workerEnv_t *workerEnv;
+
+static int jniDebug=0;
+
+int jk_jni_status_code=0;
+
+#define JK_GET_REGION 1
+#define JK_GET_BYTE_ARRAY_ELEMENTS 2
+#define JK_DIRECT_BUFFER_NIO 3
+#define JNI_TOMCAT_STARTING 1
+#define JNI_TOMCAT_STARTED 2
+
+static int arrayAccessMethod=JK_GET_REGION;
+void JK_METHOD jk2_env_setAprPool( jk_env_t *env, void *aprPool );
+
+JNIEXPORT void JNICALL 
+Java_org_apache_jk_apr_AprImpl_setArrayAccessMode(JNIEnv *jniEnv, jobject _jthis, jint mode)
+{
+    arrayAccessMethod=mode;
+}
+
+/* -------------------- Apr initialization and pools -------------------- */
+
+/** Initialize APR and jk, for standalone use. If we use in-process mode,
+    i.e. an application using jk to launch an in-process JVM - this function
+    will not do anything, since the setup is already done.
+
+    The code is identical with what mod_jk is doing.
+*/
+JNIEXPORT jint JNICALL 
+Java_org_apache_jk_apr_AprImpl_initialize(JNIEnv *jniEnv, jobject _jthis)
+{
+    jk_env_t *env;
+
+    /* For in-process the env is initialized already */
+    if( jk_env_globalEnv == NULL ) {
+        apr_pool_t *jniAprPool=NULL;
+        jk_pool_t *globalPool;
+
+        apr_initialize(); 
+        apr_pool_create( &jniAprPool, NULL );
+
+        if( jniAprPool==NULL ) {
+            return JK_ERR;
+        }
+
+        jk2_env_setAprPool( NULL, jniAprPool );
+        
+        jk2_pool_apr_create( NULL, &globalPool, NULL, jniAprPool );
+        /* Create the global env */
+        env=jk2_env_getEnv( NULL, globalPool );
+    }
+    
+    env=jk_env_globalEnv;
+
+    workerEnv=env->getByName( env, "workerEnv" );
+    if( workerEnv==NULL ) {
+        jk_bean_t *jkb;
+
+        jkb=env->createBean2( env, env->globalPool, "logger.file", "");
+        if( jkb==NULL ) {
+            fprintf(stderr, "Error creating logger ");
+            return JK_ERR;
+        }
+
+        env->l=jkb->object;
+        env->l->name="stderr";
+        env->l->level=JK_LOG_INFO_LEVEL;
+        env->alias( env, "logger.file:", "logger");
+
+        jkb=env->createBean2( env, env->globalPool,"workerEnv", "");
+        env->alias( env, "workerEnv:", "workerEnv");
+        if( jkb==NULL ) {
+            fprintf(stderr, "Error creating workerEnv ");
+            return JK_ERR;
+        }
+
+        workerEnv=jkb->object;
+
+        
+        workerEnv->init( env, workerEnv );
+    }
+
+    return JK_OK;
+}
+
+JNIEXPORT jint JNICALL 
+Java_org_apache_jk_apr_AprImpl_terminate(JNIEnv *jniEnv, jobject _jthis)
+{
+    apr_pool_t *jniAprPool=jk_env_globalEnv->getAprPool(jk_env_globalEnv);
+
+    if ( jniAprPool!=NULL ) {
+        apr_pool_destroy(jniAprPool);
+        jniAprPool = NULL;
+/*     apr_terminate(); */
+    }
+    return 0;
+}
+
+/* -------------------- Access jk components -------------------- */
+
+/*
+ * Get a jk_env_t * from the pool
+ *
+ * XXX We should use per thread data or per jniEnv data ( the jniEnv and jk_env are
+ * serving the same purpose )
+ */
+JNIEXPORT jlong JNICALL 
+Java_org_apache_jk_apr_AprImpl_getJkEnv
+  (JNIEnv *jniEnv, jobject o )
+{
+    jk_env_t *env;
+
+    if( jk_env_globalEnv == NULL )
+        return 0;
+
+    env=jk_env_globalEnv->getEnv( jk_env_globalEnv );
+    return P2J(env);
+}
+
+
+/*
+  Release the jk env 
+*/
+JNIEXPORT void JNICALL 
+Java_org_apache_jk_apr_AprImpl_releaseJkEnv
+  (JNIEnv *jniEnv, jobject o, jlong xEnv )
+{
+    jk_env_t *env=J2P( xEnv, jk_env_t *);
+
+    if( jk_env_globalEnv != NULL ) 
+        jk_env_globalEnv->releaseEnv( jk_env_globalEnv, env );
+
+    if( jniDebug > 0 )
+        env->l->jkLog(env, env->l, JK_LOG_INFO, 
+                      "aprImpl.releaseJkEnv()  %#lx\n", env);
+}
+
+/*
+ *  Recycle the jk endpoint. Will reset the tmp pool and clean error
+ *  state.
+ */
+JNIEXPORT void JNICALL 
+Java_org_apache_jk_apr_AprImpl_jkRecycle
+  (JNIEnv *jniEnv, jobject o, jlong xEnv, jlong endpointP )
+{
+    jk_env_t *env= J2P( xEnv, jk_env_t *);
+    jk_bean_t *compCtx= J2P( endpointP, jk_bean_t *);
+    
+    jk_endpoint_t *ep = (compCtx==NULL ) ? NULL : compCtx->object;
+
+    if( env == NULL )
+        return;
+
+    if( ep!=NULL ) {
+        ep->reply->reset( env, ep->reply );
+    }
+
+    env->recycleEnv( env );
+}
+
+
+/*
+ *  Find a jk component. 
+ */
+JNIEXPORT jlong JNICALL 
+Java_org_apache_jk_apr_AprImpl_getJkHandler
+  (JNIEnv *jniEnv, jobject o, jlong xEnv, jstring compNameJ)
+{
+    jk_env_t *env= J2P(xEnv, jk_env_t *);
+    
+    jk_bean_t *component;
+    char *cname=(char *)(*jniEnv)->GetStringUTFChars(jniEnv, compNameJ, 0);
+
+    component=env->getBean( env, cname );
+    
+    (*jniEnv)->ReleaseStringUTFChars(jniEnv, compNameJ, cname);
+
+    return P2J(component);
+}
+
+/*
+  Create a jk handler XXX It should be createJkBean
+*/
+JNIEXPORT jlong JNICALL 
+Java_org_apache_jk_apr_AprImpl_createJkHandler
+  (JNIEnv *jniEnv, jobject o, jlong xEnv, jstring compNameJ)
+{
+    jk_env_t *env= J2P(xEnv, jk_env_t *);
+
+    jk_bean_t *component;
+
+    char *cname=(char *)(*jniEnv)->GetStringUTFChars(jniEnv, compNameJ, 0);
+
+    component=env->createBean( env, NULL, cname );
+    
+    (*jniEnv)->ReleaseStringUTFChars(jniEnv, compNameJ, cname);
+
+    return P2J(component);
+}
+
+/*
+*/
+JNIEXPORT jint JNICALL 
+Java_org_apache_jk_apr_AprImpl_jkSetAttribute
+  (JNIEnv *jniEnv, jobject o, jlong xEnv, jlong componentP, jstring nameJ, jstring valueJ )
+{
+    jk_env_t *env=(jk_env_t *)(void *)(long)xEnv;
+    jk_bean_t *component=(jk_bean_t *)(void *)(long)componentP;
+    
+    char *name=(char *)(*jniEnv)->GetStringUTFChars(jniEnv, nameJ, 0);
+    char *value=(char *)(*jniEnv)->GetStringUTFChars(jniEnv, valueJ, 0);
+    int rc=JK_OK;
+    
+    /* XXX need to find a way how to set this to channel:jni component
+     * instead of global variable.
+     */
+    if(env == NULL || component == NULL) {
+        if (strcmp(name, "channel:jni") == 0) {
+            if (strcmp(value, "starting") == 0)
+                jk_jni_status_code = JNI_TOMCAT_STARTING;
+            else if (strcmp(value, "done") == 0)
+               jk_jni_status_code = JNI_TOMCAT_STARTED;
+        }
+    } else {
+        if( component->setAttribute!=NULL ) {
+            rc=component->setAttribute( env, component, name,
+                                        component->pool->pstrdup( env, component->pool, value ) );
+        }
+    }
+
+    (*jniEnv)->ReleaseStringUTFChars(jniEnv, nameJ, name);
+    (*jniEnv)->ReleaseStringUTFChars(jniEnv, valueJ, value);
+    
+    return rc;
+}
+
+/*
+*/
+JNIEXPORT jint JNICALL 
+Java_org_apache_jk_apr_AprImpl_jkInit
+  (JNIEnv *jniEnv, jobject o, jlong xEnv, jlong componentP )
+{
+    jk_env_t *env=(jk_env_t *)(void *)(long)xEnv;
+    jk_bean_t *component=(jk_bean_t *)(void *)(long)componentP;
+    int rc;
+
+    if( component->init ==NULL )
+        return JK_OK;
+    
+    rc=component->init( env, component );
+    return rc;
+}
+
+/*
+*/
+JNIEXPORT jint JNICALL 
+Java_org_apache_jk_apr_AprImpl_jkDestroy
+  (JNIEnv *jniEnv, jobject o, jlong xEnv, jlong componentP )
+{
+    jk_env_t *env=(jk_env_t *)(void *)(long)xEnv;
+    jk_bean_t *component=(jk_bean_t *)(void *)(long)componentP;
+    int rc;
+    
+    if( component->destroy ==NULL )
+        return JK_OK;
+    
+    rc=component->destroy( env, component );
+
+    /* XXX component->pool->reset( env, component->pool ); */
+    
+    return rc;
+}
+
+/*
+*/
+JNIEXPORT jstring JNICALL 
+Java_org_apache_jk_apr_AprImpl_jkGetAttribute
+  (JNIEnv *jniEnv, jobject o, jlong xEnv, jlong componentP, jstring nameJ)
+{
+    jk_env_t *env=(jk_env_t *)(void *)(long)xEnv;
+    jk_bean_t *component=(jk_bean_t *)(void *)(long)componentP;
+    char *name=(char *)(*jniEnv)->GetStringUTFChars(jniEnv, nameJ, 0);
+    char *value;
+    jstring valueJ=NULL;
+    
+    if( component->getAttribute !=NULL ){   
+        value=component->getAttribute( env, component, name );
+        if( value!=NULL ) {
+            valueJ=(*jniEnv)->NewStringUTF(jniEnv, value);
+        }
+    }
+    
+    (*jniEnv)->ReleaseStringUTFChars(jniEnv, nameJ, name);
+    
+    return valueJ;
+}
+
+
+/*
+*/
+JNIEXPORT jint JNICALL 
+Java_org_apache_jk_apr_AprImpl_jkInvoke
+  (JNIEnv *jniEnv, jobject o, jlong envJ, jlong componentP, jlong endpointP, jint code,
+   jbyteArray data, jint off, jint len,
+   jint raw)
+{
+    jk_env_t *env = (jk_env_t *)(void *)(long)envJ;
+    jk_bean_t *compCtx=(jk_bean_t *)(void *)(long)endpointP;
+    void *target=(void *)(long)componentP;
+    jk_bean_t *bean=(jk_bean_t *)target;
+    jk_endpoint_t *ep;
+
+    jbyte *nbuf=NULL;
+    jboolean iscopy;
+
+    int cnt=0;
+    jint rc = 0;
+    unsigned acc = 0;
+    unsigned char *oldBuf;
+
+    if( compCtx==NULL || data==NULL ) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,"jni.jkInvoke() NPE\n");
+        return JK_ERR;
+    }
+
+    ep = compCtx->object;
+
+    if( ep==NULL || ep->reply==NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,"jni.jkInvoke() NPE ep==null\n");
+        return JK_ERR;
+    }
+            
+    if( arrayAccessMethod == JK_GET_BYTE_ARRAY_ELEMENTS ) {
+        nbuf = (*jniEnv)->GetByteArrayElements(jniEnv, data, &iscopy);
+        if( iscopy )
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "aprImpl.jkInvoke() get java bytes iscopy %d\n", iscopy);
+        
+        if(nbuf==NULL) {
+            env->l->jkLog(env, env->l, JK_LOG_ERROR, 
+                          "jkInvoke() NullPointerException 2\n");
+            return -1;
+        }
+        if( raw==0 ) {
+            ep->reply->reset(env, ep->reply);
+        }
+        oldBuf=ep->reply->buf;
+        ep->reply->buf = (unsigned char *)nbuf;
+    } else if ( arrayAccessMethod == JK_GET_REGION ) {
+        (*jniEnv)->GetByteArrayRegion( jniEnv, data, off, len, (jbyte *)ep->reply->buf );
+    }
+        
+    
+    if( raw == 0 ) {
+        rc=ep->reply->checkHeader( env, ep->reply, ep );
+    } else {
+        ep->reply->len = len;
+        ep->reply->pos= off;
+    }
+
+    /* ep->reply->dump( env, ep->reply ,"MESSAGE"); */
+    if( rc < 0  ) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "jkInvoke() invalid data\n");
+        /* we just can't recover, unset recover flag */
+        if( arrayAccessMethod == JK_GET_BYTE_ARRAY_ELEMENTS ) {
+            (*jniEnv)->ReleaseByteArrayElements(jniEnv, data, ep->reply->buf, 0);
+            ep->reply->buf=oldBuf;
+        }
+        return JK_ERR;
+    }
+
+    if( bean->debug > 0 ) 
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jkInvoke() component dispatch %d %d \n", rc, code );
+    
+    if( bean->invoke != NULL ) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jkInvoke() invoke %#lx \n", bean->invoke );
+        rc=bean->invoke( env, bean, ep, code, ep->reply, raw );
+    } else {
+        /* NOT USED. Backward compat for AJP13 messages, where the code is used to
+         locate a handler. Deprecated, use the invoke() method  ! */
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jkInvoke() component dispatch2 %d %d %#lx\n", rc, code, bean->invoke);
+        rc=workerEnv->dispatch( env, workerEnv, target, ep, code, ep->reply ); 
+    }
+
+    /* Copy back the response, if any */
+
+    if( arrayAccessMethod == JK_GET_BYTE_ARRAY_ELEMENTS ) {
+        if( rc == JK_INVOKE_WITH_RESPONSE ) {
+            /* env->l->jkLog(env, env->l, JK_LOG_INFO, */
+            /*               "jkInvoke() release byte array elements %d %d %#lx\n", */
+            /*                ep->reply->pos, ep->reply->len , ep->reply->buf ); */
+            ep->reply->end( env, ep->reply );
+            (*jniEnv)->ReleaseByteArrayElements(jniEnv, data, nbuf, JNI_ABORT );
+            rc=JK_OK;
+        } else {
+            (*jniEnv)->ReleaseByteArrayElements(jniEnv, data, nbuf, 0);
+        }
+        ep->reply->buf=oldBuf;
+    } else if ( arrayAccessMethod == JK_GET_REGION ) {
+        if( rc == JK_INVOKE_WITH_RESPONSE ) {
+
+            /*env->l->jkLog(env, env->l, JK_LOG_INFO, */
+            /*              "jkInvoke() release %d %d %#lx\n", */
+            /*              ep->reply->pos, ep->reply->len , ep->reply->buf ); */
+            ep->reply->end( env, ep->reply );
+
+            (*jniEnv)->SetByteArrayRegion( jniEnv, data, 0, ep->reply->len, (jbyte *)ep->reply->buf );
+            rc=JK_OK;
+        }
+    } 
+
+    if( (*jniEnv)->ExceptionCheck( jniEnv ) ) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jkInvoke() component dispatch %d %d %#lx\n", rc, code, bean->invoke);
+        (*jniEnv)->ExceptionDescribe( jniEnv );
+        /* Not needed if Describe is used.
+            (*jniEnv)->ExceptionClear( jniEnv ) */
+    }
+    
+    return rc;
+}
+
+static JNINativeMethod org_apache_jk_apr_AprImpl_native_methods[] = {
+    { 
+        "initialize", "()I", 
+        Java_org_apache_jk_apr_AprImpl_initialize 
+    },
+    { 
+        "terminate", "()I",
+        Java_org_apache_jk_apr_AprImpl_terminate
+    },
+    { 
+        "getJkEnv", "()J",
+        Java_org_apache_jk_apr_AprImpl_getJkEnv
+    },
+    {
+        "releaseJkEnv", "(J)V",
+        Java_org_apache_jk_apr_AprImpl_releaseJkEnv
+    },
+    { 
+        "getJkHandler", "(JLjava/lang/String;)J",
+        Java_org_apache_jk_apr_AprImpl_getJkHandler
+    },
+    {
+        "createJkHandler", "(JLjava/lang/String;)J",
+        Java_org_apache_jk_apr_AprImpl_createJkHandler
+    },
+    {
+        "jkSetAttribute", "(JJLjava/lang/String;Ljava/lang/String;)I",
+        Java_org_apache_jk_apr_AprImpl_jkSetAttribute
+    },
+    {
+        "jkGetAttribute", "(JJLjava/lang/String;)Ljava/lang/String;",
+        Java_org_apache_jk_apr_AprImpl_jkGetAttribute
+    },
+    {
+        "jkInit", "(JJ)I",
+        Java_org_apache_jk_apr_AprImpl_jkInit
+    },
+    {
+        "jkDestroy", "(JJ)I",
+        Java_org_apache_jk_apr_AprImpl_jkDestroy
+    },
+    {
+        "jkInvoke", "(JJJI[BIII)I",
+        Java_org_apache_jk_apr_AprImpl_jkInvoke
+    },
+    {
+        "jkRecycle", "(JJ)V",
+        Java_org_apache_jk_apr_AprImpl_jkRecycle
+    },
+};
+
+/*
+  Register Native methods returning the total number of
+  native functions
+*/
+jint jk_jni_aprImpl_registerNatives(JNIEnv *jniEnv, jclass bridgeClass)
+{
+  
+   return (*jniEnv)->RegisterNatives(jniEnv, bridgeClass,
+                        org_apache_jk_apr_AprImpl_native_methods,
+                        sizeof(org_apache_jk_apr_AprImpl_native_methods) / 
+                        sizeof(JNINativeMethod));
+}
+
+
+#endif /* HAVE_JNI */
diff --git a/connectors/jk/native2/jni/jkjni.dsp b/connectors/jk/native2/jni/jkjni.dsp
new file mode 100644
index 0000000..05d1ed0
--- /dev/null
+++ b/connectors/jk/native2/jni/jkjni.dsp
@@ -0,0 +1,355 @@
+# Microsoft Developer Studio Project File - Name="Jni" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=Jni - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "JKJni.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "JKJni.mak" CFG="Jni - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "Jni - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "Jni - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "Jni - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JNI_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "$(APACHE2_HOME)\include" /I "$(APACHE2_HOME)\os\win32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JNI_EXPORTS" /D "HAVE_JNI" /D "HAS_APR" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0xc0a /d "NDEBUG"
+# ADD RSC /l 0xc0a /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 libapr.lib libaprutil.lib wsock32.lib advapi32.lib /nologo /dll /machine:I386 /libpath:"$(APACHE2_HOME)\lib"
+
+!ELSEIF  "$(CFG)" == "Jni - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Jni___Win32_Debug"
+# PROP BASE Intermediate_Dir "Jni___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JNI_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "$(APACHE2_HOME)\include" /I "$(APACHE2_HOME)\os\win32" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "JNI_EXPORTS" /D "HAVE_JNI" /D "HAS_APR" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0xc0a /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 libapr.lib libaprutil.lib wsock32.lib advapi32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept /libpath:"$(APACHE2_HOME)\lib"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "Jni - Win32 Release"
+# Name "Jni - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\common\jk_channel.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_channel_apr_socket.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_channel_jni.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_channel_un.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_config.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_config_file.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_endpoint.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_env.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_handler_logon.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_handler_response.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_jni_aprImpl.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_logger_file.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_logger_win32.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_msg_ajp.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_mutex.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_mutex_proc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_mutex_thread.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_nwmain.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_objCache.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_pool_apr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_registry.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_requtil.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_shm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_signal.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_uriEnv.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_uriMap.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_user.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_vm_default.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker_ajp13.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker_jni.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker_lb.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker_run.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_worker_status.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_workerEnv.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\include\jk_bean.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_channel.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_endpoint.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_env.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_global.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_handler.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_logger_win32_message.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_msg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_mutex.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_objCache.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_registry.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_requtil.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_service.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_shm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_uriEnv.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_uriMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_vm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\jk_workerEnv.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\org_apache_jk_apr_AprImpl.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=..\common\jk_logger_win32_message.mc
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\jk_logger_win32_message.rc
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/connectors/jk/native2/jni/org_apache_jk_apr_AprImpl.h b/connectors/jk/native2/jni/org_apache_jk_apr_AprImpl.h
new file mode 100644
index 0000000..dac774c
--- /dev/null
+++ b/connectors/jk/native2/jni/org_apache_jk_apr_AprImpl.h
@@ -0,0 +1,156 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_apache_jk_apr_AprImpl */
+
+#ifndef _Included_org_apache_jk_apr_AprImpl
+#define _Included_org_apache_jk_apr_AprImpl
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef org_apache_jk_apr_AprImpl_OK
+#define org_apache_jk_apr_AprImpl_OK 0L
+#undef org_apache_jk_apr_AprImpl_LAST
+#define org_apache_jk_apr_AprImpl_LAST 1L
+#undef org_apache_jk_apr_AprImpl_ERROR
+#define org_apache_jk_apr_AprImpl_ERROR 2L
+#undef org_apache_jk_apr_AprImpl_HANDLE_RECEIVE_PACKET
+#define org_apache_jk_apr_AprImpl_HANDLE_RECEIVE_PACKET 10L
+#undef org_apache_jk_apr_AprImpl_HANDLE_SEND_PACKET
+#define org_apache_jk_apr_AprImpl_HANDLE_SEND_PACKET 11L
+#undef org_apache_jk_apr_AprImpl_HANDLE_FLUSH
+#define org_apache_jk_apr_AprImpl_HANDLE_FLUSH 12L
+/* Inaccessible static: aprSingleton */
+/* Inaccessible static: ok */
+/* Inaccessible static: jniMode */
+/*
+ * Class:     org_apache_jk_apr_AprImpl
+ * Method:    initialize
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_jk_apr_AprImpl_initialize
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_apache_jk_apr_AprImpl
+ * Method:    terminate
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_jk_apr_AprImpl_terminate
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_apache_jk_apr_AprImpl
+ * Method:    getJkEnv
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_org_apache_jk_apr_AprImpl_getJkEnv
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_apache_jk_apr_AprImpl
+ * Method:    releaseJkEnv
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_org_apache_jk_apr_AprImpl_releaseJkEnv
+  (JNIEnv *, jobject, jlong);
+
+/*
+ * Class:     org_apache_jk_apr_AprImpl
+ * Method:    getJkHandler
+ * Signature: (JLjava/lang/String;)J
+ */
+JNIEXPORT jlong JNICALL Java_org_apache_jk_apr_AprImpl_getJkHandler
+  (JNIEnv *, jobject, jlong, jstring);
+
+/*
+ * Class:     org_apache_jk_apr_AprImpl
+ * Method:    createJkHandler
+ * Signature: (JLjava/lang/String;)J
+ */
+JNIEXPORT jlong JNICALL Java_org_apache_jk_apr_AprImpl_createJkHandler
+  (JNIEnv *, jobject, jlong, jstring);
+
+/*
+ * Class:     org_apache_jk_apr_AprImpl
+ * Method:    jkSetAttribute
+ * Signature: (JJLjava/lang/String;Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_jk_apr_AprImpl_jkSetAttribute
+  (JNIEnv *, jobject, jlong, jlong, jstring, jstring);
+
+/*
+ * Class:     org_apache_jk_apr_AprImpl
+ * Method:    jkGetAttribute
+ * Signature: (JJLjava/lang/String;)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_org_apache_jk_apr_AprImpl_jkGetAttribute
+  (JNIEnv *, jobject, jlong, jlong, jstring);
+
+/*
+ * Class:     org_apache_jk_apr_AprImpl
+ * Method:    jkInit
+ * Signature: (JJ)I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_jk_apr_AprImpl_jkInit
+  (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class:     org_apache_jk_apr_AprImpl
+ * Method:    jkDestroy
+ * Signature: (JJ)I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_jk_apr_AprImpl_jkDestroy
+  (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class:     org_apache_jk_apr_AprImpl
+ * Method:    jkInvoke
+ * Signature: (JJJI[BIII)I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_jk_apr_AprImpl_jkInvoke
+  (JNIEnv *, jclass, jlong, jlong, jlong, jint, jbyteArray, jint, jint, jint);
+
+/*
+ * Class:     org_apache_jk_apr_AprImpl
+ * Method:    jkRecycle
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL Java_org_apache_jk_apr_AprImpl_jkRecycle
+  (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class:     org_apache_jk_apr_AprImpl
+ * Method:    setUser
+ * Signature: (Ljava/lang/String;Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_jk_apr_AprImpl_setUser
+  (JNIEnv *, jobject, jstring, jstring);
+
+/*
+ * Class:     org_apache_jk_apr_AprImpl
+ * Method:    setPid
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_jk_apr_AprImpl_setPid
+  (JNIEnv *, jobject);
+
+/*
+ * Class:     org_apache_jk_apr_AprImpl
+ * Method:    signal
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_apache_jk_apr_AprImpl_signal
+  (JNIEnv *, jobject, jint);
+
+/*
+ * Class:     org_apache_jk_apr_AprImpl
+ * Method:    sendSignal
+ * Signature: (II)V
+ */
+JNIEXPORT void JNICALL Java_org_apache_jk_apr_AprImpl_sendSignal
+  (JNIEnv *, jobject, jint, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/connectors/jk/native2/scripts/build/config_vars.mk b/connectors/jk/native2/scripts/build/config_vars.mk
new file mode 100644
index 0000000..abce97e
--- /dev/null
+++ b/connectors/jk/native2/scripts/build/config_vars.mk
@@ -0,0 +1,2 @@
+# libtool is given by Apache-2.0 when installed otherwise we provide it.
+LIBTOOL      = $(SHELL) ../libtool
diff --git a/connectors/jk/native2/scripts/build/rules.mk b/connectors/jk/native2/scripts/build/rules.mk
new file mode 100644
index 0000000..9448674
--- /dev/null
+++ b/connectors/jk/native2/scripts/build/rules.mk
@@ -0,0 +1,29 @@
+# That an extract of what is in APR.
+#
+
+# Compile commands
+#VPATH=.:../common
+COMPILE      = $(CC) $(CFLAGS)
+LT_COMPILE   = $(LIBTOOL) --mode=compile $(COMPILE) -c $< 
+# && touch $@
+
+# Implicit rules for creating outputs from input files
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .slo .s
+
+.c.o:
+	$(COMPILE) -c $<
+
+.s.o:
+	$(COMPILE) -c $<
+
+.c.lo:
+	$(LT_COMPILE)
+
+.s.lo:
+	$(LT_COMPILE)
+
+.c.slo:
+	$(SH_COMPILE)
+
diff --git a/connectors/jk/native2/scripts/build/unix/dummy.am b/connectors/jk/native2/scripts/build/unix/dummy.am
new file mode 100644
index 0000000..1e0b230
--- /dev/null
+++ b/connectors/jk/native2/scripts/build/unix/dummy.am
@@ -0,0 +1,2 @@
+#Dummy file to prevent the error message from automake:
+#"no `Makefile.am' found or specified"
diff --git a/connectors/jk/native2/server/aolserver/jk_logger_ns.c b/connectors/jk/native2/server/aolserver/jk_logger_ns.c
new file mode 100644
index 0000000..03cbc19
--- /dev/null
+++ b/connectors/jk/native2/server/aolserver/jk_logger_ns.c
@@ -0,0 +1,177 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Description: Logger implementation using AOLserver's native logging.
+ *
+ * Author: Alexander Leykekh, aolserver@aol.net
+ *
+ * $Revision$
+ *
+ */ 
+
+#include "jk_ns.h"
+#include <stdio.h>
+
+static int JK_METHOD jk2_logger_ns_log(jk_env_t *env, jk_logger_t *l,                                 
+				       int level,
+				       const char *what)
+{
+    return JK_OK;
+}
+
+
+static int JK_METHOD jk2_logger_ns_init(jk_env_t *env, jk_logger_t *_this)
+{
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_logger_ns_close(jk_env_t *env, jk_logger_t *_this)
+{
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_logger_ns_jkVLog(jk_env_t *env, jk_logger_t *l,
+					  const char *file,
+					  int line,
+					  int level,
+					  const char *fmt,
+					  va_list args)
+{
+    apr_pool_t *aprPool;
+    int rc;
+    char *buf, *buf2, *buf3;
+
+    if (env==NULL || env->tmpPool==NULL || l==NULL || fmt==NULL)
+        return JK_ERR;
+
+    aprPool=env->tmpPool->_private;
+    if (aprPool == NULL)
+        return JK_ERR;
+
+    if( level < l->level )
+        return JK_OK;
+
+    buf=apr_pvsprintf( aprPool, fmt, args );
+    if (buf == NULL)
+        return JK_ERR;
+    
+    rc=strlen( buf );
+    /* Remove trailing \n. */
+    if( buf[rc-1] == '\n' )
+        buf[rc-1]='\0';
+
+    if (file != NULL) {
+        buf2 = apr_psprintf (aprPool, "%s:%d:", file, line);
+	if (buf2 == NULL)
+	    return JK_ERR;
+
+	buf3 = apr_pstrcat (aprPool, buf2, buf, NULL);
+	if (buf3 == NULL)
+	    return JK_ERR;
+
+    } else {
+        buf3 = buf;
+    }
+    
+    if( level == JK_LOG_DEBUG_LEVEL ) {
+        Ns_Log (Debug, "nsjk2: %s", buf3);
+    } else if( level == JK_LOG_INFO_LEVEL ) {
+        Ns_Log (Notice, "nsjk2: %s", buf3);
+    } else {
+        Ns_Log (Error, "nsjk2: %s", buf3);
+    }
+
+    return JK_OK;
+}
+
+static int jk2_logger_ns_jkLog(jk_env_t *env, jk_logger_t *l,
+                               const char *file,
+			       int line,
+			       int level,
+			       const char *fmt, ...)
+{
+    va_list args;
+    int rc;
+
+    if (env==NULL || l==NULL || fmt==NULL)
+        return JK_ERR;    
+
+    va_start(args, fmt);
+    rc=jk2_logger_ns_jkVLog( env, l, file, line, level, fmt, args );
+    va_end(args);
+
+    return rc;
+}
+
+
+static int JK_METHOD
+jk2_logger_file_setProperty(jk_env_t *env, jk_bean_t *mbean, 
+                            char *name,  void *valueP )
+{
+    jk_logger_t *_this;
+    char *value=valueP;
+
+    if (env==NULL || mbean==NULL || name==NULL)
+        return JK_ERR;
+
+    _this=mbean->object;
+    if (_this == NULL)
+        return JK_ERR;
+
+    if( strcmp( name, "level" )==0 ) {
+        _this->level = jk2_logger_file_parseLogLevel(env, value);
+        if( _this->level == JK_LOG_DEBUG_LEVEL ) {
+            env->debug = 1;
+            /*             _this->jkLog( env, _this, JK_LOG_ERROR, */
+            /*                           "Level %s %d \n", value, _this->level ); */
+        }
+        return JK_OK;
+    }
+    return JK_ERR;
+}
+
+
+
+int JK_METHOD 
+jk2_logger_ns_factory(jk_env_t *env, jk_pool_t *pool, jk_bean_t *result,
+		      const char *type, const char *name)
+{
+    jk_logger_t *l;
+
+    if (env==NULL || pool==NULL || result==NULL)
+        return JK_ERR;
+
+    l = (jk_logger_t *)pool->calloc(env, pool, sizeof(jk_logger_t));
+    if(l==NULL ) {
+        return JK_ERR;
+    }
+    
+    l->log = jk2_logger_ns_log;
+    l->logger_private = NULL;
+    l->init =jk2_logger_ns_init;
+    l->jkLog = jk2_logger_ns_jkLog;
+    l->jkVLog = jk2_logger_ns_jkVLog;
+
+    l->level=JK_LOG_ERROR_LEVEL;
+    
+    result->object=(void *)l;
+    l->mbean=result;
+    result->setAttribute = jk2_logger_file_setProperty;
+
+    return JK_OK;
+}
+
diff --git a/connectors/jk/native2/server/aolserver/jk_ns.h b/connectors/jk/native2/server/aolserver/jk_ns.h
new file mode 100644
index 0000000..8b732e3
--- /dev/null
+++ b/connectors/jk/native2/server/aolserver/jk_ns.h
@@ -0,0 +1,63 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Description: AOLserver plugin for Jakarta/Tomcat                         
+ * Author:      Alexander Leykekh, aolserver@aol.net 
+ * Version:     $Revision$                                           
+ */
+
+#ifndef JK_NS_H
+#define JK_NS_H
+
+#include "apu_compat.h"
+#include "apr_lib.h"
+#include "apr_date.h"
+#include "apr_strings.h"
+#include "apr_pools.h"
+#include "apr_tables.h"
+#include "apr_hash.h"
+
+#include "ns.h"
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_env.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+#include "jk_workerEnv.h"
+#include "jk_uriMap.h"
+#include "jk_requtil.h"
+
+extern int Ns_ModuleVersion;
+
+int Ns_ModuleInit(char *server, char *module);
+
+int JK_METHOD jk2_service_ns_init(jk_env_t *env, jk_ws_service_t *s, char* serverNameOverride);
+
+int JK_METHOD jk2_logger_ns_factory(jk_env_t *env, jk_pool_t *pool,
+                                         jk_bean_t *result, const char *type, const char *name);
+
+/* from ../../common/jk_pool_apr.c */
+int JK_METHOD jk2_pool_apr_factory(jk_env_t *env, jk_pool_t *pool,
+                                   jk_bean_t *result, const char *type, const char *name);
+
+int JK_METHOD jk2_map_aprtable_factory(jk_env_t *env, jk_pool_t *pool,
+                                       jk_bean_t *result,
+                                       const char *type, const char *name);
+
+#endif /* JK_NS_H */
diff --git a/connectors/jk/native2/server/aolserver/jk_service_ns.c b/connectors/jk/native2/server/aolserver/jk_service_ns.c
new file mode 100644
index 0000000..241cb20
--- /dev/null
+++ b/connectors/jk/native2/server/aolserver/jk_service_ns.c
@@ -0,0 +1,505 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Facility: AOLserver plugin for Jakarta/Tomcat                         
+ * Description: Implementation of JK2 HTTP request phases
+ * Author:      Alexander Leykekh, aolserver@aol.net 
+ * Version:     $Revision$                                           
+ */
+
+/*
+ * Jakarta (jk_) include files
+ */
+#include "jk_ns.h"
+
+#define NULL_FOR_EMPTY(x)   ((((x)!=NULL) && (strlen((x))!=0)) ? (x) : NULL ) 
+
+static int JK_METHOD jk2_service_ns_head(jk_env_t *env, jk_ws_service_t *s )
+{
+    int h;
+    int numheaders;
+    Ns_Conn *conn;
+    jk_map_t *headers;
+    int debug=1;
+    char *name, *val;
+    time_t lastMod;
+    
+    if(env==NULL || env->l==NULL || env->l->jkLog==NULL)
+       return JK_ERR;
+
+    if(s==NULL ||  s->ws_private==NULL || s->headers_out==NULL || s->pool==NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, 
+                      "service.head() NullPointerException\n");
+        return JK_ERR;
+    }
+    
+    if( s->uriEnv!=NULL && s->uriEnv->mbean!=NULL)
+        debug=s->uriEnv->mbean->debug;
+
+    conn = (Ns_Conn *)s->ws_private;
+    if (conn == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, 
+                      "service.head() no current connection\n");
+	return JK_ERR;
+    }
+
+    if(s->msg==NULL) {
+        s->msg = "";
+    }
+
+    headers=s->headers_out;
+
+    numheaders = headers->size(env, headers);
+
+    if( debug > 0 )
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG, 
+                      "service.head() %d %d %#lx\n", s->status,
+                      numheaders, s->uriEnv);
+    
+    for(h = 0 ; h < numheaders; h++) {
+        name=headers->nameAt( env, headers, h );
+        val=headers->valueAt( env, headers, h );
+
+	if (name==NULL || val==NULL) {
+	  env->l->jkLog(env, env->l, JK_LOG_ERROR, 
+                      "service.head() NullPointerException\n");
+	  return JK_ERR;
+	}
+
+        name=s->pool->pstrdup( env, s->pool, name );
+        val=s->pool->pstrdup( env, s->pool, val );
+
+	if (name==NULL || val==NULL) {
+	  env->l->jkLog(env, env->l, JK_LOG_ERROR, 
+                      "service.head() NullPointerException\n");
+	  return JK_ERR;
+	}
+
+        if( debug > 0 ) 
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG, 
+                          "service.head() %s: %s %d %d\n", name, val, h, headers->size( env, headers ));
+
+        if(!strcasecmp(name, "Content-type")) {
+	    Ns_ConnSetTypeHeader (conn, Ns_StrToLower (val));
+
+        } else if(!strcasecmp(name, "Last-Modified")) {
+            /*
+             * If the script gave us a Last-Modified header, we can't just
+             * pass it on blindly because of restrictions on future values.
+	     *
+             */
+	    lastMod = apr_date_parse_http (val);
+	    Ns_ConnSetLastModifiedHeader (conn, &lastMod);
+
+        } else {                
+	    Ns_ConnSetHeaders (conn, name, val);
+        }
+    }
+    
+    if (Ns_ConnFlushHeaders (conn, s->status) != NS_OK) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, 
+                      "service.head() cannot flush headers\n");
+	return JK_ERR;
+    }
+
+    s->response_started = JK_TRUE;
+    
+    return JK_OK;
+}
+
+/*
+ * Read a chunk of the request body into a buffer.  Attempt to read len
+ * bytes into the buffer.  Write the number of bytes actually read into
+ * actually_read.
+ *
+ * Think of this function as a method of the AOLserver-specific subclass of
+ * the jk_ws_service class.  Think of the *s param as a "this" or "self"
+ * pointer.
+ */
+static int JK_METHOD jk2_service_ns_read(jk_env_t *env, jk_ws_service_t *s,
+					 void *b, unsigned len,
+					 unsigned *actually_read)
+{
+    int rv;
+
+    if(s==NULL || s->ws_private==NULL ||  b==NULL || actually_read==NULL || s->ws_private==NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, 
+                      "service.read() NullPointerException\n");
+        return JK_ERR;
+    }
+
+    s->read_body_started = JK_TRUE;
+
+    if ((rv = Ns_ConnRead((Ns_Conn *)s->ws_private, b, len)) == NS_ERROR) {
+        *actually_read = 0;
+    } else {
+        *actually_read = (unsigned) rv;
+    }
+
+    return JK_OK;
+}
+
+/*
+ * Write a chunk of response data back to the browser.  If the headers
+ * haven't yet been sent over, send over default header values (Status =
+ * 200, basically).
+ *
+ * Write len bytes from buffer b.
+ *
+ * Think of this function as a method of the AOLserver-specific subclass of
+ * the jk_ws_service class.  Think of the *s param as a "this" or "self"
+ * pointer.
+ */
+
+static int JK_METHOD jk2_service_ns_write(jk_env_t *env, jk_ws_service_t *s,
+                                               const void *b, unsigned len)
+{
+    int r = 0;
+    char *bb=(char *)b;
+    Ns_Conn *conn;
+    Ns_Set *outHeaders;
+    int i;
+    int headerCount;
+    int debug=1;
+    int rc;
+
+    if (env->l->jkLog == NULL)
+        return JK_ERR;
+
+    if(s==NULL  || s->ws_private == NULL ||  b==NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, 
+                      "service.write() NullPointerException\n");
+        return JK_ERR;
+    }
+    if( s->uriEnv != NULL )
+        debug=s->uriEnv->mbean->debug;
+    
+    if(len==0 ) {
+        return JK_OK;
+    }
+
+    conn=(Ns_Conn *)s->ws_private;
+    
+    if(!s->response_started) {
+        if( debug > 0 )
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG, 
+                          "service.write() default head\n");
+        rc=s->head(env, s);
+        if( rc != JK_OK) {
+            return rc;
+        }
+        
+	if (debug > 0) {
+	    outHeaders = Ns_ConnOutputHeaders (conn);
+	    headerCount = Ns_SetSize (outHeaders);
+
+	    if (outHeaders && headerCount) {
+	        for (i = 0 ; i < headerCount ; i++) {
+		    env->l->jkLog(env, env->l, JK_LOG_DEBUG, "OutHeaders %s: %s\n",
+				  Ns_SetKey (outHeaders, i), Ns_SetValue (outHeaders, i));
+		}
+	    }
+	}
+    }
+    
+    while( len > 0 ) {
+        r = Ns_ConnWrite (conn, bb, len);
+	
+	if (r == -1)
+	    return JK_ERR;
+
+        len -= r;
+        bb += r;
+    } 
+        
+    return JK_OK;
+}
+
+/* ========================================================================= */
+/* Utility functions                                                         */
+/* ========================================================================= */
+
+static int JK_METHOD jk2_init_ws_service(jk_env_t *env, jk_ws_service_t *s,
+                                         jk_worker_t *worker, void *serverObj)
+{
+    jk_workerEnv_t *workerEnv;
+    Ns_Conn *conn=serverObj;
+    int need_content_length_header=JK_FALSE;
+    char *query_str_begin, *query_str_end;
+    Ns_DString dstr;
+    int count, i;
+    char *name, *val;
+    Ns_Set *reqHdrs = Ns_ConnHeaders (conn);
+    char* override = s->server_name; /* jk2_requtil_initRequest will null s->server_name */
+
+    if (env == NULL)
+        return JK_ERR;
+
+    if (s==NULL || s->pool==NULL || worker==NULL || serverObj==NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, 
+                      "init_ws_service() NullPointerException\n");
+        return JK_ERR;
+    }
+
+    workerEnv = worker->workerEnv;
+    /* Common initialization */
+    jk2_requtil_initRequest(env, s);
+
+    s->ws_private = conn;
+    s->response_started = JK_FALSE;
+    s->read_body_started = JK_FALSE;
+    s->workerEnv=workerEnv;
+
+    /* Unless s->realWorker is set, JNI channel will not detach thread from JVM at
+       the end of request
+    s->jvm_route = worker->route;
+    s->realWorker = worker;
+    */
+
+    s->remote_user  = NULL_FOR_EMPTY(Ns_ConnAuthUser (conn));
+    s->auth_type    = (s->remote_user==NULL? NULL : "Basic");
+
+    /* conn->request->line has HTTP request line */
+    if (conn->request!=NULL && conn->request->line!=NULL)
+        s->protocol = strrchr (conn->request->line, ' ');
+
+    if (s->protocol != NULL)
+        s->protocol++;
+    else
+        s->protocol = "HTTP/1.0";
+
+    s->remote_host  = s->remote_addr = NULL_FOR_EMPTY(Ns_ConnPeer (conn));
+
+    /* get server name */
+    if (override != NULL)
+        s->server_name = override;
+
+    else { 
+        if (reqHdrs != NULL)
+	    s->server_name = Ns_SetIGet (reqHdrs, "Host");
+
+	if (s->server_name == NULL)
+	    s->server_name= Ns_ConnServer (conn);
+    }
+
+    /* get the real port (otherwise redirect failed) */
+    s->server_port = Ns_ConnPort (conn);
+
+    s->server_software = Ns_InfoServerVersion ();
+
+    s->method         = conn->request->method;
+    s->content_length = Ns_ConnContentLength (conn);
+    s->is_chunked     = JK_FALSE;
+    s->no_more_chunks = JK_FALSE;
+    s->query_string   = conn->request->query;
+
+    s->startTime = time (NULL);
+    /*
+     * The 2.2 servlet spec errata says the uri from
+     * HttpServletRequest.getRequestURI() should remain encoded.
+     * [http://java.sun.com/products/servlet/errata_042700.html]
+     *
+     * We use JkOptions to determine which method to be used
+     *
+     */
+
+    switch (workerEnv->options & JK_OPT_FWDURIMASK) {
+
+        case JK_OPT_FWDURICOMPATUNPARSED :
+	    /* Parse away method, query and protocol from request line, e.g.,
+	     * 
+	     * GET /x/y?q HTTP/1.1
+	     *
+	     */
+	    query_str_begin = strchr (conn->request->line, ' ');
+	    if (query_str_begin == NULL) {
+	        s->req_uri = NULL;
+		break;
+	    }
+
+	    query_str_end = strchr (++query_str_begin, '?'); /* exclude query */
+	    if (query_str_end == NULL) {
+	      query_str_end = strchr (query_str_begin, ' '); /* no query? find where URI ends */
+		
+		if (query_str_end == NULL)
+		    query_str_end = conn->request->line+strlen(conn->request->line);
+	    }
+
+	    s->req_uri = s->pool->alloc (env, s->pool, query_str_end-query_str_begin);
+	    if (s->req_uri == NULL)
+	        break;
+
+            memcpy (s->req_uri, query_str_begin, query_str_end-query_str_begin+1); /* include 0 terminator */
+	    
+
+        break;
+
+        case JK_OPT_FWDURICOMPAT :
+            s->req_uri = conn->request->url;
+        break;
+
+        case JK_OPT_FWDURIESCAPED :
+	    Ns_DStringInit (&dstr);
+	    query_str_begin = Ns_EncodeUrl (&dstr, conn->request->url);
+	    
+	    if (query_str_begin != NULL) {
+	        s->req_uri = s->pool->pstrdup (env, s->pool, query_str_begin+1); /* +1 because string starts with ? */
+ 	    } else {
+	        s->req_uri = NULL;
+	    }
+
+	    Ns_DStringFree (&dstr);
+        break;
+
+        default :
+	    return JK_ERR;
+    }
+
+    s->is_ssl       = JK_FALSE;
+    s->ssl_cert     = NULL;
+    s->ssl_cert_len = 0;
+    s->ssl_cipher   = NULL;        /* required by Servlet 2.3 Api, 
+                                   allready in original ajp13 */
+    s->ssl_session  = NULL;
+    s->ssl_key_size = -1;        /* required by Servlet 2.3 Api, added in jtc */
+
+    if (workerEnv->ssl_enable || workerEnv->envvars_in_use) {
+        if (workerEnv->ssl_enable) {
+
+	    name = Ns_ConnDriverName (conn);
+
+	    if (name!=NULL && strcmp(name, "nsssl")==0)
+	        s->is_ssl = JK_TRUE;
+	    else 
+	        s->is_ssl = JK_FALSE;
+		
+	    if (s->is_ssl == JK_TRUE) {
+	        s->ssl_cert     = NULL; /* workerEnv->certs_indicator "SSL_CLIENT_CERT" */
+
+		if (s->ssl_cert) {
+		    s->ssl_cert_len = strlen(s->ssl_cert);
+		}
+
+		/* Servlet 2.3 API */
+		s->ssl_cipher   = NULL; /* workerEnv->cipher_indicator "HTTPS_CIPHER"/"SSL_CIPHER" */
+		s->ssl_session  = NULL; /* workerEnv->session_indicator "SSL_SESSION_ID" */
+
+		if (workerEnv->options & JK_OPT_FWDKEYSIZE) {
+		    /* Servlet 2.3 API */
+		    s->ssl_key_size = 0;
+		}
+	    }
+	}
+
+	jk2_map_default_create(env, &s->attributes, s->pool );
+        
+	if (workerEnv->envvars_in_use && reqHdrs) {
+	    count = workerEnv->envvars->size( env, workerEnv->envvars );
+	
+	    for ( i=0; i< count ; i++ ) {
+	        name= workerEnv->envvars->nameAt( env, workerEnv->envvars, i );
+		val= Ns_SetIGet (reqHdrs, name);
+
+		if (val==NULL) {
+		    val=workerEnv->envvars->valueAt( env, workerEnv->envvars, i );
+		}
+
+		s->attributes->put( env, s->attributes, name, val, NULL );
+	    }
+	}
+    }
+
+    jk2_map_default_create(env, &s->headers_in, s->pool );
+
+    if (reqHdrs && (count=Ns_SetSize (reqHdrs))) {
+        for (i = 0 ; i < count ; i++) {
+	     s->headers_in->add( env, s->headers_in, Ns_SetKey (reqHdrs, i), Ns_SetValue (reqHdrs, i));
+	}
+    }
+
+    if(!s->is_chunked && s->content_length == 0) {
+        /* if r->contentLength == 0 I assume there's no header
+           or a header with '0'. In the second case, put will override it 
+         */
+        s->headers_in->put( env, s->headers_in, "content-length", "0", NULL );
+    }
+
+    jk2_map_default_create(env, &s->headers_out, s->pool );
+
+    return JK_OK;
+}
+
+/*
+ * If the servlet engine didn't consume all of the
+ * request data, consume and discard all further
+ * characters left to read from client
+ *
+ */
+static void JK_METHOD jk2_service_ns_afterRequest(jk_env_t *env, jk_ws_service_t *s )
+{
+    Ns_Conn *conn;
+    char buff[2048];
+    int rd;
+    struct jk_worker *w;
+
+    if (env==NULL)
+        return;
+
+    if (s==NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, 
+                      "service.afterRequest() NullPointerException\n");
+        return;
+    }
+
+    if (s->content_read < s->content_length ||
+        (s->is_chunked && ! s->no_more_chunks)) {
+        
+         conn = s->ws_private;
+
+	 while ((rd = Ns_ConnRead (conn, buff, sizeof(buff))) > 0) {
+	     s->content_read += rd;
+	 }
+    }
+
+    if (s->realWorker) {
+        w = s->realWorker;
+
+        if (w != NULL && w->channel != NULL && w->channel->afterRequest != NULL) {
+            w->channel->afterRequest( env, w->channel, w, NULL, s );
+        }
+    }
+}
+
+
+
+int JK_METHOD jk2_service_ns_init(jk_env_t *env, jk_ws_service_t *s, char* serverNameOverride)
+{
+    if(s==NULL ) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, 
+                      "service.init() NullPointerException\n");
+        return JK_ERR;
+    }
+
+    s->head   = jk2_service_ns_head;
+    s->read   = jk2_service_ns_read;
+    s->write  = jk2_service_ns_write;
+    s->init   = jk2_init_ws_service;
+    s->afterRequest     = jk2_service_ns_afterRequest;
+    s->server_name = serverNameOverride;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/server/aolserver/nsjk2.c b/connectors/jk/native2/server/aolserver/nsjk2.c
new file mode 100644
index 0000000..8634b71
--- /dev/null
+++ b/connectors/jk/native2/server/aolserver/nsjk2.c
@@ -0,0 +1,501 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/*
+ * Module jk2: keeps all servlet/jakarta related ramblings together.
+ *
+ * Author: Alexander Leykekh (aolserver@aol.net)
+ *
+ */
+
+
+#include "ns.h"
+#include "jk_ns.h"
+#include <jni.h>
+
+#define LOG_ERROR(msg) Ns_Log(Error,"nsjk2: %s:%d:$s",__FILE__,__LINE__,msg)
+#define LOG_ERROR2(msg1,msg2) Ns_Log(Error,"nsjk2: %s:%d:$s:%s",__FILE__,__LINE__,msg1,msg2)
+
+/* Context allows request handler extract worker and vhost for the URI quickly */
+typedef struct {
+    char* serverName;
+    jk_uriEnv_t* uriEnv;
+} uriContext;
+  
+
+#ifdef WIN32
+static char  file_name[_MAX_PATH];
+#endif
+
+
+/* Standard AOLserver module global */
+int Ns_ModuleVersion = 1;
+
+/* Globals 
+ *
+ * There is one instance of JVM worker environment per process, regardless of 
+ * virtual servers in AOLserver's nsd.tcl file or their counterparts in
+ * Tomcat's server.xml. The only thing that AOLserver virtual servers do separately
+ * is map URIs for Tomcat to process.
+ *
+ */
+static jk_workerEnv_t *workerEnv = NULL; /* JK2 environment */
+static JavaVM* jvmGlobal = NULL; /* JVM instance */
+static Ns_Tls jkTls; /* TLS destructor detaches request thread from JVM */
+static unsigned jkInitCount =0; /* number of running virtual servers using this module */
+
+/** Basic initialization for jk2.
+ */
+static int jk2_create_workerEnv(apr_pool_t *p, char* serverRoot) {
+    jk_env_t *env;
+    jk_logger_t *l;
+    jk_pool_t *globalPool;
+    jk_bean_t *jkb;
+    
+    if (jk2_pool_apr_create( NULL, &globalPool, NULL, p) != JK_OK) {
+        LOG_ERROR("Cannot create apr pool");
+	return JK_ERR;
+    }
+
+    /** Create the global environment. This will register the default
+        factories
+    */
+    env=jk2_env_getEnv( NULL, globalPool );
+    if (env == NULL) {
+        LOG_ERROR("jk2_create_workerEnv: Cannot get environment");
+	return JK_ERR;
+    }
+        
+    /* Optional. Register more factories ( or replace existing ones ) */
+    /* Init the environment. */
+    
+    /* Create the logger */
+    env->registerFactory( env, "logger.ns",  jk2_logger_ns_factory );
+
+    jkb=env->createBean2( env, env->globalPool, "logger.ns", "");
+    if (jkb == NULL) {
+        LOG_ERROR("Cannot create logger bean (with factory)");
+	return JK_ERR;
+    }
+
+    env->alias( env, "logger.ns:", "logger");
+    l = jkb->object;
+    env->l=l;
+    
+#ifdef WIN32
+    env->soName=env->globalPool->pstrdup(env, env->globalPool, file_name);
+    
+    if( env->soName == NULL ){
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, "Error creating env->soName\n");
+        return JK_ERR;
+    }
+#else 
+    env->soName=NULL;
+#endif
+    
+    /* Create the workerEnv */
+    jkb=env->createBean2( env, env->globalPool,"workerEnv", "");
+    if (jkb == NULL) {
+        LOG_ERROR("Cannot create worker environment bean");
+	return JK_ERR;
+    }
+
+    workerEnv= jkb->object;
+
+    if( workerEnv==NULL ) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, "Error creating workerEnv\n");
+        return JK_ERR;
+    }
+
+    env->alias( env, "workerEnv:" , "workerEnv");
+
+    /* AOLserver is a single-process environment, but leaving childId as -1 breaks
+       JNI initialization
+    */
+    workerEnv->childId=0;
+    
+    workerEnv->initData->add( env, workerEnv->initData, "serverRoot",
+                              workerEnv->pool->pstrdup( env, workerEnv->pool, serverRoot));
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO, "Set JK2 serverRoot %s\n", serverRoot);
+    
+    return JK_OK;
+}
+
+
+/**
+ * Shutdown callback for AOLserver
+ */
+void jk2_shutdown_system (void* data)
+{
+    if (--jkInitCount == 0) {
+
+	apr_pool_terminate ();
+	apr_terminate ();
+    }
+}    
+
+
+/**
+ * JK2 module shutdown callback for APR (Apache Portable Runtime)
+ */
+static apr_status_t jk2_shutdown(void *data)
+{
+    jk_env_t *env;
+    if (workerEnv) {
+        env=workerEnv->globalEnv;
+        /* bug in APR?? results in SEGV. Not important, the process is going away
+
+	workerEnv->close(env, workerEnv);
+	*/
+
+	if( workerEnv->vm != NULL  && !workerEnv->vm->mbean->disabled)
+	    workerEnv->vm->destroy( env, workerEnv->vm );
+
+        workerEnv = NULL;
+    }
+    return APR_SUCCESS;
+}
+
+
+/* ========================================================================= */
+/* The JK module handlers                                                    */
+/* ========================================================================= */
+
+/** Main service method, called to forward a request to tomcat
+ */
+static int jk2_handler(void* context, Ns_Conn *conn)
+{   
+    jk_logger_t      *l=NULL;
+    int              rc;
+    jk_worker_t *worker=NULL;
+    jk_endpoint_t *end = NULL;
+    jk_env_t *env;
+    char* workerName;
+    jk_ws_service_t *s=NULL;
+    jk_pool_t *rPool=NULL;
+    int rc1;
+    jk_uriEnv_t *uriEnv;
+    uriContext* uriCtx = (uriContext*)context;
+
+
+    /* must make this call to assure TLS destructor runs when thread exists */
+    Ns_TlsSet (&jkTls, jvmGlobal);
+
+    uriEnv= uriCtx->uriEnv;
+    /* there is a chance of dynamic reconfiguration, so instead of doing above statement, might map
+       the URI to its context object on the fly, like this:
+
+       uriEnv = workerEnv->uriMap->mapUri (workerEnv->globalEnv, workerEnv->uriMap, conn->request->host, conn->request->url);
+    */
+
+    /* Get an env instance */
+    env = workerEnv->globalEnv->getEnv( workerEnv->globalEnv );
+
+    worker = uriEnv->worker;
+
+    if (worker == NULL) /* use global default */
+        worker=workerEnv->defaultWorker;
+
+    workerName = uriEnv->mbean->getAttribute (env, uriEnv->mbean, "group");
+
+    if( worker==NULL && workerName!=NULL ) {
+        worker=env->getByName( env, workerName);
+        env->l->jkLog(env, env->l, JK_LOG_INFO, 
+                      "finding worker for %#lx %#lx %s\n",
+                      worker, uriEnv, workerName);
+        uriEnv->worker=worker;
+    }
+
+    if(worker==NULL || worker->mbean==NULL || worker->mbean->localName==NULL ) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, 
+                      "No worker for %s\n", conn->request->url); 
+        workerEnv->globalEnv->releaseEnv( workerEnv->globalEnv, env );
+        return NS_FILTER_RETURN;
+    }
+
+    if( uriEnv->mbean->debug > 0 )
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG, 
+                      "serving %s with %#lx %#lx %s\n",
+                      uriEnv->mbean->localName, worker, worker->mbean, worker->mbean->localName );
+
+    /* Get a pool for the request */
+    rPool= worker->rPoolCache->get( env, worker->rPoolCache );
+    if( rPool == NULL ) {
+        rPool=worker->mbean->pool->create( env, worker->mbean->pool, HUGE_POOL_SIZE );
+        if( uriEnv->mbean->debug > 0 )
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG, "new rpool %#lx\n", rPool );
+    }
+    
+    s=(jk_ws_service_t *)rPool->calloc( env, rPool, sizeof( jk_ws_service_t ));
+    
+    jk2_service_ns_init( env, s, uriCtx->serverName);
+    
+    s->pool = rPool;
+    s->init( env, s, worker, conn );
+
+    /* reset the reco_status, will be set to INITED in LB mode */
+    s->reco_status = RECO_NONE;
+    
+    s->is_recoverable_error = JK_FALSE;
+    s->uriEnv = uriEnv; 
+    rc = worker->service(env, worker, s);    
+    s->afterRequest(env, s);
+    rPool->reset(env, rPool);
+
+    rc1=worker->rPoolCache->put( env, worker->rPoolCache, rPool );
+
+    if( rc1 == JK_OK ) {
+        rPool=NULL;
+    }
+
+    if( rPool!=NULL ) {
+        rPool->close(env, rPool);
+    }
+
+    if(rc==JK_OK) {
+        workerEnv->globalEnv->releaseEnv( workerEnv->globalEnv, env );
+        return NS_OK;
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_ERROR, "Error connecting to tomcat %d\n", rc);
+    workerEnv->globalEnv->releaseEnv( workerEnv->globalEnv, env );
+    Ns_ConnReturnInternalError (conn);
+    return NS_FILTER_RETURN;
+}
+
+
+/*
+ * TLS destructor. Sole purpose is to detach current thread from JVM before the thread exits
+ */
+static void jkTlsDtor (void* arg) {
+    void* env = NULL;
+    jint err;
+
+    /* check if attached 1st */
+    if (jvmGlobal!=NULL && 
+	JNI_OK==((*jvmGlobal)->GetEnv(jvmGlobal, &env, JNI_VERSION_1_2 ))) 
+        {
+        (*jvmGlobal)->DetachCurrentThread (jvmGlobal);
+	}
+}
+
+
+/** Register a URI pattern with AOLserver
+ */
+static void registerURI (jk_env_t* env, jk_uriEnv_t* uriEnv, char* vserver, char* serverName) {
+    uriContext* ctx;
+    char* uriname;
+
+    if (uriEnv==NULL || uriEnv->mbean==NULL)
+	return;
+
+    uriname=uriEnv->mbean->getAttribute (env, uriEnv->mbean, "uri");
+    if (uriname==NULL || strcmp(uriname, "/")==0)
+        return;
+
+    ctx = Ns_Malloc (sizeof (uriContext));
+    if (ctx == NULL)
+        return;
+
+    ctx->serverName = serverName;
+    ctx->uriEnv = uriEnv;
+
+    if (uriEnv->mbean->debug > 0)
+        env->l->jkLog (env, env->l, JK_LOG_DEBUG, "registering URI %s", uriname);
+
+    Ns_RegisterRequest(vserver, "GET", uriname, jk2_handler, NULL, ctx, 0);
+    Ns_RegisterRequest(vserver, "HEAD", uriname, jk2_handler, NULL, ctx, 0);
+    Ns_RegisterRequest(vserver, "POST", uriname, jk2_handler, NULL, ctx, 0);
+}
+
+
+/** Standard AOLserver callback.
+ * Initialize jk2, unless already done in another server. Register URI mappping for Tomcat
+ * If multiple virtual servers use this module, calls to Ns_ModuleInit will be made serially
+ * in order of appearance of those servers in nsd.tcl
+ */
+int Ns_ModuleInit(char *server, char *module)
+{
+    jk_env_t *env;
+    
+    /* configuration-related */
+    char* serverName=NULL;
+    char* confPath;
+    static char cwdBuf[PATH_MAX];
+    static char* serverRoot = NULL;
+
+    /* APR-related */
+    apr_status_t aprrc;
+    char errbuf[512];
+    apr_pool_t *jk_globalPool = NULL;
+
+    /* URI registration */
+    char *hosts[2] = {"*", NULL};
+    jk_map_t *vhosts;
+    int i, j, k, l, cnt1, cnt2;
+    jk_map_t *uriMap, *webapps, *uriMaps[3];
+    jk_uriEnv_t *uriEnv, *hostEnv, *appEnv;
+
+
+    if (jkInitCount++ == 0) {
+
+        /* Get Tomcat installation root - this value is same for all virtual servers*/
+        if (serverRoot == NULL) {
+            confPath = Ns_ConfigGetPath (NULL, module, NULL);
+            serverRoot = (confPath? Ns_ConfigGetValue (confPath, "serverRoot") : NULL);
+        }
+
+        /* not configured in nsd.tcl? try env. variable */
+        if (serverRoot == NULL) {
+            serverRoot = getenv ("TOMCAT_HOME");
+        }
+
+        /* not in env. variables? get it from CWD */
+        if (serverRoot == NULL) {
+            serverRoot = getcwd (cwdBuf, sizeof(cwdBuf));
+        }
+
+	/* Initialize APR */
+	if ((aprrc=apr_initialize()) != APR_SUCCESS) {
+	    LOG_ERROR2 ("Cannot initialize APR", apr_strerror (aprrc, errbuf, sizeof(errbuf)));
+	    return NS_ERROR;
+	}
+
+	if ((aprrc=apr_pool_create(&jk_globalPool, NULL)) != APR_SUCCESS) {
+	    LOG_ERROR2 ("Cannot create global APR pool", apr_strerror (aprrc, errbuf, sizeof(errbuf)));
+	    return NS_ERROR;
+	}
+
+	/* Initialize JNI */
+	if (workerEnv==NULL && jk2_create_workerEnv(jk_globalPool, serverRoot)==JK_ERR) {
+	    return NS_ERROR;
+	}
+
+	env=workerEnv->globalEnv;
+	env->setAprPool(env, jk_globalPool);
+
+	/* Initialize JK2 */
+	if (workerEnv->init(env, workerEnv ) != JK_OK) {
+	    LOG_ERROR("Cannot initialize worker environment");
+	    return NS_ERROR;
+	}
+
+	workerEnv->server_name = apr_pstrcat (jk_globalPool, Ns_InfoServerName(), " ", Ns_InfoServerVersion (), NULL);
+	apr_pool_cleanup_register(jk_globalPool, NULL, jk2_shutdown, apr_pool_cleanup_null);
+
+	workerEnv->was_initialized = JK_TRUE; 
+    
+	if ((aprrc=apr_pool_userdata_set( "INITOK", "Ns_ModuleInit", NULL, jk_globalPool )) != APR_SUCCESS) {
+	    LOG_ERROR2 ("Cannot set APR pool user data", apr_strerror (aprrc, errbuf, sizeof(errbuf)));
+	    return NS_ERROR;
+	}
+
+	if (workerEnv->parentInit( env, workerEnv) != JK_OK) {
+	    LOG_ERROR ("Cannot initialize global environment");
+	    return NS_ERROR;
+	}
+
+	/* Obtain TLS slot - it's destructor will detach threads from JVM */
+	jvmGlobal = workerEnv->vm->jvm;
+	Ns_TlsAlloc (&jkTls, jkTlsDtor);
+
+	Ns_Log (Notice, "nsjk2: Initialized JK2 environment");
+
+    } else {
+      
+      env = workerEnv->globalEnv;
+    }
+
+    Ns_RegisterShutdown (jk2_shutdown_system, NULL);
+
+    /* Register URI patterns from workers2.properties with AOLserver 
+     *  
+     * Worker environment has a list of vhosts, including "*" vhost.
+     * Each vhost has a list of web applications (contexts) associated with it.
+     * Each webapp has a list of exact, prefix, suffix and regexp URI patterns.
+     *
+     * Will register URIs that are either in vhost "*", or one with name matching
+     * this AOLserver virtual server. Will ignore regexp patterns. Will register 
+     * exact webapp URIs (context root) as JK2 somehow doesn't include them in URI
+     * maps, even if specified in workers2.properties.
+     *
+     */
+
+    /* virtual server name override if specified */
+    confPath = Ns_ConfigGetPath (server, module, NULL);
+    if (confPath != NULL)
+        serverName = Ns_ConfigGetValue (confPath, "serverName");
+
+    if (serverName == NULL)
+        serverName = server;
+    
+    vhosts=workerEnv->uriMap->vhosts;
+    hosts[1]= serverName;
+
+    for (i=0; i<sizeof(hosts)/sizeof(*hosts); i++) {
+        hostEnv=vhosts->get (env, vhosts, hosts[i]);
+
+	if (hostEnv==NULL || hostEnv->webapps==NULL)
+	    continue;
+
+	webapps=hostEnv->webapps;
+	cnt1=webapps->size(env, webapps);
+
+	for (j=0; j<cnt1; j++) {
+	    appEnv = webapps->valueAt (env, webapps, j);
+	    if (appEnv == NULL)
+	        continue;
+
+	    /* register webapp root - registerURI checks if it is "/" */
+	    registerURI (env, appEnv, server, serverName);
+	    
+	    uriMaps[0] = appEnv->exactMatch;
+	    uriMaps[1] = appEnv->prefixMatch;
+	    uriMaps[2] = appEnv->suffixMatch;
+
+	    for (k=0; k<sizeof(uriMaps)/sizeof(*uriMaps); k++) {
+	        if (uriMaps[k] == NULL)
+		    continue;
+
+	        cnt2 = uriMaps[k]->size (env, uriMaps[k]);
+		
+		for (l=0; l<cnt2; l++) {
+		     registerURI (env, uriMaps[k]->valueAt (env, uriMaps[k], l), server, serverName);
+		}
+	    }
+	}
+    }
+
+    Ns_Log (Notice, "nsjk2: Initialized on %s", server);
+
+    return NS_OK;
+}
+
+
+
+#ifdef WIN32
+
+BOOL WINAPI DllMain(HINSTANCE hInst,        // Instance Handle of the DLL
+                    ULONG ulReason,         // Reason why NT called this DLL
+                    LPVOID lpReserved)      // Reserved parameter for future use
+{
+    GetModuleFileName( hInst, file_name, sizeof(file_name));
+    return TRUE;
+}
+
+
+#endif
diff --git a/connectors/jk/native2/server/aolserver/nsjk2.dsp b/connectors/jk/native2/server/aolserver/nsjk2.dsp
new file mode 100644
index 0000000..dbb8a1c
--- /dev/null
+++ b/connectors/jk/native2/server/aolserver/nsjk2.dsp
@@ -0,0 +1,359 @@
+# Microsoft Developer Studio Project File - Name="nsjk2" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=nsjk2 - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "nsjk2.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "nsjk2.mak" CFG="nsjk2 - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "nsjk2 - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "nsjk2 - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "nsjk2 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NSJK2_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\include" /I "$(TCL_HOME)\include" /I "$(AOL_HOME)\include" /I "$(JAVA_HOME)\include" /I "$(APACHE2_HOME)\include" /D "NDEBUG" /D "NSJK2_EXPORTS" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D FD_SETSIZE=128 /D TCL_THREADS=1 /D NO_CONST=1 /D "HAVE_JNI" /D "HAS_APR" /D "HAS_PCRE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x41a /d "NDEBUG"
+# ADD RSC /l 0x41a /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 nsd.lib nsthread.lib libapr.lib libaprutil.lib wsock32.lib advapi32.lib pcre.lib pcreposix.lib /nologo /dll /machine:I386 /libpath:"$(TCL_HOME)\lib" /libpath:"$(AOL_HOME)\lib" /libpath:"$(APACHE2_HOME)\lib"
+
+!ELSEIF  "$(CFG)" == "nsjk2 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "NSJK2_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\include" /I "$(TCL_HOME)\include" /I "$(AOL_HOME)\include" /I "$(JAVA_HOME)\include" /I "$(APACHE2_HOME)\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D FD_SETSIZE=128 /D TCL_THREADS=1 /D NO_CONST=1 /D "HAVE_JNI" /D "HAS_APR" /D "HAS_PCRE" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x41a /d "_DEBUG"
+# ADD RSC /l 0x41a /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 nsd.lib nsthread.lib libapr.lib libaprutil.lib wsock32.lib advapi32.lib pcre.lib pcreposix.lib /nologo /dll /debug /machine:I386 /pdbtype:sept /libpath:"$(TCL_HOME)\lib" /libpath:"$(AOL_HOME)\lib" /libpath:"$(APACHE2_HOME)\lib"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "nsjk2 - Win32 Release"
+# Name "nsjk2 - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\jk_logger_ns.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_service_ns.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jni_nslog.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jni_nstcl.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\nsjk2.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\..\include\jk_bean.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_channel.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_endpoint.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_env.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_global.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_handler.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_msg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_mutex.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_ns.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_objCache.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_requtil.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_service.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_shm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_uriEnv.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_uriMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_vm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_workerEnv.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\jni_nstcl.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# Begin Group "Common Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel_apr_socket.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel_jni.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel_un.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_config.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_config_file.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_endpoint.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_env.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_handler_logon.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_handler_response.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_file.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_win32.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_msg_ajp.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_mutex.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_mutex_proc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_mutex_thread.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_objCache.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_pool_apr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_registry.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_requtil.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_shm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_signal.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_uriEnv.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_uriMap.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_user.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_vm_default.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_ajp13.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_jni.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_lb.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_run.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_status.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_workerEnv.c
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/connectors/jk/native2/server/apache13/Makefile.apxs.in b/connectors/jk/native2/server/apache13/Makefile.apxs.in
new file mode 100755
index 0000000..6a0eaf4
--- /dev/null
+++ b/connectors/jk/native2/server/apache13/Makefile.apxs.in
@@ -0,0 +1,37 @@
+## configure should make the Makefile out of this file.
+prefix=@prefix@
+exec_prefix=@prefix@
+
+APXS=@APXS@
+OS=@OS@
+JK_DIR := ../..
+APR_CFLAGS=@APR_CFLAGS@
+JAVA_HOME=@JAVA_HOME@
+APR_LIBS=`@APR_DIR@/bin/apr-config --link-ld`
+APR_UTIL_LIBS=`@APR_UTIL_DIR@/bin/apu-config --link-ld`
+
+ifneq ($(strip $(JAVA_HOME)),)
+JAVA_INCL=-I ${JAVA_HOME}/include -I ${JAVA_HOME}/include/${OS}
+JAVA_LIB=-L ${JAVA_HOME}/jre/lib/${ARCH} -L ${JAVA_HOME}/lib/${ARCH}/native_threads
+endif
+
+INCLUDES= -I${JK_DIR}/include \
+	  ${JAVA_INCL}
+
+JK_CFLAGS=-DCHUNK_SIZE=4096 -DUSE_APACHE_MD5 ${APR_CFLAGS} -DHAVE_MMAP @HAVE_JNI@ @HAS_PCRE@
+
+COMMON_C_FILES := $(wildcard ${JK_DIR}/common/*.c )
+JNI_C_FILES := $(wildcard ${JK_DIR}/jni/*.c )
+C_FILES=jk_service_apache13.c mod_jk2.c
+
+all: mod_jk2.so
+
+mod_jk2.so: 
+	$(APXS) -c -o $@ ${INCLUDES} ${JK_CFLAGS} ${C_FILES} ${COMMON_C_FILES} ${JNI_C_FILES} \
+		${APR_LIBS} ${APR_UTIL_LIBS} ${JAVA_LIB} @PCRE_LIBS@
+
+install: mod_jk2.so
+	$(APXS) -i mod_jk2.so
+
+clean:
+	rm -f *.o *.so
diff --git a/connectors/jk/native2/server/apache13/Makefile.in b/connectors/jk/native2/server/apache13/Makefile.in
new file mode 100644
index 0000000..0cc8709
--- /dev/null
+++ b/connectors/jk/native2/server/apache13/Makefile.in
@@ -0,0 +1,121 @@
+# Gnu makefile and libtool are required
+# use -D options to overrides defaults
+CC=@CC@
+CP=@CP@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+APACHE_HOME=@APACHE_HOME@
+OS=@OS@
+APACHE_INCL=@APACHE_INCL@
+APACHE_LIBEXEC=@APACHE_LIBEXEC@
+APACHE_LIBDIR=${APACHE_HOME}/lib
+EXTRA_CFLAGS=@APXS_CFLAGS@
+EXTRA_CPPFLAGS=@APXS_CPPFLAGS@
+JAVA_HOME=@JAVA_HOME@
+APR_LDFLAGS=@APR_LDFLAGS@ `@APR_DIR@/apr-config --ldflags --libs` `@APR_UTIL_DIR@/apu-config --ldflags --libs`
+
+ifneq ($(strip $(JAVA_HOME)),)
+JAVA_INCL=-I ${JAVA_HOME}/include -I ${JAVA_HOME}/include/${OS}
+JAVA_LIB=-L ${JAVA_HOME}/jre/lib/${ARCH} -L ${JAVA_HOME}/lib/${ARCH}/native_threads
+endif
+
+JK_DIR := ../..
+BUILD_DIR = ${JK_DIR}/../build/jk2/apache13
+
+top_builddir=../..
+LIBTOOL=@LIBTOOL@
+
+# It doesn't hurt if we include all
+INCLUDES= -I${JK_DIR}/include \
+          ${APACHE_INCL} \
+	  ${JAVA_INCL}
+
+JK_CFLAGS=-DCHUNK_SIZE=4096 -DUSE_APACHE_MD5 @APR_CFLAGS@ -DHAVE_MMAP @HAVE_JNI@ @HAS_PCRE@
+JK_LDFLAGS=${APR_LDFLAGS} ${JAVA_LIB} @PCRE_LIBS@
+
+###### Based on rules.mk ##########################################
+ALL_CFLAGS   = $(EXTRA_CFLAGS) $(NOTEST_CFLAGS) $(CFLAGS)
+ALL_CPPFLAGS = $(DEFS) $(EXTRA_CPPFLAGS) $(NOTEST_CPPFLAGS) $(CPPFLAGS)
+ALL_LDFLAGS  = $(EXTRA_LDFLAGS) $(NOTEST_LDFLAGS) $(LDFLAGS)
+ALL_LIBS     = $(EXTRA_LIBS) $(NOTEST_LIBS) $(LIBS)
+ALL_INCLUDES = $(INCLUDES) $(EXTRA_INCLUDES)
+
+# Compile commands
+COMPILE      = $(CC)  $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(ALL_INCLUDES)
+
+SH_COMPILE = $(LIBTOOL) --mode=compile $(COMPILE) $(JK_CFLAGS)
+MOD_LINK = $(LIBTOOL) --mode=link $(CC) -avoid-version -module -rpath $(APACHE_LIBEXEC) $(LT_LDFLAGS) $(ALL_LDFLAGS) $(JK_LDFLAGS)
+MOD_INSTALL = $(LIBTOOL) --mode=install $(CP)
+
+#############################################################################
+
+# ---------- File list creation -------------------- 
+# Same behavior as ant - 'all files from a dir'. 
+# Excludes are not yet implemented.
+
+COMMON_C_FILES := $(wildcard ${JK_DIR}/common/*.c )
+JNI_C_FILES := $(wildcard ${JK_DIR}/jni/*.c )
+A_C_FILES := $(wildcard ${JK_DIR}/server/apache13/*.c )
+H_FILES := $(wildcard ${JK_DIR}/include/*.h )
+
+COMMON_LO_FILES := $(patsubst ${JK_DIR}/common/%, ${BUILD_DIR}/%, \
+			 $(patsubst %c, %lo, ${COMMON_C_FILES} ))
+JNI_LO_FILES := $(patsubst ${JK_DIR}/jni/%, ${BUILD_DIR}/%, \
+			 $(patsubst %c, %lo, ${JNI_C_FILES} ))
+A_LO_FILES := $(patsubst ${JK_DIR}/server/apache13/%, ${BUILD_DIR}/%, \
+			 $(patsubst %c, %lo, ${A_C_FILES} ))
+
+
+# ---------- Compile rules --------------------
+
+.PHONY: all
+
+
+VPATH=.:../../common
+
+.c.lo:
+	 ${SH_COMPILE} -c $< -o $>
+
+${BUILD_DIR}/%.lo: ${JK_DIR}/common/%.c
+	 ${SH_COMPILE} -c $< -o $@
+
+${BUILD_DIR}/%.lo: ${JK_DIR}/jni/%.c
+	 ${SH_COMPILE} -c $< -o $@
+
+${BUILD_DIR}/%.lo: ${JK_DIR}/server/apache13/%.c
+	 ${SH_COMPILE} -c $< -o $@
+
+
+# ---------- Targets -------------------- 
+
+all: prepare ${BUILD_DIR}/mod_jk2.so @JNI_BUILD@
+
+jni-build: ${BUILD_DIR}/libjkjni.so
+
+${BUILD_DIR}/mod_jk2.so: ${BUILD_DIR}/$(APACHE_LIBEXEC)/mod_jk2.so
+	$(CP) $^ $@
+
+${BUILD_DIR}/$(APACHE_LIBEXEC)/mod_jk2.so: ${BUILD_DIR}/mod_jk2.la
+	$(MOD_INSTALL) $^ `pwd`/${BUILD_DIR}/$(APACHE_LIBEXEC)
+
+${BUILD_DIR}/libjkjni.so: ${BUILD_DIR}/${APACHE_LIBEXEC}/libjkjni.so
+	$(CP) $^ $@
+
+${BUILD_DIR}/${APACHE_LIBEXEC}/libjkjni.so: ${BUILD_DIR}/libjkjni.la
+	$(MOD_INSTALL) $^ `pwd`/${BUILD_DIR}/${APACHE_LIBEXEC}
+
+${BUILD_DIR}/libjkjni.la: ${JNI_LO_FILES} ${COMMON_LO_FILES}
+	$(MOD_LINK) -o $@ $(JK_LDFLAGS) $^
+
+${BUILD_DIR}/mod_jk2.la: ${COMMON_LO_FILES} ${JNI_LO_FILES} ${A_LO_FILES}
+	${MOD_LINK} -o $@ $^ ${APR_LDFLAGS}
+
+${COMMON_C_FILES} ${A_C_FILES}: ${H_FILES}
+
+prepare: 
+	mkdir -p ${BUILD_DIR}${APACHE_LIBEXEC}
+
+clean: 
+	rm -rf ${BUILD_DIR}/*.lo ${BUILD_DIR}/*.la ${BUILD_DIR}/*.o ${BUILD_DIR}/.libs \
+		${BUILD_DIR}/*.so ${BUILD_DIR}${APACHE_LIBEXEC}/*.so
diff --git a/connectors/jk/native2/server/apache13/jk_service_apache13.c b/connectors/jk/native2/server/apache13/jk_service_apache13.c
new file mode 100644
index 0000000..b9e4bc8
--- /dev/null
+++ b/connectors/jk/native2/server/apache13/jk_service_apache13.c
@@ -0,0 +1,472 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Description: Apache1.3 adapter 
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "http_log.h"
+
+#include "util_script.h"
+#include "util_date.h"
+/*
+ * Jakarta (jk_) include files
+ */
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_env.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+#include "jk_workerEnv.h"
+#include "jk_uriMap.h"
+#include "jk_requtil.h"
+
+
+#define NULL_FOR_EMPTY(x)   ((x && !strlen(x)) ? NULL : x)
+
+static int JK_METHOD jk2_service_apache13_head(jk_env_t *env,
+                                               jk_ws_service_t *s)
+{
+    int h;
+    int numheaders;
+    request_rec *r;
+    jk_map_t *headers;
+
+    if (s == NULL || s->ws_private == NULL)
+        return JK_ERR;
+
+    r = (request_rec *) s->ws_private;
+
+    if (s->msg == NULL) {
+        s->msg = "";
+    }
+    r->status = s->status;
+    r->status_line = ap_psprintf(r->pool, "%d %s", s->status, s->msg);
+
+    headers = s->headers_out;
+    numheaders = headers->size(env, headers);
+    /* XXX As soon as we switch to jk_map_apache13, this will not be needed ! */
+    if (s->uriEnv->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "service.head() %d %d\n", s->status, numheaders);
+
+    for (h = 0; h < numheaders; h++) {
+        char *name = headers->nameAt(env, headers, h);
+        char *val = headers->valueAt(env, headers, h);
+
+        if (s->uriEnv->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "service.head() %s: %s %d %d\n",
+                          name, val, h, headers->size(env, headers));
+
+        /* the cmp can also be avoided in we do this earlier and use
+           the header id */
+        if (strcasecmp(name, "Content-type") == 0) {
+            /* XXX should be done in handler ! */
+            char *tmp = ap_pstrdup(r->pool, val);
+            ap_content_type_tolower(tmp);
+            r->content_type = tmp;
+            ap_table_set(r->headers_out, name, val);
+        }
+        else if (strcasecmp(name, "Location") == 0) {
+            /* XXX setn */
+            ap_table_set(r->headers_out, name, val);
+        }
+        else if (strcasecmp(name, "Content-Length") == 0) {
+            ap_table_set(r->headers_out, name, val);
+        }
+        else if (strcasecmp(name, "Transfer-Encoding") == 0) {
+            ap_table_set(r->headers_out, name, val);
+        }
+        else if (strcasecmp(name, "Last-Modified") == 0) {
+            /*
+             * If the script gave us a Last-Modified header, we can't just
+             * pass it on blindly because of restrictions on future values.
+             */
+            ap_update_mtime(r, ap_parseHTTPdate(val));
+            ap_set_last_modified(r);
+            ap_table_set(r->headers_out, name, val);
+        }
+        else {
+            ap_table_add(r->headers_out, name, val);
+            /* ap_table_set(r->headers_out, name, val); */
+        }
+    }
+
+    ap_send_http_header(r);
+    s->response_started = JK_TRUE;
+
+    return JK_OK;
+}
+
+/*
+ * Read a chunk of the request body into a buffer.  Attempt to read len
+ * bytes into the buffer.  Write the number of bytes actually read into
+ * actually_read.
+ *
+ * Think of this function as a method of the apache1.3-specific subclass of
+ * the jk_ws_service class.  Think of the *s param as a "this" or "self"
+ * pointer.
+ */
+static int JK_METHOD jk2_service_apache13_read(jk_env_t *env,
+                                               jk_ws_service_t *s, void *b,
+                                               unsigned len,
+                                               unsigned *actually_read)
+{
+    long rv;
+
+    if (s == NULL || s->ws_private == NULL || b == NULL
+        || actually_read == NULL) {
+        return JK_ERR;
+    }
+
+    if (!s->read_body_started) {
+        if (ap_should_client_block(s->ws_private)) {
+            s->read_body_started = JK_TRUE;
+        }
+    }
+    rv = ap_get_client_block(s->ws_private, b, len);
+
+    if (rv < 0) {
+        *actually_read = 0;
+    }
+    else {
+        *actually_read = (unsigned)rv;
+    }
+    return JK_OK;
+}
+
+/*
+ * Write a chunk of response data back to the browser.  If the headers
+ * haven't yet been sent over, send over default header values (Status =
+ * 200, basically).
+ *
+ * Write len bytes from buffer b.
+ *
+ * Think of this function as a method of the apache1.3-specific subclass of
+ * the jk_ws_service class.  Think of the *s param as a "this" or "self"
+ * pointer.
+ */
+
+/* Works with 4096, fails with 8192 */
+#ifndef CHUNK_SIZE
+#define CHUNK_SIZE 4096
+#endif
+
+static int JK_METHOD jk2_service_apache13_write(jk_env_t *env,
+                                                jk_ws_service_t *s,
+                                                const void *b,
+                                                unsigned int len)
+{
+    int rc;
+
+    if (s == NULL || s->ws_private == NULL || b == NULL)
+        return JK_ERR;
+
+    {
+        size_t rd = 0;
+        long ll = len;
+        char *bb = (char *)b;
+        request_rec *rr = s->ws_private;
+        BUFF *bf = rr->connection->client;
+
+        if (!s->response_started) {
+            if (s->uriEnv->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_INFO,
+                              "service.write() default head\n");
+
+            rc = s->head(env, s);
+            if (rc != JK_OK) {
+                return rc;
+            }
+        }
+        if (rr->header_only) {
+            ap_bflush(bf);
+            return JK_OK;
+        }
+
+        /* Debug - try to get around rwrite */
+        while (ll > 0) {
+            unsigned long toSend = (ll > CHUNK_SIZE) ? CHUNK_SIZE : ll;
+            rd = ap_rwrite((const char *)bb, toSend, rr);
+            if (s->uriEnv->mbean->debug > 0)
+                env->l->jkLog(env, env->l, JK_LOG_INFO,
+                              "service.write()  %ld (%ld) out of %ld \n",
+                              toSend, rd, ll);
+            ll -= CHUNK_SIZE;
+            bb += CHUNK_SIZE;
+
+            if (toSend != rd) {
+                return JK_ERR;
+            }
+
+        }
+
+        /*
+         * To allow server push. After writing full buffers
+         */
+        ap_bflush(bf);
+    }
+    return JK_OK;
+}
+
+/* ========================================================================= */
+/* Utility functions                                                         */
+/* ========================================================================= */
+
+static int jk2_get_content_length(jk_env_t *env, request_rec * r)
+{
+    if (r->clength > 0) {
+        return r->clength;
+    }
+    else {
+        char *lenp = (char *)ap_table_get(r->headers_in, "Content-Length");
+
+        if (lenp) {
+            int rc = atoi(lenp);
+            if (rc > 0) {
+                return rc;
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int JK_METHOD jk2_init_ws_service(jk_env_t *env, jk_ws_service_t *s,
+                                         jk_worker_t *worker, void *serverObj)
+{
+    char *ssl_temp = NULL;
+    jk_workerEnv_t *workerEnv = worker->workerEnv;
+    request_rec *r = serverObj;
+    int need_content_length_header = JK_FALSE;
+
+    /* Common initialization */
+    /* XXX Probably not needed, we're duplicating */
+    jk2_requtil_initRequest(env, s);
+
+    s->ws_private = r;
+    s->response_started = JK_FALSE;
+    s->read_body_started = JK_FALSE;
+    s->workerEnv = workerEnv;
+
+    workerEnv->childId = r->connection->child_num;
+
+    s->jvm_route = NULL;        /* Used for sticky session routing */
+
+    s->auth_type = NULL_FOR_EMPTY(r->connection->ap_auth_type);
+    s->remote_user = NULL_FOR_EMPTY(r->connection->user);
+
+    s->protocol = r->protocol;
+    s->remote_host = (char *)ap_get_remote_host(r->connection,
+                                                r->per_dir_config,
+                                                REMOTE_HOST);
+    s->remote_host = NULL_FOR_EMPTY(s->remote_host);
+    s->remote_addr = NULL_FOR_EMPTY(r->connection->remote_ip);
+
+    /* get server name */
+    s->server_name = (char *)(r->hostname ? r->hostname :
+                              r->server->server_hostname);
+    s->server_port = htons(r->connection->local_addr.sin_port);
+    s->server_software = (char *)ap_get_server_version();
+
+    s->method = (char *)r->method;
+    s->content_length = jk2_get_content_length(env, r);
+    s->is_chunked = r->read_chunked;
+    s->no_more_chunks = 0;
+    s->query_string = r->args;
+
+    /*
+     * The 2.2 servlet spec errata says the uri from
+     * HttpServletRequest.getRequestURI() should remain encoded.
+     * [http://java.sun.com/products/servlet/errata_042700.html]
+     *
+     * We use JkOptions to determine which method to be used
+     *
+     * ap_escape_uri is the latest recommanded but require
+     *               some java decoding (in TC 3.3 rc2)
+     *
+     * unparsed_uri is used for strict compliance with spec and
+     *              old Tomcat (3.2.3 for example)
+     *
+     * uri is use for compatibilty with mod_rewrite with old Tomcats
+     */
+
+    switch (workerEnv->options & JK_OPT_FWDURIMASK) {
+
+    case JK_OPT_FWDURICOMPATUNPARSED:
+        s->req_uri = r->unparsed_uri;
+        if (s->req_uri != NULL) {
+            char *query_str = strchr(s->req_uri, '?');
+            if (query_str != NULL) {
+                *query_str = 0;
+            }
+        }
+
+        break;
+
+    case JK_OPT_FWDURICOMPAT:
+        s->req_uri = r->uri;
+        break;
+
+    case JK_OPT_FWDURIESCAPED:
+        s->req_uri = ap_escape_uri(r->pool, r->uri);
+        break;
+
+    default:
+        return JK_ERR;
+    }
+
+    s->is_ssl = JK_FALSE;
+    s->ssl_cert = NULL;
+    s->ssl_cert_len = 0;
+    s->ssl_cipher = NULL;       /* required by Servlet 2.3 Api, 
+                                   allready in original ajp13 */
+    s->ssl_session = NULL;
+    s->ssl_key_size = -1;       /* required by Servlet 2.3 Api, added in jtc */
+
+    if (workerEnv->ssl_enable || workerEnv->envvars_in_use) {
+        ap_add_common_vars(r);
+
+        if (workerEnv->ssl_enable) {
+            ssl_temp =
+                (char *)ap_table_get(r->subprocess_env,
+                                     workerEnv->https_indicator);
+            if (ssl_temp && !strcasecmp(ssl_temp, "on")) {
+                s->is_ssl = JK_TRUE;
+                s->ssl_cert =
+                    (char *)ap_table_get(r->subprocess_env,
+                                         workerEnv->certs_indicator);
+                if (s->ssl_cert) {
+                    s->ssl_cert_len = strlen(s->ssl_cert);
+                }
+                /* Servlet 2.3 API */
+                s->ssl_cipher =
+                    (char *)ap_table_get(r->subprocess_env,
+                                         workerEnv->cipher_indicator);
+                s->ssl_session =
+                    (char *)ap_table_get(r->subprocess_env,
+                                         workerEnv->session_indicator);
+
+                if (workerEnv->options & JK_OPT_FWDKEYSIZE) {
+                    /* Servlet 2.3 API */
+                    ssl_temp = (char *)ap_table_get(r->subprocess_env,
+                                                    workerEnv->
+                                                    key_size_indicator);
+                    if (ssl_temp)
+                        s->ssl_key_size = atoi(ssl_temp);
+                }
+            }
+        }
+
+        jk2_map_default_create(env, &s->attributes, s->pool);
+
+        if (workerEnv->envvars_in_use) {
+            int envCnt = workerEnv->envvars->size(env, workerEnv->envvars);
+            int i;
+
+            for (i = 0; i < envCnt; i++) {
+                char *name =
+                    workerEnv->envvars->nameAt(env, workerEnv->envvars, i);
+                char *val = (char *)ap_table_get(r->subprocess_env, name);
+                if (val == NULL) {
+                    val =
+                        workerEnv->envvars->valueAt(env, workerEnv->envvars,
+                                                    i);
+                }
+                s->attributes->put(env, s->attributes, name, val, NULL);
+            }
+        }
+    }
+
+    jk2_map_default_create(env, &s->headers_in, s->pool);
+
+    if (r->headers_in && ap_table_elts(r->headers_in)) {
+        const array_header *t = ap_table_elts(r->headers_in);
+        if (t && t->nelts) {
+            int i;
+
+            table_entry *elts = (table_entry *) t->elts;
+
+            for (i = 0; i < t->nelts; i++) {
+                s->headers_in->add(env, s->headers_in,
+                                   elts[i].key, elts[i].val);
+            }
+        }
+    }
+
+    if (!s->is_chunked && s->content_length == 0) {
+        /* XXX if r->contentLength == 0 I assume there's no header
+           or a header with '0'. In the second case, put will override it 
+         */
+        s->headers_in->put(env, s->headers_in, "content-length", "0", NULL);
+    }
+
+    jk2_map_default_create(env, &s->headers_out, s->pool);
+
+    return JK_OK;
+}
+
+/*
+ * If the servlet engine didn't consume all of the
+ * request data, consume and discard all further
+ * characters left to read from client
+ *
+ *  XXX Is it the right thing to do ????? Why spend the
+ *  bandwith, the servlet decided not to read the POST then
+ *  jk shouldn't do it instead, and the user should get the
+ *  error message !
+ */
+static void JK_METHOD jk2_service_apache13_afterRequest(jk_env_t *env,
+                                                        jk_ws_service_t *s)
+{
+
+    if (s->content_read < s->content_length ||
+        (s->is_chunked && !s->no_more_chunks)) {
+
+        request_rec *r = s->ws_private;
+
+        char *buff = ap_palloc(r->pool, 2048);
+        if (buff != NULL) {
+            int rd;
+            while ((rd = ap_get_client_block(r, buff, 2048)) > 0) {
+                s->content_read += rd;
+            }
+        }
+    }
+}
+
+int jk2_service_apache13_init(jk_env_t *env, jk_ws_service_t *s)
+{
+    if (s == NULL) {
+        return JK_ERR;
+    }
+
+    s->head = jk2_service_apache13_head;
+    s->read = jk2_service_apache13_read;
+    s->write = jk2_service_apache13_write;
+    s->init = jk2_init_ws_service;
+    s->afterRequest = jk2_service_apache13_afterRequest;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/server/apache13/mod_jk2.c b/connectors/jk/native2/server/apache13/mod_jk2.c
new file mode 100644
index 0000000..79d2181
--- /dev/null
+++ b/connectors/jk/native2/server/apache13/mod_jk2.c
@@ -0,0 +1,698 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Apache 1.3 plugin for Jakarta/Tomcat                       *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ *              Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+/*
+ * mod_jk: keeps all servlet/jakarta related ramblings together.
+ */
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "util_script.h"
+#include "http_conf_globals.h"
+
+/*
+ * Jakarta (jk_) include files
+ */
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_env.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+#include "jk_workerEnv.h"
+#include "jk_uriMap.h"
+#include "jk_requtil.h"
+
+#ifdef WIN32
+static char file_name[_MAX_PATH];
+#endif
+
+
+#define JK_HANDLER          ("jakarta-servlet2")
+#define JK_MAGIC_TYPE       ("application/x-jakarta-servlet2")
+
+module MODULE_VAR_EXPORT jk2_module;
+
+int jk2_service_apache13_init(jk_env_t *env, jk_ws_service_t *s);
+
+
+/* In apache1.3 this is reset when the module is reloaded ( after
+ * config. No good way to discover if it's the first time or not.
+ */
+static jk_workerEnv_t *workerEnv;
+/* This is used to ensure that jk2_create_dir_config creates unique 
+ * dir mappings. This prevents vhost configs as configured through
+ * httpd.conf from getting crossed.
+ */
+static int dirCounter = 0;
+
+/* ==================== Options setters ==================== */
+
+/*
+ * JkSet name value
+ *
+ * Set jk options. Same as using workers.properties or a
+ * config application.
+ *
+ * Known properties: see workers.properties documentation
+ *
+ * XXX Shouldn't abuse it, there is no way to write back
+ * the properties.
+ */
+static const char *jk2_set2(cmd_parms * cmd, void *per_dir,
+                            const char *name, char *value)
+{
+    server_rec *s = cmd->server;
+    jk_uriEnv_t *serverEnv = (jk_uriEnv_t *)
+        ap_get_module_config(s->module_config, &jk2_module);
+    jk_env_t *env = workerEnv->globalEnv;
+    int rc;
+
+    rc = workerEnv->config->setPropertyString(env, workerEnv->config,
+                                              (char *)name, value);
+    if (rc != JK_OK) {
+        fprintf(stderr, "mod_jk2: Unrecognized option %s %s\n", name, value);
+    }
+
+    return NULL;
+}
+
+
+/**
+ * Set a property associated with a URI, using native <Location> 
+ * directives.
+ *
+ * This is used if you want to use the native mapping and
+ * integrate better into apache.
+ *
+ * Same behavior can be achieved by using uri.properties and/or JkSet.
+ * 
+ * Example:
+ *   <VirtualHost foo.com>
+ *      <Location /examples>
+ *         JkUriSet worker ajp13
+ *      </Location>
+ *   </VirtualHost>
+ *
+ * This is the best way to define a webapplication in apache. It is
+ * scalable ( using apache native optimizations, you can have hundreds
+ * of hosts and thousands of webapplications ), 'natural' to any
+ * apache user.
+ *
+ * XXX This is a special configuration, for most users just use
+ * the properties files.
+ */
+static const char *jk2_uriSet(cmd_parms * cmd, void *per_dir,
+                              const char *name, const char *val)
+{
+    jk_uriEnv_t *uriEnv = (jk_uriEnv_t *)per_dir;
+
+    char *tmp_virtual = NULL;
+    char *tmp_full_url = NULL;
+    server_rec *s = cmd->server;
+
+    uriEnv->mbean->setAttribute(workerEnv->globalEnv, uriEnv->mbean,
+                                (char *)name, (void *)val);
+
+    /*
+     * all of the objects that get passed in now are unique. create_dir adds a incrementing counter to the
+     * uri that is used to create the object!
+     * Here we must now 'fix' the content of the object passed in.
+     * Apache doesn't care what we do here as it has the reference to the unique object that has been
+     * created. What we need to do is ensure that the data given to mod_jk2 is correct. Hopefully in the long run
+     * we can ignore some of the mod_jk details...
+     */
+
+    /* if applicable we will set the hostname etc variables. */
+    if (s->is_virtual && s->server_hostname != NULL &&
+        (uriEnv->virtual == NULL || !strchr(uriEnv->virtual, ':') ||
+         uriEnv->port != s->port)) {
+        tmp_virtual = (char *)ap_pcalloc(cmd->pool,
+                                         sizeof(char *) *
+                                         (strlen(s->server_hostname) + 8));
+        tmp_full_url =
+            (char *)ap_pcalloc(cmd->pool,
+                               sizeof(char *) * (strlen(s->server_hostname) +
+                                                 strlen(uriEnv->uri) + 8));
+        /* do not pass the hostname:0/ scheme */
+        if (s->port) {
+            sprintf(tmp_virtual, "%s:%d", s->server_hostname, s->port);
+            sprintf(tmp_full_url, "%s:%d%s", s->server_hostname, s->port,
+                    uriEnv->uri);
+        }
+        else {
+            strcpy(tmp_virtual, s->server_hostname);
+            strcpy(tmp_full_url, s->server_hostname);
+            strcat(tmp_full_url, uriEnv->uri);
+        }
+        uriEnv->mbean->setAttribute(workerEnv->globalEnv, uriEnv->mbean,
+                                    "uri", tmp_full_url);
+        uriEnv->mbean->setAttribute(workerEnv->globalEnv, uriEnv->mbean,
+                                    "path", cmd->path);
+        uriEnv->name = tmp_virtual;
+        uriEnv->virtual = tmp_virtual;
+
+    }
+    /* now lets actually add the parameter set in the <Location> block */
+    uriEnv->mbean->setAttribute(workerEnv->globalEnv, uriEnv->mbean,
+                                (char *)name, (void *)val);
+
+    return NULL;
+}
+
+
+static void *jk2_create_dir_config(pool * p, char *path)
+{
+    /* We don't know the vhost yet - so path is not
+     * unique. We'll have to generate a unique name
+     */
+    char *tmp = NULL;
+    int a = 0;
+    jk_uriEnv_t *newUri;
+    jk_bean_t *jkb;
+
+    if (!path)
+        return NULL;
+
+    a = strlen(path) + 10;
+    tmp = (char *)ap_pcalloc(p, sizeof(char *) * (a));
+    sprintf(tmp, "%s-%d", path == NULL ? "" : path, dirCounter++);
+
+    /* the greatest annoyance here is that we can't create the uri correctly with the hostname as well.
+       as apache doesn't give us the hostname .
+       we'll fix this in JkUriSet
+     */
+
+    jkb = workerEnv->globalEnv->createBean2(workerEnv->globalEnv,
+                                            workerEnv->pool, "uri", tmp);
+    newUri = jkb->object;
+    newUri->workerEnv = workerEnv;
+    newUri->mbean->setAttribute(workerEnv->globalEnv, newUri->mbean, "path",
+                                tmp);
+    /* I'm hoping that setting the id won't break anything. I havn't noticed it breaking anything. */
+    newUri->mbean->id = (dirCounter - 1);
+    /* this makes the display in the status display make more sense  */
+    newUri->mbean->localName = path;
+
+    return newUri;
+}
+
+/*
+ * Need to re-do this to make more sense - like properly creating a new config and returning the merged config...
+ * Looks like parent needs to be dominant.
+ */
+static void *jk2_merge_dir_config(pool * p, void *childv, void *parentv)
+{
+    jk_uriEnv_t *child = (jk_uriEnv_t *)childv;
+    jk_uriEnv_t *parent = (jk_uriEnv_t *)parentv;
+    jk_uriEnv_t *winner = NULL;
+
+
+/*        fprintf(stderr,"Merging child & parent. (dir)\n");
+   fprintf(stderr, "Merging for  vhost child(%s) vhost parent(%s) uri child(%s) uri parent(%s) child worker (%s) parentworker(%s)\n",
+              (child->virtual==NULL)?"":child->virtual,
+         (parent->virtual==NULL)?"":parent->virtual,
+         (child->uri==NULL)?"":child->uri,
+              (parent->uri==NULL)?"":parent->uri,
+         (child->workerName==NULL)?"":child->workerName,
+         (parent->workerName==NULL)?"":parent->workerName
+         ); */
+
+
+    if (child == NULL || child->uri == NULL || child->workerName == NULL)
+        winner = parent;
+    else if (parent == NULL || parent->uri == NULL
+             || parent->workerName == NULL)
+        winner = child;
+    /* interresting bit... so far they are equal ... */
+    else if (strlen(parent->uri) > strlen(child->uri))
+        winner = parent;
+    else
+        winner = child;
+
+/*     if ( winner == child )
+   fprintf(stderr, "Going with the child\n");  
+     else if ( winner == parent )
+   fprintf(stderr, "Going with the parent\n"); 
+     else
+   fprintf(stderr, "Going with NULL\n");    
+*/
+
+    return (void *)winner;
+
+}
+
+
+
+apr_pool_t *jk_globalPool;
+
+/* Create the initial set of objects. You need to cut&paste this and
+   adapt to your server.
+ */
+static int jk2_create_workerEnv(ap_pool * p, const server_rec * s)
+{
+    jk_env_t *env;
+    jk_pool_t *globalPool;
+    jk_bean_t *jkb;
+
+    apr_initialize();
+    apr_pool_create(&jk_globalPool, NULL);
+
+    jk2_pool_apr_create(NULL, &globalPool, NULL, jk_globalPool);
+
+    /** Create the global environment. This will register the default
+        factories, to be overriten later.
+    */
+    env = jk2_env_getEnv(NULL, globalPool);
+
+    /* Optional. Register more factories ( or replace existing ones )
+       Insert your server-specific objects here.
+     */
+
+    /* Create the logger . We use the default jk logger, will output
+       to a file. Check the logger for default settings.
+     */
+    jkb = env->createBean2(env, env->globalPool, "logger.file", "");
+    env->alias(env, "logger.file:", "logger");
+    env->alias(env, "logger.file:", "logger:");
+    if (jkb == NULL) {
+        fprintf(stderr, "Error creating logger ");
+        return JK_ERR;
+    }
+    env->l = jkb->object;
+    env->alias(env, "logger.file:", "logger");
+
+    /* Create the workerEnv
+     */
+    jkb = env->createBean2(env, env->globalPool, "workerEnv", "");
+    if (jkb == NULL) {
+        fprintf(stderr, "Error creating workerEnv ");
+        return JK_ERR;
+    }
+    workerEnv = jkb->object;
+    env->alias(env, "workerEnv:", "workerEnv");
+
+    if (workerEnv == NULL || env->l == NULL) {
+        fprintf(stderr, "Error initializing jk, NULL objects \n");
+        return JK_ERR;
+    }
+
+    /* serverRoot via ap_server_root
+     */
+    workerEnv->initData->add(env, workerEnv->initData, "serverRoot",
+                             workerEnv->pool->pstrdup(env, workerEnv->pool,
+                                                      ap_server_root));
+
+    /* Local initialization.
+     */
+    env->l->jkLog(env, env->l, JK_LOG_INFO, "Set serverRoot %s\n",
+                  ap_server_root);
+
+    workerEnv->_private = (void *)s;
+
+    return JK_OK;
+}
+
+
+/* -------------------- Apache specific initialization -------------------- */
+
+/* Command table.
+ */
+static const command_rec jk2_cmds[] = {
+    /* This is the 'main' directive for tunning jk2. It takes 2 parameters,
+       and it behaves _identically_ as a setting in workers.properties.
+     */
+    {"JkSet", jk2_set2, NULL, RSRC_CONF, TAKE2,
+     "Set a jk property, same syntax and rules as in JkWorkersFile"},
+    {"JkUriSet", jk2_uriSet, NULL, ACCESS_CONF, TAKE2,
+     "Defines a jk property associated with a Location"},
+    NULL
+};
+
+/** This makes the config for the specified server_rec s
+    This will include vhost info.
+ */
+static void *jk2_create_config(ap_pool * p, server_rec * s)
+{
+    jk_uriEnv_t *newUri;
+    jk_bean_t *jkb;
+    char *tmp;
+
+    if (workerEnv == NULL) {
+        jk2_create_workerEnv(p, s);
+    }
+    if (s->is_virtual == 1) {
+        /* Virtual host */
+
+        tmp =
+            (char *)ap_pcalloc(p,
+                               sizeof(char *) * (strlen(s->server_hostname) +
+                                                 8));
+        sprintf(tmp, "%s:%d/", s->server_hostname, s->port);
+
+        /* for the sake of consistency we must have the port in the uri.
+           Really it isn't necessary to have one - but I would like in the future for 
+           the server config to hold the workers for that server... */
+        jkb = workerEnv->globalEnv->createBean2(workerEnv->globalEnv,
+                                                workerEnv->pool, "uri", tmp);
+    }
+    else {
+        /* Default host */
+        jkb = workerEnv->globalEnv->createBean2(workerEnv->globalEnv,
+                                                workerEnv->pool, "uri", "");
+    }
+
+    newUri = jkb->object;
+    newUri->workerEnv = workerEnv;
+
+    return (void *)newUri;
+}
+
+
+
+/** Standard apache callback, merge jk options specified in 
+    <Host> context. Used to set per virtual host configs
+ */
+static void *jk2_merge_config(ap_pool * p, void *basev, void *overridesv)
+{
+    jk_uriEnv_t *base = (jk_uriEnv_t *)basev;
+    jk_uriEnv_t *overrides = (jk_uriEnv_t *)overridesv;
+
+    /* The 'mountcopy' option should be implemented in common.
+     */
+    return overrides;
+}
+
+/** Standard apache callback, initialize jk. This is called after all
+    the settings took place.
+ */
+static int jk2_init(server_rec * s, ap_pool * pconf)
+{
+    jk_uriEnv_t *serverEnv =
+        (jk_uriEnv_t *)ap_get_module_config(s->module_config, &jk2_module);
+
+    jk_env_t *env = workerEnv->globalEnv;
+
+    ap_pool *gPool = NULL;
+    void *data = NULL;
+    int rc = JK_OK;
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO, "mod_jk child init\n");
+
+    if (s->is_virtual)
+        return OK;
+
+    if (!workerEnv->was_initialized) {
+        workerEnv->was_initialized = JK_TRUE;
+
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "mod_jk.post_config() init worker env\n");
+
+        workerEnv->parentInit(env, workerEnv);
+
+        workerEnv->init(env, workerEnv);
+
+        workerEnv->server_name = (char *)ap_get_server_version();
+
+
+#if MODULE_MAGIC_NUMBER >= 19980527
+        /* Tell apache we're here */
+        ap_add_version_component(JK_EXPOSED_VERSION);
+#endif
+
+    }
+    return OK;
+}
+
+/* ========================================================================= */
+/* The JK module handlers                                                    */
+/* ========================================================================= */
+
+/** Main service method, called to forward a request to tomcat
+ */
+static int jk2_handler(request_rec * r)
+{
+    jk_logger_t *l = NULL;
+    int rc;
+    jk_worker_t *worker = NULL;
+    jk_endpoint_t *end = NULL;
+    jk_uriEnv_t *uriEnv;
+    jk_env_t *env;
+
+    /* If this is a proxy request, we'll notify an error */
+    if (r->proxyreq) {
+        return HTTP_INTERNAL_SERVER_ERROR;      /* We don't proxy requests. */
+    }
+
+    /* changed from r->request_config to r->per_dir_config. This should give us the one that was set in 
+       either the translate phase (if it was a config added through workers.properties) 
+       or in the create_dir config. */
+    uriEnv = ap_get_module_config(r->request_config, &jk2_module);      /* get one for the dir */
+    if (uriEnv == NULL) {
+        uriEnv = ap_get_module_config(r->per_dir_config, &jk2_module);  /* get one specific to this request if there isn't a dir one. */
+    }
+
+    /* not for me, try next handler */
+    if (uriEnv == NULL || strcmp(r->handler, JK_HANDLER) != 0)
+        return DECLINED;
+
+    /* Get an env instance */
+    env = workerEnv->globalEnv->getEnv(workerEnv->globalEnv);
+
+    /* Set up r->read_chunked flags for chunked encoding, if present */
+    if (rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "mod_jk.handler() Can't setup client block %d\n", rc);
+        workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+        return rc;
+    }
+
+    if (uriEnv == NULL) {
+        /* SetHandler case - per_dir config should have the worker */
+        worker = workerEnv->defaultWorker;
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "mod_jk.handler() Default worker for %s %s\n",
+                      r->uri, worker->mbean->name);
+    }
+    else {
+        worker = uriEnv->worker;
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "mod_jk.handler() per dir worker for %#lx %#lx\n",
+                      worker, uriEnv);
+
+        if (worker == NULL && uriEnv->workerName != NULL) {
+            worker = env->getByName(env, uriEnv->workerName);
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "mod_jk.handler() finding worker for %s %#lx %#lx\n",
+                          uriEnv->workerName, worker, uriEnv);
+            uriEnv->worker = worker;
+
+        }
+    }
+
+    if (worker == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "mod_jk.handle() No worker for %s\n", r->uri);
+        workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+        return HTTP_INTERNAL_SERVER_ERROR;      /* No worker defined for this uri */
+    }
+
+    {
+        jk_ws_service_t sOnStack;
+        jk_ws_service_t *s = &sOnStack;
+        jk_pool_t *rPool = NULL;
+        int rc1;
+
+        /* Get a pool for the request XXX move it in workerEnv to
+           be shared with other server adapters */
+        rPool = worker->rPoolCache->get(env, worker->rPoolCache);
+
+        if (rPool == NULL) {
+            rPool =
+                worker->mbean->pool->create(env, worker->mbean->pool,
+                                            HUGE_POOL_SIZE);
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "mod_jk.handler(): new rpool\n");
+        }
+
+        jk2_service_apache13_init(env, s);
+        s->pool = rPool;
+        s->init(env, s, worker, r);
+
+        /* reset the reco_status, will be set to INITED in LB mode */
+        s->reco_status = RECO_NONE;
+
+        s->is_recoverable_error = JK_FALSE;
+        s->uriEnv = uriEnv;
+
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "modjk.handler() Calling %s %#lx\n",
+                      worker->mbean->name, uriEnv);
+
+        rc = worker->service(env, worker, s);
+
+        s->afterRequest(env, s);
+
+        rPool->reset(env, rPool);
+
+        rc1 = worker->rPoolCache->put(env, worker->rPoolCache, rPool);
+        if (rc1 == JK_OK) {
+            rPool = NULL;
+        }
+        else {
+            rPool->close(env, rPool);
+        }
+    }
+
+    if (rc == JK_OK) {
+        workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+        return OK;              /* NOT r->status, even if it has changed. */
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                  "mod_jk.handler() Error connecting to tomcat %d %s\n", rc,
+                  worker == NULL ? "" : worker->channelName ==
+                  NULL ? "" : worker->channelName);
+    workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+    return HTTP_INTERNAL_SERVER_ERROR;  /* Is your tomcat server down ? */
+}
+
+/** Use the internal mod_jk mappings to find if this is a request for
+ *    tomcat and what worker to use. 
+ */
+static int jk2_translate(request_rec * r)
+{
+    jk_uriEnv_t *uriEnv;
+    jk_env_t *env;
+
+    jk_uriMap_t *uriMap;
+    char *name = NULL;
+    int n;
+    const char *ptr;
+
+    if (r->proxyreq) {
+        return DECLINED;
+    }
+
+    uriEnv = ap_get_module_config(r->per_dir_config, &jk2_module);
+    if (uriEnv != NULL && uriEnv->workerName != NULL) {
+        /* jk2_handler tries to get the request_config and then falls back to the per_dir one. 
+           so no point setting the request_config  */
+        r->handler = JK_HANDLER;
+        return OK;
+    }
+
+
+    uriMap = workerEnv->uriMap;
+    /* Get an env instance */
+    env = workerEnv->globalEnv->getEnv(workerEnv->globalEnv);
+    n = uriMap->vhosts->size(env, uriMap->vhosts);
+
+    /* Check JkMount directives, if any */
+/*     if( workerEnv->uriMap->size == 0 ) */
+/*         return DECLINED; */
+
+    /* get_env() */
+    env = workerEnv->globalEnv->getEnv(workerEnv->globalEnv);
+    ptr = ap_get_server_name(r);
+    if (strlen(ptr) > 1024 - 12) {
+        /* That is probably an invalid request, DECLINED could display jsp source code. */
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "jk2_map_to_storage Host too big %s\n", ptr);
+        return HTTP_BAD_REQUEST;
+    }
+    uriEnv = workerEnv->uriMap->mapUri(env, workerEnv->uriMap,
+                                       ptr, ap_get_server_port(r), r->uri);
+
+    if (uriEnv == NULL || uriEnv->workerName == NULL) {
+        workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+        return DECLINED;
+    }
+
+    ap_set_module_config(r->request_config, &jk2_module, uriEnv);
+    r->handler = JK_HANDLER;
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "mod_jk.translate(): uriMap %s %s\n",
+                  r->uri, uriEnv->workerName);
+
+    workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+    return OK;
+}
+
+static const handler_rec jk2_handlers[] = {
+    {JK_MAGIC_TYPE, jk2_handler},
+    {JK_HANDLER, jk2_handler},
+    NULL
+};
+
+module MODULE_VAR_EXPORT jk2_module = {
+    STANDARD_MODULE_STUFF,
+    jk2_init,                   /* module initializer */
+    jk2_create_dir_config,      /* per-directory config creator */
+    jk2_merge_dir_config,       /* dir config merger */
+    jk2_create_config,          /* server config creator */
+/*    jk2_merge_config,            / * server config merger */
+    NULL,
+    jk2_cmds,                   /* command table */
+    jk2_handlers,               /* [7] list of handlers */
+    jk2_translate,              /* [2] filename-to-URI translation */
+    NULL,                       /* [5] check/validate user_id */
+    NULL,                       /* [6] check user_id is valid *here* */
+    NULL,                       /* [4] check access by host address */
+    NULL,                       /* XXX [7] MIME type checker/setter */
+    NULL,                       /* [8] fixups */
+    NULL,                       /* [10] logger */
+    NULL,                       /* [3] header parser */
+    NULL,                       /* apache child process initializer */
+    NULL,                       /* exit_handler, *//* apache child process exit/cleanup */
+    NULL                        /* [1] post read_request handling */
+#ifdef X_EAPI
+        /*
+         * Extended module APIs, needed when using SSL.
+         * STDC say that we do not have to have them as NULL but
+         * why take a chance
+         */
+        , NULL,                 /* add_module */
+    NULL,                       /* remove_module */
+    NULL,                       /* rewrite_command */
+    NULL,                       /* new_connection */
+    NULL                        /* close_connection */
+#endif /* EAPI */
+};
+
+#ifdef WIN32
+
+BOOL WINAPI DllMain(HINSTANCE hInst,    // Instance Handle of the DLL
+                    ULONG ulReason,     // Reason why NT called this DLL
+                    LPVOID lpReserved)  // Reserved parameter for future use
+{
+    GetModuleFileName(hInst, file_name, sizeof(file_name));
+    return TRUE;
+}
+
+#endif
diff --git a/connectors/jk/native2/server/apache13/mod_jk2.dsp b/connectors/jk/native2/server/apache13/mod_jk2.dsp
new file mode 100644
index 0000000..92b86a2
--- /dev/null
+++ b/connectors/jk/native2/server/apache13/mod_jk2.dsp
@@ -0,0 +1,390 @@
+# Microsoft Developer Studio Project File - Name="mod_jk2" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_jk2 - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_jk2.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_jk2.mak" CFG="mod_jk2 - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_jk2 - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_jk2 - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "mod_jk2 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\include" /I "$(APACHE_HOME)\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SHARED_MODULE" /D "WIN32_LEAN_AND_MEAN" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "$(APACHE_HOME)\include" /I "$(APACHE2_HOME)\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SHARED_MODULE" /D "WIN32_LEAN_AND_MEAN" /D "HAVE_JNI" /D "HAS_APR" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x41a /d "_DEBUG"
+# ADD RSC /l 0x41a /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ApacheCore.lib wsock32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept /libpath:"$(APACHE_HOME)\lib" /libpath:"$(APACHE2_HOME)\lib"
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ApacheCore.lib wsock32.lib libapr.lib libaprutil.lib /nologo /dll /debug /machine:I386 /out:"Debug/mod_jk2.so" /pdbtype:sept /libpath:"$(APACHE_HOME)\lib" /libpath:"$(APACHE2_HOME)\lib"
+
+!ELSEIF  "$(CFG)" == "mod_jk2 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /I "$(APACHE_HOME)\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SHARED_MODULE" /D "WIN32_LEAN_AND_MEAN" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "C:\WRKPLACE\PROJECTS\jtc\apache13\include" /I "$(APACHE2_HOME)\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SHARED_MODULE" /D "WIN32_LEAN_AND_MEAN" /D "HAVE_JNI" /D "HAS_APR" /D "EAPI" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x41a /d "NDEBUG"
+# ADD RSC /l 0x41a /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ApacheCore.lib ws2_32.lib /nologo /dll /machine:I386 /libpath:"$(APACHE_HOME)\lib" /libpath:"$(APACHE2_HOME)\lib"
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ApacheCore.lib ws2_32.lib libapr.lib libaprutil.lib /nologo /dll /machine:I386 /out:"Release/mod_jk2.so" /libpath:"C:\WRKPLACE\PROJECTS\jtc\apache13\lib" /libpath:"C:\WRKPLACE\PROJECTS\jtc\apache13\libexec" /libpath:"$(APACHE2_HOME)\lib"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_jk2 - Win32 Debug"
+# Name "mod_jk2 - Win32 Release"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel_apr_socket.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel_jni.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel_un.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_config.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_config_file.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_endpoint.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_env.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_handler_logon.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_handler_response.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jni\jk_jni_aprImpl.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_file.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_win32.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_msg_ajp.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_mutex.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_mutex_proc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_mutex_thread.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_nwmain.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_objCache.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_pool_apr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_registry.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_requtil.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_service_apache13.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_shm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_signal.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_uriEnv.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_uriMap.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_user.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_vm_default.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_ajp13.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_jni.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_lb.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_run.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_status.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_workerEnv.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_jk2.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\..\include\jk_bean.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_channel.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_endpoint.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_env.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_global.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_handler.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_win32_message.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_msg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_mutex.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_objCache.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_registry.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_requtil.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_service.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_shm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_uriEnv.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_uriMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_vm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_workerEnv.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jni\org_apache_jk_apr_AprImpl.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_win32_message.mc
+
+!IF  "$(CFG)" == "mod_jk2 - Win32 Debug"
+
+# Begin Custom Build
+InputDir=\WRKPLACE\PROJECTS\jtc\jk\native2\common
+InputPath=..\..\common\jk_logger_win32_message.mc
+
+"..\..\common\jk_logger_win32_message.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	mc -h $(InputDir) -r $(InputDir) $(InputPath)
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "mod_jk2 - Win32 Release"
+
+# Begin Custom Build
+InputDir=\WRKPLACE\PROJECTS\jtc\jk\native2\common
+InputPath=..\..\common\jk_logger_win32_message.mc
+
+"..\..\common\jk_logger_win32_message.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	mc -h $(InputDir) -r $(InputDir) $(InputPath)
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_win32_message.rc
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/connectors/jk/native2/server/apache2/.cvsignore b/connectors/jk/native2/server/apache2/.cvsignore
new file mode 100644
index 0000000..325f684
--- /dev/null
+++ b/connectors/jk/native2/server/apache2/.cvsignore
@@ -0,0 +1,7 @@
+mod_jk2.dsw
+mod_jk2.ncb
+mod_jk2.opt
+mod_jk2.plg
+Debug
+Release
+
diff --git a/connectors/jk/native2/server/apache2/Makefile.apxs.in b/connectors/jk/native2/server/apache2/Makefile.apxs.in
new file mode 100644
index 0000000..b7a8477
--- /dev/null
+++ b/connectors/jk/native2/server/apache2/Makefile.apxs.in
@@ -0,0 +1,40 @@
+## configure should make the Makefile out of this file.
+prefix=@prefix@
+exec_prefix=@prefix@
+
+APXS=@APXS2@
+OS=@OS@
+JAVA_HOME=@JAVA_HOME@
+JK_DIR := ../..
+APXS_LDFLAGS=@APXS2_LDFLAGS@
+APXS_CFLAGS=@APXS2_CFLAGS@
+
+ifneq ($(strip $(JAVA_HOME)),)
+JAVA_INCL=-I ${JAVA_HOME}/include -I ${JAVA_HOME}/include/${OS}
+JAVA_LIB=-L ${JAVA_HOME}/jre/lib/${ARCH} -L ${JAVA_HOME}/lib/${ARCH}/native_threads
+endif
+
+INCLUDES= -I${JK_DIR}/include \
+          ${JAVA_INCL}
+
+JK_CFLAGS=-DCHUNK_SIZE=4096 -DUSE_APACHE_MD5 @HAVE_JNI@ @HAS_PCRE@
+
+COMMON_C_FILES := $(wildcard ${JK_DIR}/common/*.c )
+JNI_C_FILES := $(wildcard ${JK_DIR}/jni/*.c )
+A2_C_FILES := $(wildcard *.c )
+
+all: mod_jk2.la
+
+mod_jk2.la: 
+	$(APXS) -c -o $@ -Wc,"${INCLUDES} ${JK_CFLAGS}" ${JAVA_LIB} @PCRE_LIBS@ \
+		${A2_C_FILES} ${COMMON_C_FILES} ${JNI_C_FILES}
+
+install: mod_jk2.la
+	$(APXS) -i mod_jk2.la
+ 
+clean:
+	rm -f *.o *.so *.lo *.la *.slo ${JK_DIR}/common/*.o ${JK_DIR}/common/*.so \
+		${JK_DIR}/common/*.lo ${JK_DIR}/common/*.la ${JK_DIR}/common/*.slo \
+		${JK_DIR}/jni/*.o ${JK_DIR}/jni/*.so ${JK_DIR}/jni/*.lo \
+		${JK_DIR}/jni/*.la ${JK_DIR}/jni/*.slo
+	rm -rf .libs ${JK_DIR}/common/.libs ${JK_DIR}/jni/.libs
diff --git a/connectors/jk/native2/server/apache2/Makefile.in b/connectors/jk/native2/server/apache2/Makefile.in
new file mode 100755
index 0000000..93dcffd
--- /dev/null
+++ b/connectors/jk/native2/server/apache2/Makefile.in
@@ -0,0 +1,133 @@
+# Gnu makefile and libtool are required
+# use -D options to overrides defaults
+CC=@CC@
+CP=@CP@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+APACHE2_HOME=@APACHE2_HOME@
+OS=@OS@
+JAVA_HOME=@JAVA_HOME@
+APACHE2_INCL=@APACHE2_INCL@
+APACHE2_LIBEXEC=@APACHE2_LIBEXEC@
+APACHE2_LIBDIR=@APACHE2_LIBDIR@
+APR_INCL=@APR_CFLAGS@ @APR_UTIL_INCDIR@ @APR_INCDIR@
+APR_LIBS=@APR_LIBS@
+APR_LIBDIR_LA=@APR_LIBDIR_LA@
+EXTRA_CFLAGS=@APXS2_CFLAGS@
+EXTRA_CPPFLAGS=@APXS2_CPPFLAGS@
+
+ifneq ($(strip $(JAVA_HOME)),)
+JAVA_INCL=-I ${JAVA_HOME}/include -I ${JAVA_HOME}/include/${OS}
+JAVA_LIB=-L ${JAVA_HOME}/jre/lib/${ARCH} -L ${JAVA_HOME}/lib/${ARCH}/native_threads
+endif
+
+JK_DIR := ../..
+BUILD_DIR = ${JK_DIR}/../build/jk2/apache2
+
+# Extract EXTRA_CFLAGS and EXTRA_CPPFLAGS - same flags used during apache2 
+# compilation
+#include ${APACHE2_HOME}/build/config_vars.mk
+
+# Yes, we use the same properties file as ant
+#include ../../../build.properties
+
+LIBTOOL=@LIBTOOL@ 
+
+# It doesn't hurt if we include all
+INCLUDES= -I${JK_DIR}/include \
+          ${APACHE2_INCL} \
+          ${APR_INCL} \
+          ${JAVA_INCL}
+
+JK_CFLAGS=-DCHUNK_SIZE=4096 -DUSE_APACHE_MD5 @HAVE_JNI@ @HAS_PCRE@
+ifdef APR_LIBDIR_LA
+JK_LDFLAGS=-L${APACHE2_LIBDIR} -lcrypt @PCRE_LIBS@
+else
+JK_LDFLAGS=-lcrypt ${APR_LIBS} @PCRE_LIBS@
+endif
+
+###### Based on rules.mk ##########################################
+ALL_CFLAGS   = $(EXTRA_CFLAGS) $(NOTEST_CFLAGS) $(CFLAGS)
+ALL_CPPFLAGS = $(DEFS) $(EXTRA_CPPFLAGS) $(NOTEST_CPPFLAGS) $(CPPFLAGS)
+ALL_LDFLAGS  = $(EXTRA_LDFLAGS) $(NOTEST_LDFLAGS) $(LDFLAGS)
+ALL_LIBS     = $(EXTRA_LIBS) $(NOTEST_LIBS) $(LIBS)
+ALL_INCLUDES = $(INCLUDES) $(EXTRA_INCLUDES)
+
+# Compile commands
+COMPILE      = $(CC)  $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(ALL_INCLUDES)
+
+SH_COMPILE = $(LIBTOOL) --mode=compile $(COMPILE) $(JK_CFLAGS)
+MOD_LINK = $(LIBTOOL) --mode=link $(CC) -avoid-version -module -rpath ${APACHE2_LIBEXEC} $(LT_LDFLAGS) $(ALL_LDFLAGS)
+MOD_INSTALL = $(LIBTOOL) --mode=install $(CP)
+
+#############################################################################
+
+# ---------- File list creation -------------------- 
+# Same behavior as ant - 'all files from a dir'. 
+# Excludes are not yet implemented.
+
+COMMON_C_FILES := $(wildcard ${JK_DIR}/common/*.c )
+JNI_C_FILES := $(wildcard ${JK_DIR}/jni/*.c )
+A2_C_FILES := $(wildcard ${JK_DIR}/server/apache2/*.c )
+H_FILES := $(wildcard ${JK_DIR}/include/*.h )
+
+COMMON_LO_FILES := $(patsubst ${JK_DIR}/common/%, ${BUILD_DIR}/%, \
+			 $(patsubst %c, %lo, ${COMMON_C_FILES} ))
+JNI_LO_FILES := $(patsubst ${JK_DIR}/jni/%, ${BUILD_DIR}/%, \
+			 $(patsubst %c, %lo, ${JNI_C_FILES} ))
+A2_LO_FILES := $(patsubst ${JK_DIR}/server/apache2/%, ${BUILD_DIR}/%, \
+			 $(patsubst %c, %lo, ${A2_C_FILES} ))
+
+
+# ---------- Compile rules --------------------
+
+.PHONY: all
+
+
+VPATH=.:../../common
+
+.c.lo:
+	 ${SH_COMPILE} -c $< -o $>
+
+${BUILD_DIR}/%.lo: ${JK_DIR}/common/%.c
+	 ${SH_COMPILE} -c $< -o $@
+
+${BUILD_DIR}/%.lo: ${JK_DIR}/jni/%.c
+	 ${SH_COMPILE} -c $< -o $@
+
+${BUILD_DIR}/%.lo: ${JK_DIR}/server/apache2/%.c
+	 ${SH_COMPILE} -c $< -o $@
+
+
+# ---------- Targets -------------------- 
+
+all: prepare ${BUILD_DIR}/mod_jk2.so @JNI_BUILD@
+
+jni-build: ${BUILD_DIR}/libjkjni.so
+
+${BUILD_DIR}/mod_jk2.so: ${BUILD_DIR}/${APACHE2_LIBEXEC}/mod_jk2.so
+	$(CP) $^ $@
+${BUILD_DIR}/${APACHE2_LIBEXEC}/mod_jk2.so: ${BUILD_DIR}/mod_jk2.la
+	$(MOD_INSTALL) $^ `pwd`/${BUILD_DIR}/${APACHE2_LIBEXEC}
+
+${BUILD_DIR}/libjkjni.so: ${BUILD_DIR}/${APACHE2_LIBEXEC}/libjkjni.so
+	$(CP) $^ $@
+${BUILD_DIR}/${APACHE2_LIBEXEC}/libjkjni.so: ${BUILD_DIR}/libjkjni.la
+	$(MOD_INSTALL) $^ `pwd`/${BUILD_DIR}/${APACHE2_LIBEXEC}
+
+${BUILD_DIR}/libjkjni.la: ${JNI_LO_FILES} ${COMMON_LO_FILES}
+	$(MOD_LINK) -o $@ $(JK_LDFLAGS) $^
+
+${BUILD_DIR}/mod_jk2.la: ${COMMON_LO_FILES} ${JNI_LO_FILES} ${A2_LO_FILES}
+	${MOD_LINK} -o $@ $^ 
+
+${COMMON_C_FILES} ${A2_C_FILES}: ${H_FILES}
+
+prepare: 
+	mkdir -p ${BUILD_DIR}${APACHE2_LIBEXEC}
+
+clean: 
+	rm -rf ${BUILD_DIR}/*.lo ${BUILD_DIR}/*.la ${BUILD_DIR}/*.o ${BUILD_DIR}/*.a \
+		${BUILD_DIR}/.libs ${BUILD_DIR}/*.so ${BUILD_DIR}${APACHE2_LIBEXEC}/*.so \
+		${BUILD_DIR}${APACHE2_LIBEXEC}/*.la
diff --git a/connectors/jk/native2/server/apache2/NWGNUmakefile b/connectors/jk/native2/server/apache2/NWGNUmakefile
new file mode 100644
index 0000000..c319b77
--- /dev/null
+++ b/connectors/jk/native2/server/apache2/NWGNUmakefile
@@ -0,0 +1,352 @@
+#
+# Makefile for mod_jk2 (uses the build system of Apache2 - gnu make)
+# created by Guenter Knauf <eflash@gmx.net>
+#
+
+# The folloeing setting enables build with jni support. Either uncommend
+# the following line, or set an environment var.
+# WITH_JNI = 1
+
+#
+# Declare the sub-directories to be built here
+#
+
+SUBDIRS = \
+	$(EOLIST)
+
+#
+# Get the 'head' of the build environment.  This includes default targets and
+# paths to tools
+#
+
+include $(AP_WORK)\build\NWGNUhead.inc
+
+#
+# build this level's files
+
+#
+# Make sure all needed macro's are defined
+#
+
+JKCOMMON = ../../common
+JKINCLUDE = ../../include
+JKJNI = ../../jni
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS	+= \
+			$(JKCOMMON) \
+			$(JKINCLUDE) \
+			$(JKJNI) \
+			$(AP_WORK)/include \
+			$(NWOS) \
+			$(AP_WORK)/modules/arch/netware \
+			$(AP_WORK)/srclib/apr/include \
+			$(AP_WORK)/srclib/apr-util/include \
+			$(AP_WORK)/srclib/apr \
+			$(AP_WORK)/srclib/pcre \
+			$(JAVA_HOME)/include \
+			$(JAVA_HOME)/include/netware \
+			$(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS		+= \
+			-relax_pointers \
+			$(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES	+= \
+			-D__NOVELL_LIBC__ \
+			-D_POSIX_SOURCE \
+			-DHAS_APR \
+			-DHAS_AP_PCRE \
+			$(EOLIST)
+
+ifdef WITH_JNI
+	XDEFINES += -DHAVE_JNI
+endif
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS		+= \
+			$(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+		   	$(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+		   	$(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS	+= \
+			$(EOLIST)
+
+XCFLAGS		+= \
+			$(EOLIST)
+
+XDEFINES	+= \
+			$(EOLIST)
+
+XLFLAGS		+= \
+			$(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm.  If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME		= mod_jk2
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION	= Apache $(VERSION_STR) plugin for Jakarta/Tomcat $(JK_VERSION_STR)
+ifdef WITH_JNI
+	NLM_DESCRIPTION += (JNI)
+endif
+
+#
+# This is used by the link '-copy ' directive.
+# If left blank, the ASF copyright defined in NWGNUtail.inc will be used.
+#
+NLM_COPYRIGHT	=
+
+#
+# This is used by the '-threadname' directive.  If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME	= JK2 Module
+
+#
+# If this is specified, it will override VERSION value in
+# $(AP_WORK)\build\NWGNUenvironment.inc
+#
+NLM_VERSION	= $(JK_VERSION)
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE	= 49152
+
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM	= _LibCPrelude
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM	= _LibCPostlude
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM	=
+
+#
+# If these are specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS	=  AUTOUNLOAD, PSEUDOPREEMPTION
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(NWOS)/apache.xdc.  XDCData can be disabled
+# by setting APACHE_UNIPROC in the environment
+#
+XDCDATA		=
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+	$(OBJDIR)/mod_jk2.nlm \
+	$(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+	$(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+	$(OBJDIR)/jk_nwmain.o \
+	$(OBJDIR)/jk_channel.o \
+	$(OBJDIR)/jk_channel_apr_socket.o \
+	$(OBJDIR)/jk_channel_jni.o \
+	$(OBJDIR)/jk_channel_un.o \
+	$(OBJDIR)/jk_config.o \
+	$(OBJDIR)/jk_config_file.o \
+	$(OBJDIR)/jk_endpoint.o \
+	$(OBJDIR)/jk_env.o \
+	$(OBJDIR)/jk_handler_logon.o \
+	$(OBJDIR)/jk_handler_response.o \
+	$(OBJDIR)/jk_logger_file.o \
+	$(OBJDIR)/jk_logger_win32.o \
+	$(OBJDIR)/jk_map.o \
+	$(OBJDIR)/jk_md5.o \
+	$(OBJDIR)/jk_msg_ajp.o \
+	$(OBJDIR)/jk_mutex.o \
+	$(OBJDIR)/jk_mutex_proc.o \
+	$(OBJDIR)/jk_mutex_thread.o \
+	$(OBJDIR)/jk_objCache.o \
+	$(OBJDIR)/jk_pool_apr.o \
+	$(OBJDIR)/jk_registry.o \
+	$(OBJDIR)/jk_requtil.o \
+	$(OBJDIR)/jk_shm.o \
+	$(OBJDIR)/jk_signal.o \
+	$(OBJDIR)/jk_uriEnv.o \
+	$(OBJDIR)/jk_uriMap.o \
+	$(OBJDIR)/jk_user.o \
+	$(OBJDIR)/jk_vm_default.o \
+	$(OBJDIR)/jk_worker_ajp13.o \
+	$(OBJDIR)/jk_worker_jni.o \
+	$(OBJDIR)/jk_worker_lb.o \
+	$(OBJDIR)/jk_worker_run.o \
+	$(OBJDIR)/jk_worker_status.o \
+	$(OBJDIR)/jk_workerEnv.o \
+	$(OBJDIR)/jk_logger_apache2.o \
+	$(OBJDIR)/jk_map_aprtable.o \
+	$(OBJDIR)/jk_service_apache2.o \
+	$(OBJDIR)/mod_jk2.o \
+	$(EOLIST)
+
+ifdef WITH_JNI
+	FILES_nlm_objs += $(OBJDIR)/jk_jni_aprImpl.o
+endif
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+	libcpre.o \
+	$(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+	aprlib \
+	libc \
+	$(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override $(NWOS)\copyright.txt.
+#
+FILE_nlm_copyright = 
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+	@$(APR)/aprlib.imp \
+	@$(NWOS)/httpd.imp \
+	@libc.imp \
+	@ws2nlm.imp \
+	$(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+	jk2_module \
+	$(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+		$(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place.  (See $(AP_WORK)\build\NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+	copy $(OBJDIR)\*.nlm $(INSTALL)\Apache2\modules\*.*
+
+#
+# Any specialized rules here
+#
+
+vpath %.c $(JKCOMMON);$(JKJNI)
+
+$(OBJDIR)/version.inc: $(JKINCLUDE)/jk_global.h $(OBJDIR)
+	@echo Creating $@
+	@awk -f ../../../../common/build/get_ver.awk $< > $@
+
+#
+# Include the version info retrieved from jk_global.h
+#
+
+-include $(OBJDIR)/version.inc
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(AP_WORK)\build\NWGNUtail.inc
+
+
+
diff --git a/connectors/jk/native2/server/apache2/bldjk2.qclsrc b/connectors/jk/native2/server/apache2/bldjk2.qclsrc
new file mode 100644
index 0000000..4d920c9
--- /dev/null
+++ b/connectors/jk/native2/server/apache2/bldjk2.qclsrc
@@ -0,0 +1,529 @@
+PGM

+CRTCMOD MODULE(MOD_JK2/MOD_JK2) +

+	SRCSTMF('/home/apache/jk/native2/server/apache2/mod_jk2.c') +

+	DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' + 

+	       'USE_APACHE_MD5' '_REENTRANT') +

+	TEXT('mod_jk2.c') +

+	OPTIMIZE(40) +

+	SYSIFCOPT(*IFSIO) +

+	TGTCCSID(*JOB) +

+	OPTION(*LOGMSG) +

+	TERASPACE(*YES *TSIFC) +

+	STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' + 

+           '/home/apache/jk/native2/server/apache2' +

+           '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_LOG_AP2) +

+	SRCSTMF('/home/apache/jk/native2/server/apache2/jk_logger_apache2.c') +

+	DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' + 

+	       'USE_APACHE_MD5' '_REENTRANT') +

+	TEXT('jk_logger_apache2.c') +

+	OPTIMIZE(40) +

+	SYSIFCOPT(*IFSIO) +

+	TGTCCSID(*JOB) +

+	OPTION(*LOGMSG) +

+	TERASPACE(*YES *TSIFC) +

+	STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' + 

+           '/home/apache/jk/native2/server/apache2' +

+           '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_MAP_APR) +

+	SRCSTMF('/home/apache/jk/native2/server/apache2/jk_map_aprtable.c') +

+	DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' + 

+	       'USE_APACHE_MD5' '_REENTRANT') +

+	TEXT('jk_map_aprtable.c') +

+	OPTIMIZE(40) +

+	SYSIFCOPT(*IFSIO) +

+	TGTCCSID(*JOB) +

+	OPTION(*LOGMSG) +

+	TERASPACE(*YES *TSIFC) +

+	STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' + 

+           '/home/apache/jk/native2/server/apache2' +

+           '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_SRV_AP2) +

+	SRCSTMF('/home/apache/jk/native2/server/apache2/jk_service_apache2.c') +

+	DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' + 

+	       'USE_APACHE_MD5' '_REENTRANT') +

+	TEXT('jk_service_apache2.c') +

+	OPTIMIZE(40) +

+	SYSIFCOPT(*IFSIO) +

+	TGTCCSID(*JOB) +

+	OPTION(*LOGMSG) +

+	TERASPACE(*YES *TSIFC) +

+	STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' + 

+           '/home/apache/jk/native2/server/apache2' +

+           '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_CHANNEL) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_channel.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_channel.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_CH_APRS) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_channel_apr_socket.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_channel_apr_socket.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_CH_JNI) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_channel_jni.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' 'OS400_JVM_12' + 

+           '_XOPEN_SOURCE=520' +  'USE_APACHE_MD5' +

+    '_REENTRANT') +

+    TEXT('jk_channel_jni.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_CH_UN) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_channel_un.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_channel_un') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_CONFIG) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_config.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_config.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_ENDP) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_endpoint.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_endpoint.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_ENV) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_env.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_env.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_HDL_LOG) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_handler_logon.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_handler_logon.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_HDL_RES) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_handler_response.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_handler_response.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG ) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_LOG_FIL) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_logger_file.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_logger_file') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_MAP) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_map.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_map.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_MD5) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_md5.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_md5.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_MSG_AJP) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_msg_ajp.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_msg_ajp.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_MUTEX) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_mutex.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_mutex.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_MUTEX_T) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_mutex_thread.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_mutex_thread.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_OBJ_CAC) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_objCache.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_objCache.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_POOL_AP) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_pool_apr.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_pool_apr.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_REGIS) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_registry.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_registry.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_REQUTIL) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_requtil.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_requtil.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_SHM) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_shm.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_shm.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_SIGNAL) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_signal.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_signal.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_URIENV) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_uriEnv.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_uriEnv.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_URIMAP) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_uriMap.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_uriMap.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_USER) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_user.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_user.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_VM_DEF) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_vm_default.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' 'OS400_JVM_12' + 

+           '_XOPEN_SOURCE=520' +  'USE_APACHE_MD5' +

+    '_REENTRANT') +

+    TEXT('jk_vm_default.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_W_ENV) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_workerEnv.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_workerEnv.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_W_AJP13) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_worker_ajp13.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_worker_ajp13.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_W_JNI) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_worker_jni.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' 'OS400_JVM_12' + 

+           '_XOPEN_SOURCE=520' +  'USE_APACHE_MD5' +

+    '_REENTRANT') +

+    TEXT('jk_worker_jni.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_W_LB) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_worker_lb.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_worker_lb.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_W_RUN) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_worker_run.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_worker_run.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTCMOD MODULE(MOD_JK2/JK_W_STAT) +

+    SRCSTMF('/home/apache/jk/native2/common/jk_worker_status.c') +

+    DEFINE('AS400' 'HAVE_JNI' 'HAVE_APR' 'HAS_PCRE' '_XOPEN_SOURCE=520' +  

+           'USE_APACHE_MD5' '_REENTRANT') +

+    TEXT('jk_worker_status.c') +

+    OPTIMIZE(40) +

+    SYSIFCOPT(*IFSIO) +

+    LANGLVL(*ANSI) +

+    TGTCCSID(*JOB) +

+    OPTION(*LOGMSG) +

+    TERASPACE(*YES *TSIFC) +

+    STGMDL(*INHERIT) +

+    INCDIR('/home/apache/jk/native2/include' '/QIBM/ProdData/HTTPA/Include')

+

+CRTSRVPGM SRVPGM(MOD_JK2/MOD_JK2) +

+      MODULE(MOD_JK2/MOD_JK2    MOD_JK2/JK_LOG_AP2 +

+             MOD_JK2/JK_MAP_APR MOD_JK2/JK_SRV_AP2 +

+             MOD_JK2/JK_CHANNEL MOD_JK2/JK_CH_APRS +

+             MOD_JK2/JK_CH_JNI  MOD_JK2/JK_CH_UN   +

+             MOD_JK2/JK_CONFIG  MOD_JK2/JK_ENDP    +

+             MOD_JK2/JK_ENV     MOD_JK2/JK_HDL_LOG +

+             MOD_JK2/JK_HDL_RES MOD_JK2/JK_LOG_FIL +

+             MOD_JK2/JK_MAP     MOD_JK2/JK_MD5     +

+             MOD_JK2/JK_MSG_AJP MOD_JK2/JK_MUTEX   +

+             MOD_JK2/JK_MUTEX_T MOD_JK2/JK_MSG_AJP +

+             MOD_JK2/JK_MUTEX_T MOD_JK2/JK_OBJ_CAC +

+             MOD_JK2/JK_POOL_AP MOD_JK2/JK_REGIS   +

+             MOD_JK2/JK_REQUTIL MOD_JK2/JK_SHM     +

+             MOD_JK2/JK_SIGNAL  MOD_JK2/JK_URIENV  +

+             MOD_JK2/JK_URIMAP  MOD_JK2/JK_USER    +

+             MOD_JK2/JK_VM_DEF  MOD_JK2/JK_W_ENV   +

+             MOD_JK2/JK_W_AJP13 MOD_JK2/JK_W_JNI   +

+             MOD_JK2/JK_W_LB    MOD_JK2/JK_W_RUN   +

+             MOD_JK2/JK_W_STAT) +

+      EXPORT(*SRCFILE) +

+      BNDDIR() +

+      SRCFILE(MOD_JK2/QSRVSRC) +

+      SRCMBR(MOD_JK2) +

+      DETAIL(*BASIC) +

+      STGMDL(*INHERIT) +

+      BNDSRVPGM(QHTTPSVR/QZSRAPR QHTTPSVR/QZSRCORE +

+                QHTTPSVR/QZSRXMLP QHTTPSVR/QZSRSDBM) +

+      TEXT('Apache mod_jk2 tomcat connector module')

+

+ENDPGM

diff --git a/connectors/jk/native2/server/apache2/jk_apache2.h b/connectors/jk/native2/server/apache2/jk_apache2.h
new file mode 100644
index 0000000..8c6e694
--- /dev/null
+++ b/connectors/jk/native2/server/apache2/jk_apache2.h
@@ -0,0 +1,76 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Description: Apache 2 plugin for Jakarta/Tomcat                         
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           
+ *              Henri Gomez <hgomez@apache.org>                            
+ * Version:     $Revision$                                           
+ */
+
+#ifndef JK_APACHE2_H
+#define JK_APACHE2_H
+
+#include "ap_config.h"
+#include "apr_lib.h"
+#include "apr_date.h"
+#include "apr_strings.h"
+#include "apr_pools.h"
+#include "apr_tables.h"
+#include "apr_hash.h"
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "http_log.h"
+
+#include "jk_global.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_env.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+#include "jk_workerEnv.h"
+#include "jk_uriMap.h"
+#include "jk_requtil.h"
+
+/* changed with apr 1.0 */
+#include "apr_version.h"
+#if (APR_MAJOR_VERSION < 1)
+#define apr_filepath_name_get apr_filename_of_pathname
+#define apr_pool_parent_get apr_pool_get_parent
+#endif
+
+extern module AP_MODULE_DECLARE_DATA jk2_module;
+
+int JK_METHOD jk2_service_apache2_init(jk_env_t *env, jk_ws_service_t *s);
+
+int JK_METHOD jk2_logger_apache2_factory(jk_env_t *env, jk_pool_t *pool,
+                                         jk_bean_t *result, const char *type,
+                                         const char *name);
+
+int JK_METHOD jk2_pool_apr_factory(jk_env_t *env, jk_pool_t *pool,
+                                   jk_bean_t *result, const char *type,
+                                   const char *name);
+
+int JK_METHOD jk2_map_aprtable_factory(jk_env_t *env, jk_pool_t *pool,
+                                       jk_bean_t *result,
+                                       const char *type, const char *name);
+
+#endif /* JK_APACHE2_H */
diff --git a/connectors/jk/native2/server/apache2/jk_logger_apache2.c b/connectors/jk/native2/server/apache2/jk_logger_apache2.c
new file mode 100644
index 0000000..e6fc16c
--- /dev/null
+++ b/connectors/jk/native2/server/apache2/jk_logger_apache2.c
@@ -0,0 +1,168 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Description: Logger implementation using apache's native logging.
+ *
+ * This is the result of lazyness - a single log file to watch ( error.log )
+ * instead of 2, no need to explain/document/decide where to place mod_jk
+ * logging, etc.
+ *
+ * Normal apache logging rules apply.
+ *
+ * XXX Jk will use per/compoment logging level. All logs will be WARN level
+ * in apache, and the filtering will happen on each component level.
+ *
+ * XXX Add file/line
+ *
+ * XXX Use env, use the current request structure ( so we can split the log
+ * based on vhost configs ).
+ *
+ * @author Costin Manolache
+ */
+
+#include "jk_apache2.h"
+#include <stdio.h>
+
+#define HUGE_BUFFER_SIZE (8*1024)
+
+
+static int JK_METHOD jk2_logger_apache2_log(jk_env_t *env, jk_logger_t *l,
+                                            int level, const char *what)
+{
+    return JK_OK;
+}
+
+
+static int JK_METHOD jk2_logger_apache2_init(jk_env_t *env,
+                                             jk_logger_t *_this)
+{
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_logger_apache2_close(jk_env_t *env,
+                                              jk_logger_t *_this)
+{
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_logger_apache2_jkVLog(jk_env_t *env, jk_logger_t *l,
+                                               const char *file,
+                                               int line,
+                                               int level,
+                                               const char *fmt, va_list args)
+{
+    /* XXX map jk level to apache level */
+    server_rec *s = (server_rec *) l->logger_private;
+    /* If we use apache2 logger, we should also use APR pools.
+       It is possible to do some workarounds, but it would be stupid, especially
+       since the idea is to use apr pools long term, with the old jk_pool as
+       a workaround for apache13 and where apr is not available */
+    apr_pool_t *aprPool = env->tmpPool->_private;
+    int rc;
+    char *buf;
+
+    /* XXX XXX Change this to "SMALLSTACK" or something, I don't think it's
+       netware specific */
+
+    if (level < l->level)
+        return JK_OK;
+
+    if (s == NULL) {
+        return JK_ERR;
+    }
+
+    buf = apr_pvsprintf(aprPool, fmt, args);
+
+    rc = strlen(buf);
+    /* Remove trailing \n. XXX need to change the log() to not include \n */
+    if (buf[rc - 1] == '\n')
+        buf[rc - 1] = '\0';
+
+    if (level == JK_LOG_DEBUG_LEVEL) {
+        ap_log_error(file, line, APLOG_DEBUG | APLOG_NOERRNO, 0, s, buf);
+    }
+    else if (level == JK_LOG_INFO_LEVEL) {
+        ap_log_error(file, line, APLOG_NOTICE | APLOG_NOERRNO, 0, s, buf);
+    }
+    else {
+        ap_log_error(file, line, APLOG_ERR | APLOG_NOERRNO, 0, s, buf);
+    }
+
+    return rc;
+}
+
+static int jk2_logger_apache2_jkLog(jk_env_t *env, jk_logger_t *l,
+                                    const char *file,
+                                    int line, int level, const char *fmt, ...)
+{
+    va_list args;
+    int rc;
+
+    va_start(args, fmt);
+    rc = jk2_logger_apache2_jkVLog(env, l, file, line, level, fmt, args);
+    va_end(args);
+
+    return rc;
+}
+
+
+static int JK_METHOD
+jk2_logger_file_setProperty(jk_env_t *env, jk_bean_t *mbean,
+                            char *name, void *valueP)
+{
+    jk_logger_t *_this = mbean->object;
+    char *value = valueP;
+
+    if (strcmp(name, "level") == 0) {
+        _this->level = jk2_logger_file_parseLogLevel(env, value);
+        if (_this->level == JK_LOG_DEBUG_LEVEL) {
+            env->debug = 1;
+            /*             _this->jkLog( env, _this, JK_LOG_ERROR, */
+            /*                           "Level %s %d \n", value, _this->level ); */
+        }
+        return JK_OK;
+    }
+    return JK_ERR;
+}
+
+
+
+int JK_METHOD
+jk2_logger_apache2_factory(jk_env_t *env, jk_pool_t *pool, jk_bean_t *result,
+                           const char *type, const char *name)
+{
+    jk_logger_t *l = (jk_logger_t *)pool->calloc(env, pool,
+                                                 sizeof(jk_logger_t));
+
+    if (l == NULL) {
+        return JK_ERR;
+    }
+
+    l->log = jk2_logger_apache2_log;
+    l->logger_private = NULL;
+    l->init = jk2_logger_apache2_init;
+    l->jkLog = jk2_logger_apache2_jkLog;
+    l->jkVLog = jk2_logger_apache2_jkVLog;
+
+    l->level = JK_LOG_ERROR_LEVEL;
+
+    result->object = (void *)l;
+    l->mbean = result;
+    result->setAttribute = jk2_logger_file_setProperty;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/server/apache2/jk_map_aprtable.c b/connectors/jk/native2/server/apache2/jk_map_aprtable.c
new file mode 100644
index 0000000..8985f0f
--- /dev/null
+++ b/connectors/jk/native2/server/apache2/jk_map_aprtable.c
@@ -0,0 +1,128 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Implementation of map using apr_table. This avoids copying the headers,
+ * env, etc in jk_service - we can just wrap them.
+ *
+ * Note that this _require_ that apr pools are used ( can't be used
+ * with jk_pools ), i.e. you must use apr for both pools and maps.
+ *
+ * @author Costin Manolache
+ */
+
+#include "jk_pool.h"
+#include "jk_env.h"
+#include "apr_pools.h"
+#include "apr_strings.h"
+#include "apr_tables.h"
+
+#include "jk_apache2.h"
+
+
+static void *jk2_map_aprtable_get(struct jk_env *env, struct jk_map *_this,
+                                  const char *name)
+{
+    apr_table_t *aprMap = _this->_private;
+    return (void *)apr_table_get(aprMap, name);
+}
+
+static int jk2_map_aprtable_put(struct jk_env *env, struct jk_map *_this,
+                                const char *name, void *value,
+                                void **oldValue)
+{
+    apr_table_t *aprMap = _this->_private;
+    if (oldValue != NULL) {
+        *oldValue = (void *)apr_table_get(aprMap, (char *)name);
+    }
+
+    apr_table_setn(aprMap, name, (char *)value);
+
+    return JK_OK;
+}
+
+static int jk2_map_aprtable_add(struct jk_env *env, struct jk_map *_this,
+                                const char *name, void *value)
+{
+    apr_table_t *aprMap = _this->_private;
+
+    apr_table_addn(aprMap, name, (char *)value);
+
+    return JK_OK;
+}
+
+static int jk2_map_aprtable_size(struct jk_env *env, struct jk_map *_this)
+{
+    apr_table_t *aprMap = _this->_private;
+    const apr_array_header_t *ah = apr_table_elts(aprMap);
+
+    return ah->nelts;
+
+}
+
+static char *jk2_map_aprtable_nameAt(struct jk_env *env, struct jk_map *_this,
+                                     int pos)
+{
+    apr_table_t *aprMap = _this->_private;
+    const apr_array_header_t *ah = apr_table_elts(aprMap);
+    apr_table_entry_t *elts = (apr_table_entry_t *) ah->elts;
+
+
+    return elts[pos].key;
+}
+
+static void *jk2_map_aprtable_valueAt(struct jk_env *env,
+                                      struct jk_map *_this, int pos)
+{
+    apr_table_t *aprMap = _this->_private;
+    const apr_array_header_t *ah = apr_table_elts(aprMap);
+    apr_table_entry_t *elts = (apr_table_entry_t *) ah->elts;
+
+    return elts[pos].val;
+}
+
+static void jk2_map_aprtable_init(jk_env_t *env, jk_map_t *m, int initialSize,
+                                  void *wrappedObj)
+{
+    m->_private = wrappedObj;
+}
+
+static void jk2_map_aprtable_clear(jk_env_t *env, jk_map_t *m)
+{
+
+}
+
+
+/* Not used yet */
+int JK_METHOD jk2_map_aprtable_factory(jk_env_t *env, jk_pool_t *pool,
+                                       jk_bean_t *result,
+                                       const char *type, const char *name)
+{
+    jk_map_t *_this = (jk_map_t *)pool->calloc(env, pool, sizeof(jk_map_t));
+
+    result->object = _this;
+
+    _this->get = jk2_map_aprtable_get;
+    _this->put = jk2_map_aprtable_put;
+    _this->add = jk2_map_aprtable_add;
+    _this->size = jk2_map_aprtable_size;
+    _this->nameAt = jk2_map_aprtable_nameAt;
+    _this->valueAt = jk2_map_aprtable_valueAt;
+    _this->init = jk2_map_aprtable_init;
+    _this->clear = jk2_map_aprtable_clear;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/server/apache2/jk_service_apache2.c b/connectors/jk/native2/server/apache2/jk_service_apache2.c
new file mode 100644
index 0000000..0578f97
--- /dev/null
+++ b/connectors/jk/native2/server/apache2/jk_service_apache2.c
@@ -0,0 +1,572 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Description: Apache 2 plugin for Jakarta/Tomcat                         
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           
+ *                 Henri Gomez <hgomez@apache.org>                            
+ * Version:     $Revision$                                           
+ */
+
+/*
+ * Jakarta (jk_) include files
+ */
+#include "jk_apache2.h"
+
+#include "util_script.h"
+
+/* #define USE_APRTABLES  */
+
+#define NULL_FOR_EMPTY(x)   ((((x)!=NULL) && (strlen((x))!=0)) ? (x) : NULL )
+
+static int JK_METHOD jk2_service_apache2_head(jk_env_t *env,
+                                              jk_ws_service_t *s)
+{
+    int h;
+    int numheaders;
+    request_rec *r;
+    jk_map_t *headers;
+    int debug = 1;
+
+    if (s == NULL || s->ws_private == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "service.head() NullPointerException\n");
+        return JK_ERR;
+    }
+
+    if (s->uriEnv != NULL)
+        debug = s->uriEnv->mbean->debug;
+
+    r = (request_rec *) s->ws_private;
+
+    if (s->msg == NULL) {
+        s->msg = "";
+    }
+    r->status = s->status;
+    r->status_line = apr_psprintf(r->pool, "%d %s", s->status, s->msg);
+    headers = s->headers_out;
+
+#ifdef USE_APRTABLES
+    {
+        char *val = headers->get(env, headers, "Content-Type");
+        if (val != NULL) {
+            char *tmp = apr_pstrdup(r->pool, val);
+            ap_content_type_tolower(tmp);
+            /* It should be done like this in Apache 2.0 */
+            /* This way, Apache 2.0 will be able to set the output filter */
+            /* and it make jk useable with deflate using AddOutputFilterByType DEFLATE text/html */
+            ap_set_content_type(r, tmp);
+        }
+        val = headers->get(env, headers, "Last-Modified");
+        if (val != NULL) {
+            /*
+             * If the script gave us a Last-Modified header, we can't just
+             * pass it on blindly because of restrictions on future values.
+             */
+            ap_update_mtime(r, apr_date_parse_http(val));
+            ap_set_last_modified(r);
+        }
+
+        /* No other change required - headers is the same as req->headers_out,
+           just with a different interface
+         */
+    }
+#else
+    numheaders = headers->size(env, headers);
+    /* XXX As soon as we switch to jk_map_apache2, this will not be needed ! */
+    if (debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "service.head() %d %d %#lx\n", s->status,
+                      numheaders, s->uriEnv);
+
+    for (h = 0; h < numheaders; h++) {
+        char *name = headers->nameAt(env, headers, h);
+        char *val = headers->valueAt(env, headers, h);
+        name = s->pool->pstrdup(env, s->pool, name);
+        val = s->pool->pstrdup(env, s->pool, val);
+
+        if (debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "service.head() %s: %s %d %d\n", name, val, h,
+                          headers->size(env, headers));
+
+        /* the cmp can also be avoided in we do this earlier and use
+           the header id */
+        if (!strcasecmp(name, "Content-type")) {
+            /* XXX should be done in handler ! */
+            char *tmp = apr_pstrdup(r->pool, val);
+            ap_content_type_tolower(tmp);
+            /* It should be done like this in Apache 2.0 */
+            /* This way, Apache 2.0 will be able to set the output filter */
+            /* and it make jk useable with deflate using AddOutputFilterByType DEFLATE text/html */
+            ap_set_content_type(r, tmp);
+            r->content_type = tmp;
+            apr_table_set(r->headers_out, name, val);
+        }
+        else if (!strcasecmp(name, "Location")) {
+            /* XXX setn */
+            apr_table_set(r->headers_out, name, val);
+        }
+        else if (!strcasecmp(name, "Content-Length")) {
+            apr_table_set(r->headers_out, name, val);
+        }
+        else if (!strcasecmp(name, "Transfer-Encoding")) {
+            apr_table_set(r->headers_out, name, val);
+        }
+        else if (!strcasecmp(name, "Last-Modified")) {
+            /*
+             * If the script gave us a Last-Modified header, we can't just
+             * pass it on blindly because of restrictions on future values.
+             */
+            ap_update_mtime(r, apr_date_parse_http(val));
+            ap_set_last_modified(r);
+            apr_table_set(r->headers_out, name, val);
+        }
+        else {
+            /* All other headers may have multiple values like
+             * Set-Cookie, so use the table_add to allow that.
+             */
+            apr_table_add(r->headers_out, name, val);
+            /* apr_table_set(r->headers_out, name, val); */
+        }
+    }
+#endif
+
+    /* this NOP function was removed in apache 2.0 alpha14 */
+    /* ap_send_http_header(r); */
+    s->response_started = JK_TRUE;
+
+    return JK_OK;
+}
+
+/*
+ * Read a chunk of the request body into a buffer.  Attempt to read len
+ * bytes into the buffer.  Write the number of bytes actually read into
+ * actually_read.
+ *
+ * Think of this function as a method of the apache1.3-specific subclass of
+ * the jk_ws_service class.  Think of the *s param as a "this" or "self"
+ * pointer.
+ */
+static int JK_METHOD jk2_service_apache2_read(jk_env_t *env,
+                                              jk_ws_service_t *s, void *b,
+                                              unsigned len,
+                                              unsigned *actually_read)
+{
+    if (s == NULL || s->ws_private == NULL || b == NULL
+        || actually_read == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "service.read() NullPointerException\n");
+        return JK_ERR;
+    }
+
+    if (!s->read_body_started) {
+        if (ap_should_client_block(s->ws_private)) {
+            s->read_body_started = JK_TRUE;
+        }
+    }
+
+    if (s->read_body_started) {
+        long rv;
+        if ((rv = ap_get_client_block(s->ws_private, b, len)) < 0) {
+            *actually_read = 0;
+        }
+        else {
+            *actually_read = (unsigned)rv;
+        }
+    }
+    return JK_OK;
+}
+
+/*
+ * Write a chunk of response data back to the browser.  If the headers
+ * haven't yet been sent over, send over default header values (Status =
+ * 200, basically).
+ *
+ * Write len bytes from buffer b.
+ *
+ * Think of this function as a method of the apache1.3-specific subclass of
+ * the jk_ws_service class.  Think of the *s param as a "this" or "self"
+ * pointer.
+ */
+/* Works with 4096, fails with 8192 */
+#ifndef CHUNK_SIZE
+#define CHUNK_SIZE 4096
+#endif
+
+static int JK_METHOD jk2_service_apache2_write(jk_env_t *env,
+                                               jk_ws_service_t *s,
+                                               const void *b,
+                                               unsigned int len)
+{
+    size_t r = 0;
+    long ll = len;
+    char *bb = (char *)b;
+    request_rec *rr;
+    int debug = 1;
+    int rc;
+
+    if (s == NULL || s->ws_private == NULL || b == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "service.write() NullPointerException\n");
+        return JK_ERR;
+    }
+    if (s->uriEnv != NULL)
+        debug = s->uriEnv->mbean->debug;
+
+    if (len == 0) {
+        return JK_OK;
+    }
+
+    /* BUFF *bf = p->r->connection->client; */
+    /* size_t w = (size_t)l; */
+    rr = s->ws_private;
+
+    if (!s->response_started) {
+        if (debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "service.write() default head\n");
+        rc = s->head(env, s);
+        if (rc != JK_OK) {
+            return rc;
+        }
+
+        {
+            const apr_array_header_t *t = apr_table_elts(rr->headers_out);
+            if (t && t->nelts) {
+                int i;
+
+                apr_table_entry_t *elts = (apr_table_entry_t *) t->elts;
+
+                if (debug > 0) {
+                    for (i = 0; i < t->nelts; i++) {
+                        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                      "OutHeaders %s: %s\n", elts[i].key,
+                                      elts[i].val);
+                    }
+                }
+            }
+        }
+    }
+
+    if (rr->header_only) {
+        ap_rflush(rr);
+        return JK_OK;
+    }
+
+    /* Debug - try to get around rwrite */
+    while (ll > 0) {
+        unsigned long toSend = (ll > CHUNK_SIZE) ? CHUNK_SIZE : ll;
+        r = ap_rwrite((const char *)bb, toSend, rr);
+        /*  env->l->jkLog(env, env->l, JK_LOG_INFO,  */
+        /*     "service.write()  %ld (%ld) out of %ld \n",toSend, r, ll ); */
+        ll -= CHUNK_SIZE;
+        bb += CHUNK_SIZE;
+
+        if (toSend != r) {
+            return JK_ERR;
+        }
+
+    }
+
+    /*
+     * To allow server push. After writing full buffers
+     */
+    if (ap_rflush(rr) != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0,
+                     NULL, "mod_jk: Error flushing");
+        return JK_ERR;
+    }
+
+    return JK_OK;
+}
+
+/* ========================================================================= */
+/* Utility functions                                                         */
+/* ========================================================================= */
+
+static long jk2_get_content_length(jk_env_t *env, request_rec * r)
+{
+    if (r->clength > 0) {
+        return (long)(r->clength);
+    }
+    else {
+        char *lenp = (char *)apr_table_get(r->headers_in, "Content-Length");
+
+        if (lenp) {
+            long rc = atol(lenp);
+            if (rc > 0) {
+                return rc;
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int JK_METHOD jk2_init_ws_service(jk_env_t *env, jk_ws_service_t *s,
+                                         jk_worker_t *worker, void *serverObj)
+{
+    char *ssl_temp = NULL;
+    jk_workerEnv_t *workerEnv;
+    request_rec *r = serverObj;
+    int need_content_length_header = JK_FALSE;
+
+    workerEnv = worker->workerEnv;
+    /* Common initialization */
+    /* XXX Probably not needed, we're duplicating */
+    jk2_requtil_initRequest(env, s);
+
+
+    s->ws_private = r;
+    s->response_started = JK_FALSE;
+    s->read_body_started = JK_FALSE;
+    s->workerEnv = workerEnv;
+
+    s->jvm_route = NULL;        /* Used for sticky session routing */
+
+    s->auth_type = NULL_FOR_EMPTY(r->ap_auth_type);
+    s->remote_user = NULL_FOR_EMPTY(r->user);
+
+    s->protocol = r->protocol;
+    s->remote_host = (char *)ap_get_remote_host(r->connection,
+                                                r->per_dir_config,
+                                                REMOTE_HOST, NULL);
+    s->remote_host = NULL_FOR_EMPTY(s->remote_host);
+    s->remote_addr = NULL_FOR_EMPTY(r->connection->remote_ip);
+
+    /* get server name like in jk 1.2.x */
+    s->server_name = (char *)ap_get_server_name(r);
+
+    /* get the real port (otherwise redirect failed) */
+    s->server_port = r->connection->local_addr->port;
+
+    s->server_software = (char *)ap_get_server_version();
+
+    s->method = (char *)r->method;
+    s->content_length = jk2_get_content_length(env, r);
+    s->is_chunked = r->read_chunked;
+    s->no_more_chunks = 0;
+    s->query_string = r->args;
+
+    s->startTime = r->request_time;
+    /*
+     * The 2.2 servlet spec errata says the uri from
+     * HttpServletRequest.getRequestURI() should remain encoded.
+     * [http://java.sun.com/products/servlet/errata_042700.html]
+     *
+     * We use JkOptions to determine which method to be used
+     *
+     * ap_escape_uri is the latest recommanded but require
+     *               some java decoding (in TC 3.3 rc2)
+     *
+     * unparsed_uri is used for strict compliance with spec and
+     *              old Tomcat (3.2.3 for example)
+     *
+     * uri is use for compatibilty with mod_rewrite with old Tomcats
+     */
+
+    switch (workerEnv->options & JK_OPT_FWDURIMASK) {
+
+    case JK_OPT_FWDURICOMPATUNPARSED:
+        s->req_uri = r->unparsed_uri;
+        if (s->req_uri != NULL) {
+            char *query_str = strchr(s->req_uri, '?');
+            if (query_str != NULL) {
+                *query_str = 0;
+            }
+        }
+
+        break;
+
+    case JK_OPT_FWDURICOMPAT:
+        s->req_uri = r->uri;
+        break;
+
+    case JK_OPT_FWDURIESCAPED:
+        s->req_uri = ap_escape_uri(r->pool, r->uri);
+        break;
+
+    default:
+        return JK_ERR;
+    }
+
+    s->is_ssl = JK_FALSE;
+    s->ssl_cert = NULL;
+    s->ssl_cert_len = 0;
+    s->ssl_cipher = NULL;       /* required by Servlet 2.3 Api, 
+                                   allready in original ajp13 */
+    s->ssl_session = NULL;
+    s->ssl_key_size = -1;       /* required by Servlet 2.3 Api, added in jtc */
+
+    if (workerEnv->ssl_enable || workerEnv->envvars_in_use) {
+        ap_add_common_vars(r);
+
+        if (workerEnv->ssl_enable) {
+            ssl_temp =
+                (char *)apr_table_get(r->subprocess_env,
+                                      workerEnv->https_indicator);
+            if (ssl_temp && !strcasecmp(ssl_temp, "on")) {
+                s->is_ssl = JK_TRUE;
+                s->ssl_cert =
+                    (char *)apr_table_get(r->subprocess_env,
+                                          workerEnv->certs_indicator);
+                if (s->ssl_cert) {
+                    s->ssl_cert_len = strlen(s->ssl_cert);
+                }
+                /* Servlet 2.3 API */
+                s->ssl_cipher =
+                    (char *)apr_table_get(r->subprocess_env,
+                                          workerEnv->cipher_indicator);
+                s->ssl_session =
+                    (char *)apr_table_get(r->subprocess_env,
+                                          workerEnv->session_indicator);
+
+                if (workerEnv->options & JK_OPT_FWDKEYSIZE) {
+                    /* Servlet 2.3 API */
+                    ssl_temp = (char *)apr_table_get(r->subprocess_env,
+                                                     workerEnv->
+                                                     key_size_indicator);
+                    if (ssl_temp)
+                        s->ssl_key_size = atoi(ssl_temp);
+                }
+            }
+        }
+
+#ifdef USE_APRTABLES
+        /* We can't do that - the filtering should happen in
+           common to enable that.
+
+           jk2_map_aprtable_factory( workerEnv->env, s->pool,
+           &s->attributes,
+           "map", "aprtable" );
+           s->attributes->init( NULL, s->attributes, 0, XXX);
+         */
+        jk2_map_default_create(env, &s->attributes, s->pool);
+#else
+        jk2_map_default_create(env, &s->attributes, s->pool);
+#endif
+
+        if (workerEnv->envvars_in_use) {
+            int envCnt = workerEnv->envvars->size(env, workerEnv->envvars);
+            int i;
+
+            for (i = 0; i < envCnt; i++) {
+                char *name =
+                    workerEnv->envvars->nameAt(env, workerEnv->envvars, i);
+                char *val = (char *)apr_table_get(r->subprocess_env, name);
+                if (val == NULL) {
+                    val =
+                        workerEnv->envvars->valueAt(env, workerEnv->envvars,
+                                                    i);
+                }
+                s->attributes->put(env, s->attributes, name, val, NULL);
+            }
+        }
+    }
+
+#ifdef USE_APRTABLES
+    jk2_map_aprtable_factory(env, s->pool,
+                             (void *)&s->headers_in, "map", "aprtable");
+    s->headers_in->init(env, s->headers_in, 0, r->headers_in);
+#else
+    jk2_map_default_create(env, &s->headers_in, s->pool);
+
+    if (r->headers_in && apr_table_elts(r->headers_in)) {
+        const apr_array_header_t *t = apr_table_elts(r->headers_in);
+        if (t && t->nelts) {
+            int i;
+
+            apr_table_entry_t *elts = (apr_table_entry_t *) t->elts;
+
+            for (i = 0; i < t->nelts; i++) {
+                s->headers_in->add(env, s->headers_in,
+                                   elts[i].key, elts[i].val);
+            }
+        }
+    }
+#endif
+
+    if (!s->is_chunked && s->content_length == 0) {
+        /* XXX if r->contentLength == 0 I assume there's no header
+           or a header with '0'. In the second case, put will override it 
+         */
+        s->headers_in->put(env, s->headers_in, "content-length", "0", NULL);
+    }
+
+#ifdef USE_APRTABLES
+    jk2_map_aprtable_factory(env, s->pool, (void *)&s->headers_out,
+                             "map", "aprtable");
+    s->headers_in->init(env, s->headers_out, 0, r->headers_out);
+#else
+    jk2_map_default_create(env, &s->headers_out, s->pool);
+#endif
+
+    return JK_OK;
+}
+
+/*
+ * If the servlet engine didn't consume all of the
+ * request data, consume and discard all further
+ * characters left to read from client
+ *
+ *  XXX Is it the right thing to do ????? Why spend the
+ *  bandwith, the servlet decided not to read the POST then
+ *  jk shouldn't do it instead, and the user should get the
+ *  error message !
+ */
+static void JK_METHOD jk2_service_apache2_afterRequest(jk_env_t *env,
+                                                       jk_ws_service_t *s)
+{
+
+    if (s->content_read < s->content_length ||
+        (s->is_chunked && !s->no_more_chunks)) {
+
+        request_rec *r = s->ws_private;
+
+        char *buff = apr_palloc(r->pool, 2048);
+        if (buff != NULL) {
+            int rd;
+            while ((rd = ap_get_client_block(r, buff, 2048)) > 0) {
+                s->content_read += rd;
+            }
+        }
+    }
+    if (s->realWorker) {
+        struct jk_worker *w = s->realWorker;
+        if (w != NULL && w->channel != NULL
+            && w->channel->afterRequest != NULL) {
+            w->channel->afterRequest(env, w->channel, w, NULL, s);
+        }
+    }
+}
+
+int JK_METHOD jk2_service_apache2_init(jk_env_t *env, jk_ws_service_t *s)
+{
+    if (s == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "service.init() NullPointerException\n");
+        return JK_ERR;
+    }
+
+    s->head = jk2_service_apache2_head;
+    s->read = jk2_service_apache2_read;
+    s->write = jk2_service_apache2_write;
+    s->init = jk2_init_ws_service;
+    s->afterRequest = jk2_service_apache2_afterRequest;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/server/apache2/mod_jk2.c b/connectors/jk/native2/server/apache2/mod_jk2.c
new file mode 100644
index 0000000..c24ec0c
--- /dev/null
+++ b/connectors/jk/native2/server/apache2/mod_jk2.c
@@ -0,0 +1,925 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Apache 2 plugin for Jakarta/Tomcat                         *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ *              Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+/*
+ * mod_jk2: keeps all servlet/jakarta related ramblings together.
+ */
+
+
+#include "jk_apache2.h"
+#include "scoreboard.h"
+#include "ap_mpm.h"
+
+#include "util_script.h"
+
+#ifdef WIN32
+static char file_name[_MAX_PATH];
+#endif
+
+/* This is used to ensure that jk2_create_dir_config creates unique
+ * dir mappings. This prevents vhost configs as configured through
+ * httpd.conf from getting crossed.
+ */
+static int dirCounter = 0;
+
+#define JK_HANDLER          ("jakarta-servlet2")
+#define JK_MAGIC_TYPE       ("application/x-jakarta-servlet2")
+
+module AP_MODULE_DECLARE_DATA jk2_module;
+
+/* In apache1.3 this is reset when the module is reloaded ( after
+ * config. No good way to discover if it's the first time or not.
+ */
+static jk_workerEnv_t *workerEnv;
+
+/* ==================== Options setters ==================== */
+
+/*
+ * The JK2 module command processors. The options can be specified
+ * in a properties file or in httpd.conf, depending on user's taste.
+ *
+ * There is absolutely no difference from the point of view of jk,
+ * but apache config tools might prefer httpd.conf and the extra
+ * information included in the command descriptor. It also have
+ * a 'natural' feel, and is consistent with all other apache
+ * settings and modules. 
+ *
+ * Properties file are easier to parse/generate from java, and
+ * allow identical configuration for all servers. We should have
+ * code to generate the properties file or use the wire protocol,
+ * and make all those properties part of server.xml or jk's
+ * java-side configuration. This will give a 'natural' feel for
+ * those comfortable with the java side.
+ *
+ * The only exception is webapp definition, where in the near
+ * future you can expect a scalability difference between the
+ * 2 choices. If you have a large number of apps/vhosts you
+ * _should_ use the apache style, that makes use of the
+ * internal apache mapper ( known to scale to very large number
+ * of hosts ). The internal jk mapper uses linear search, ( will
+ * eventually use hash tables, when we add support for apr_hash ),
+ * and is nowhere near the apache mapper.
+ */
+
+/*
+ * JkSet name value
+ *
+ * Set jk options. Same as using workers.properties.
+ * Common properties: see workers.properties documentation
+ */
+static const char *jk2_set2(cmd_parms * cmd, void *per_dir,
+                            const char *name, char *value)
+{
+    server_rec *s = cmd->server;
+    jk_env_t *env = workerEnv->globalEnv;
+    int rc;
+
+    rc = workerEnv->config->setPropertyString(env, workerEnv->config,
+                                              (char *)name, value);
+    if (rc != JK_OK) {
+        ap_log_perror(APLOG_MARK, APLOG_NOTICE, 0, cmd->temp_pool,
+                      "mod_jk2: Unrecognized option %s %s", name, value);
+    }
+
+    return NULL;
+}
+
+/*
+ * JkSet2 oname property value
+ *
+ * Set jk options. 
+ */
+static const char *jk2_set3(cmd_parms * cmd, void *per_dir,
+                            const char *name, char *property, char *value)
+{
+    server_rec *s = cmd->server;
+    jk_env_t *env = workerEnv->globalEnv;
+    int rc;
+    jk_bean_t *mbean;
+
+    if (name == NULL || property == NULL || value == NULL) {
+        ap_log_perror(APLOG_MARK, APLOG_NOTICE, 0, cmd->temp_pool,
+                      "mod_jk2: Null option in JkSet2");
+        return NULL;
+    }
+
+    mbean = env->getBean(env, name);
+    if (mbean == NULL) {
+        ap_log_perror(APLOG_MARK, APLOG_NOTICE, 0, cmd->temp_pool,
+                      "mod_jk2: Creating object %s", name);
+        mbean = env->createBean(env, workerEnv->config->pool, (char *)name);
+    }
+
+    if (mbean == NULL) {
+        /* Can't create it, save the value in our map */
+        workerEnv->config->setProperty(env, workerEnv->config,
+                                       workerEnv->config->mbean, (char *)name,
+                                       value);
+        return NULL;
+    }
+
+    if (mbean->settings == NULL)
+        jk2_map_default_create(env, &mbean->settings,
+                               workerEnv->config->pool);
+
+    rc = workerEnv->config->setProperty(env, workerEnv->config, mbean,
+                                        property, value);
+
+    if (rc != JK_OK) {
+        ap_log_perror(APLOG_MARK, APLOG_NOTICE, 0, cmd->temp_pool,
+                      "mod_jk2: Unrecognized option %s %s %s", name, property,
+                      value);
+    }
+
+    return NULL;
+}
+
+/**
+ * Set a property associated with a URI, using native <Location> 
+ * directives.
+ *
+ * This is used if you want to use the native mapping and
+ * integrate better into apache.
+ *
+ * Same behavior can be achieved by using uri.properties and/or JkSet.
+ * 
+ * Example:
+ *   <VirtualHost foo.com>
+ *      <Location /examples>
+ *         JkUriSet worker ajp13
+ *      </Location>
+ *   </VirtualHost>
+ *
+ * This is the best way to define a webapplication in apache. It is
+ * scalable ( using apache native optimizations, you can have hundreds
+ * of hosts and thousands of webapplications ), 'natural' to any
+ * apache user.
+ *
+ * XXX This is a special configuration, for most users just use
+ * the properties files.
+ */
+static const char *jk2_uriSet(cmd_parms * cmd, void *per_dir,
+                              const char *name, const char *val)
+{
+    jk_uriEnv_t *uriEnv = (jk_uriEnv_t *)per_dir;
+
+    char *tmp_virtual = NULL;
+    char *tmp_full_url = NULL;
+    server_rec *s = cmd->server;
+
+    uriEnv->mbean->setAttribute(workerEnv->globalEnv, uriEnv->mbean,
+                                (char *)name, (void *)val);
+
+    /*
+     * all of the objects that get passed in now are unique. create_dir adds a incrementing counter to the
+     * uri that is used to create the object!
+     * Here we must now 'fix' the content of the object passed in.
+     * Apache doesn't care what we do here as it has the reference to the unique object that has been
+     * created. What we need to do is ensure that the data given to mod_jk2 is correct. Hopefully in the long run
+     * we can ignore some of the mod_jk2 details...
+     */
+
+    /* if applicable we will set the hostname etc variables. */
+    if (s->is_virtual && s->server_hostname != NULL &&
+        (uriEnv->virtual == NULL || !strchr(uriEnv->virtual, ':') ||
+         uriEnv->port != s->port)) {
+        tmp_virtual = (char *)apr_pcalloc(cmd->pool,
+                                          sizeof(char *) *
+                                          (strlen(s->server_hostname) + 8));
+        tmp_full_url =
+            (char *)apr_pcalloc(cmd->pool,
+                                sizeof(char *) * (strlen(s->server_hostname) +
+                                                  strlen(uriEnv->uri) + 8));
+        /* do not pass the hostname:0/ scheme */
+        if (s->port) {
+            sprintf(tmp_virtual, "%s:%d", s->server_hostname, s->port);
+            sprintf(tmp_full_url, "%s:%d%s", s->server_hostname, s->port,
+                    uriEnv->uri);
+        }
+        else {
+            strcpy(tmp_virtual, s->server_hostname);
+            strcpy(tmp_full_url, s->server_hostname);
+            strcat(tmp_full_url, uriEnv->uri);
+        }
+
+        uriEnv->mbean->setAttribute(workerEnv->globalEnv, uriEnv->mbean,
+                                    "uri", tmp_full_url);
+        uriEnv->mbean->setAttribute(workerEnv->globalEnv, uriEnv->mbean,
+                                    "path", cmd->path);
+
+        uriEnv->name = tmp_virtual;
+        uriEnv->virtual = tmp_virtual;
+
+    } else {
+        /*
+         * The jk2_create_dir_config added an id to uri and  path
+         * we have to correct it here.
+         */
+
+        uriEnv->mbean->setAttribute(workerEnv->globalEnv, uriEnv->mbean,
+                                    "uri", cmd->path);
+    }
+
+    /* now lets actually add the parameter set in the <Location> block */
+    uriEnv->mbean->setAttribute(workerEnv->globalEnv, uriEnv->mbean,
+                                (char *)name, (void *)val);
+
+    return NULL;
+}
+
+/* Command table.
+ */
+static const command_rec jk2_cmds[] = {
+    /* This is the 'main' directive for tunning jk2. It takes 2 parameters,
+       and it behaves _identically_ as a setting in workers.properties.
+     */
+    AP_INIT_TAKE2("JkSet", jk2_set2, NULL, RSRC_CONF,
+                  "Set a jk property, 2 parameters - objectName.property value"),
+    AP_INIT_TAKE3("JkSet2", jk2_set3, NULL, RSRC_CONF,
+                  "Set a jk property, 3 parameters - objectName property value"),
+    AP_INIT_TAKE2("JkUriSet", jk2_uriSet, NULL, ACCESS_CONF,
+                  "Defines a jk property associated with a Location"),
+    NULL
+};
+
+static void *jk2_create_dir_config(apr_pool_t * p, char *path)
+{
+    /* We don't know the vhost yet - so path is not
+     * unique. We'll have to generate a unique name
+     */
+    char *tmp = NULL;
+    int a = 10;
+    jk_bean_t *jkb;
+    jk_uriEnv_t *newUri;
+
+    if (!path)
+        return NULL;
+
+    a = strlen(path) + 10;
+    /* Original patch: a * sizeof( char * ) - that's weird, we only use a chars, not char*
+       Maybe I wrote too much java...
+     */
+    tmp = (char *)apr_pcalloc(p, a);
+    sprintf(tmp, "%s-%d", path, dirCounter++);
+    /* I changed the default to /, otherwise it complains */
+
+    jkb = workerEnv->globalEnv->createBean2(workerEnv->globalEnv,
+                                            workerEnv->pool, "uri", tmp);
+    newUri = jkb->object;
+
+    newUri->workerEnv = workerEnv;
+    newUri->mbean->setAttribute(workerEnv->globalEnv, newUri->mbean, "path",
+                                tmp);
+    /* I'm hoping that setting the id won't break anything. I havn't noticed it breaking anything. */
+    newUri->mbean->id = (dirCounter - 1);
+    /* this makes the display in the status display make more sense */
+    newUri->mbean->localName = path;
+
+    return newUri;
+}
+
+
+static void *jk2_merge_dir_config(apr_pool_t * p, void *childv, void *parentv)
+{
+    jk_uriEnv_t *child = (jk_uriEnv_t *)childv;
+    jk_uriEnv_t *parent = (jk_uriEnv_t *)parentv;
+    jk_uriEnv_t *winner = NULL;
+    char *hostchild;
+    char *hostparent;
+
+    if (child == NULL || child->uri == NULL || child->workerName == NULL) {
+        winner = parent;
+    }
+    else if (parent == NULL || parent->uri == NULL
+             || parent->workerName == NULL) {
+        winner = child;
+        /* interresting bit... so far they are equal ... */
+    }
+    else if (strlen(parent->uri) > strlen(child->uri)) {
+        winner = parent;
+    }
+    else if (strlen(parent->uri) == strlen(child->uri)) {
+        /* Try the virtual host to decide */
+        hostchild = child->mbean->getAttribute(workerEnv->globalEnv, child->mbean,"host");
+        hostparent = parent->mbean->getAttribute(workerEnv->globalEnv, parent->mbean,"host");
+        if (hostchild == NULL)
+            winner = parent;
+        if (hostparent == NULL)
+            winner = child;
+        if (winner == NULL) {
+            if (strlen(hostchild) > strlen(hostparent))
+                winner = child;
+            else
+                winner = parent;
+        }
+    }
+    else {
+        winner = child;
+    }
+
+    /* Do we merge loser into winner - i.e. inherit properties ? */
+
+    ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, NULL, 
+                  "mod_jk2 Merging %s %s winner: %s\n",
+            (child == NULL || child->uri == NULL) ? "" : child->uri,
+            (parent == NULL || parent->uri == NULL) ? "" : parent->uri,
+            (winner == child) ? "parent" : "child" );
+
+
+    return (void *)winner;
+
+}
+
+/** Basic initialization for jk2.
+ */
+static void jk2_create_workerEnv(apr_pool_t * p, server_rec * s)
+{
+    jk_env_t *env;
+    jk_logger_t *l;
+    jk_pool_t *globalPool;
+    jk_bean_t *jkb;
+
+    jk2_pool_apr_create(NULL, &globalPool, NULL, p);
+
+    /** Create the global environment. This will register the default
+        factories
+    */
+    env = jk2_env_getEnv(NULL, globalPool);
+
+    /* Optional. Register more factories ( or replace existing ones ) */
+    /* Init the environment. */
+
+    /* Create the logger */
+#ifdef NO_APACHE_LOGGER
+    jkb = env->createBean2(env, env->globalPool, "logger.file", "");
+    env->alias(env, "logger.file:", "logger");
+    env->alias(env, "logger.file:", "logger:");
+    l = jkb->object;
+#else
+    env->registerFactory(env, "logger.apache2", jk2_logger_apache2_factory);
+    jkb = env->createBean2(env, env->globalPool, "logger.apache2", "");
+    env->alias(env, "logger.apache2:", "logger");
+    l = jkb->object;
+    l->logger_private = s;
+#endif
+
+    env->l = l;
+
+#ifdef WIN32
+    env->soName = env->globalPool->pstrdup(env, env->globalPool, file_name);
+
+    if (env->soName == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "Error creating env->soName\n");
+        return;
+    }
+#else
+    env->soName = NULL;
+#endif
+    /* We should make it relative to JK_HOME or absolute path.
+       ap_server_root_relative(cmd->pool,opt); */
+
+    /* Create the workerEnv */
+    jkb = env->createBean2(env, env->globalPool, "workerEnv", "");
+    workerEnv = jkb->object;
+/*     workerEnv->logger_name= "logger.apache2"; */
+    env->alias(env, "workerEnv:", "workerEnv");
+
+    if (workerEnv == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "Error creating workerEnv\n");
+        return;
+    }
+    workerEnv->initData->add(env, workerEnv->initData, "serverRoot",
+                             workerEnv->pool->pstrdup(env, workerEnv->pool,
+                                                      ap_server_root));
+    env->l->jkLog(env, env->l, JK_LOG_INFO, "Set serverRoot %s\n",
+                  ap_server_root);
+
+    /* Local initialization */
+    workerEnv->_private = s;
+}
+
+/** Create default jk_config. XXX This is mostly server-independent,
+    all servers are using something similar - should go to common.
+
+    This is the first thing called ( or should be )
+ */
+static void *jk2_create_config(apr_pool_t * p, server_rec * s)
+{
+    jk_uriEnv_t *newUri;
+    jk_bean_t *jkb;
+
+    if (workerEnv == NULL) {
+        jk2_create_workerEnv(p, s);
+    }
+    if (s->is_virtual) {
+        /* Virtual host */
+        ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, p,
+                      "mod_jk2 Create config for virtual host %s",
+                      s->server_hostname);
+    }
+    else {
+        ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, p,
+                      "mod_jk2 Create config for default server %s",
+                      s->server_hostname);
+    }
+
+    jkb = workerEnv->globalEnv->createBean2(workerEnv->globalEnv,
+                                            workerEnv->pool, "uri", "");
+    newUri = jkb->object;
+
+    newUri->workerEnv = workerEnv;
+
+    return newUri;
+}
+
+
+
+/** Standard apache callback, merge jk options specified in 
+    <Host> context. Used to set per virtual host configs
+ */
+static void *jk2_merge_config(apr_pool_t * p, void *basev, void *overridesv)
+{
+    jk_uriEnv_t *overrides = (jk_uriEnv_t *)overridesv;
+
+    ap_log_perror(APLOG_MARK, APLOG_DEBUG, 0, p, "mod_jk2 Merging workerEnv");
+
+
+    /* The 'mountcopy' option should be implemented in common.
+     */
+    return overrides;
+}
+
+static apr_status_t jk2_shutdown(void *data)
+{
+    jk_env_t *env;
+    if (workerEnv) {
+        env = workerEnv->globalEnv;
+
+/*         env->l->jkLog(env, env->l, JK_LOG_INFO, */
+/*                       "mod_jk2 Shutting down\n"); */
+        workerEnv->close(env, workerEnv);
+        workerEnv = NULL;
+    }
+    return APR_SUCCESS;
+}
+
+
+/** Initialize jk, using worker.properties. 
+    We also use apache commands ( JkWorker, etc), but this use is 
+    deprecated, as we'll try to concentrate all config in
+    workers.properties, urimap.properties, and ajp14 autoconf.
+    
+    Apache config will only be used for manual override, using 
+    SetHandler and normal apache directives ( but minimal jk-specific
+    stuff )
+*/
+static char *jk2_init(jk_env_t *env, apr_pool_t * pconf,
+                      jk_workerEnv_t *workerEnv, server_rec * s)
+{
+
+    ap_mpm_query(AP_MPMQ_MAX_DAEMONS, &workerEnv->maxDaemons);
+
+    workerEnv->init(env, workerEnv);
+    workerEnv->server_name = (char *)ap_get_server_version();
+
+    apr_pool_cleanup_register(pconf, NULL, jk2_shutdown,
+                              apr_pool_cleanup_null);
+    return NULL;
+}
+
+/* Apache will first validate the config then restart.
+   That will unload all .so modules - including ourself.
+   Keeping 'was_initialized' in workerEnv is pointless, since both
+   will disapear.
+*/
+static int jk2_apache2_isValidating(apr_pool_t * gPool,
+                                    apr_pool_t ** mainPool)
+{
+    apr_pool_t *tmpPool = NULL;
+    void *data = NULL;
+    int i;
+
+    for (i = 0; i < 10; i++) {
+        tmpPool = apr_pool_parent_get(gPool);
+        if (tmpPool == NULL) {
+            /* fprintf(stderr, "XXX Found Root pool %#lx\n", gPool ); */
+            break;
+        }
+        gPool = tmpPool;
+    }
+
+    if (tmpPool != NULL) {
+        /* We can't detect the root pool */
+        /* fprintf(stderr, "XXX Can't find root pool\n" ); */
+        return JK_ERR;
+    }
+    if (mainPool != NULL)
+        *mainPool = gPool;
+
+    /* We have a global pool ! */
+    apr_pool_userdata_get(&data, "mod_jk2_init", gPool);
+    if (data == NULL) {
+        return JK_OK;
+    }
+    else {
+        return JK_ERR;
+    }
+}
+
+static int jk2_post_config(apr_pool_t * pconf,
+                           apr_pool_t * plog,
+                           apr_pool_t * ptemp, server_rec * s)
+{
+    apr_pool_t *gPool = NULL;
+    int rc;
+    jk_env_t *env;
+
+    if (s->is_virtual)
+        return OK;
+
+    /* Other apache 2.0 modules add version info at post_config */
+    ap_add_version_component(pconf, JK_EXPOSED_VERSION);
+
+    env = workerEnv->globalEnv;
+
+    rc = jk2_apache2_isValidating(plog, &gPool);
+
+    env->setAprPool(env, gPool);
+
+    if (rc == JK_OK && gPool != NULL) {
+        /* This is the first step */
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "mod_jk2.post_config() first invocation\n");
+
+        apr_pool_userdata_set("INITOK", "mod_jk2_init", NULL, gPool);
+        return OK;
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO,
+                  "mod_jk2.post_config() second invocation\n");
+    workerEnv->parentInit(env, workerEnv);
+
+    return OK;
+}
+
+/** Standard apache callback, initialize jk.
+ */
+static void jk2_child_init(apr_pool_t * pconf, server_rec * s)
+{
+    apr_proc_t proc;
+    jk_uriEnv_t *serverEnv = (jk_uriEnv_t *)
+        ap_get_module_config(s->module_config, &jk2_module);
+    jk_env_t *env;
+
+    if (workerEnv == NULL)
+        workerEnv = serverEnv->workerEnv;
+
+    env = workerEnv->globalEnv;
+
+    if (!workerEnv->childProcessId)
+        workerEnv->childProcessId = getpid();
+
+    proc.pid = workerEnv->childProcessId;
+
+    /* detect if scoreboard exists
+     */
+    if (!ap_exists_scoreboard_image()) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "jk2_init() Scoreboard image does not exists %d\n",
+                      proc.pid);
+        workerEnv->childId = -2;
+    }
+    else
+        workerEnv->childId = find_child_by_pid(&proc);
+    /* Avoid looking again
+     *  and fix the mpm_winnt reporting 0 daemons.
+     */
+    if (workerEnv->childId == -1) {
+        /* If the server max daemons are less then 2
+         * this is the single child mpm.
+         * the WINNT mpm has a bug returning 0 instead 1
+         */
+        if (workerEnv->maxDaemons < 2) {
+            workerEnv->childId = proc.pid;
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "jk2_init() Setting scoreboard slot 0 for child %d\n",
+                          proc.pid);
+        }
+        else {
+             /*
+              * try again several times, there's a
+              * race condition here where jk2_child_init gets
+              * called before make_child completes.
+              */
+             int counter = 0;
+             while (counter++ < 50 && workerEnv->childId == -1) {
+                 env->l->jkLog(env, env->l, JK_LOG_INFO,
+                     "jk2_child_init() child %d not in scoreboard yet, spin %d\n", 
+                     proc.pid, counter);
+                 apr_sleep((apr_interval_time_t)10);
+                 workerEnv->childId = find_child_by_pid(&proc);
+             }
+             if (workerEnv->childId == -1) {
+                 env->l->jkLog(env, env->l, JK_LOG_ERROR, 
+                    "jk2_init() Can't find child %d in any of the %d scoreboard slots\n",
+                     proc.pid, workerEnv->maxDaemons);
+                 workerEnv->childId = -2;
+             }
+        }
+    }
+    else {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jk2_init() Found child %d in scoreboard slot %d\n",
+                      proc.pid, workerEnv->childId);
+    }
+
+    if (!workerEnv->was_initialized) {
+        workerEnv->was_initialized = JK_TRUE;
+
+        jk2_init(env, pconf, workerEnv, s);
+
+        if (workerEnv->childId > 0)
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "mod_jk2 child %d initialized\n",
+                          workerEnv->childId);
+    }
+}
+
+
+/* ========================================================================= */
+/* The JK module handlers                                                    */
+/* ========================================================================= */
+
+/** Main service method, called to forward a request to tomcat
+ */
+static int jk2_handler(request_rec * r)
+{
+    int rc;
+    jk_worker_t *worker = NULL;
+    jk_uriEnv_t *uriEnv;
+    jk_env_t *env;
+
+    jk_ws_service_t *s = NULL;
+    jk_pool_t *rPool = NULL;
+    int rc1;
+
+    uriEnv = ap_get_module_config(r->request_config, &jk2_module);
+    if (uriEnv == NULL)
+        uriEnv = ap_get_module_config(r->per_dir_config, &jk2_module);
+
+    /* not for me, try next handler */
+    if (uriEnv == NULL || strcmp(r->handler, JK_HANDLER) != 0)
+        return DECLINED;
+
+    /* If this is a proxy request, we'll notify an error */
+    if (r->proxyreq) {
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    /* Get an env instance */
+    env = workerEnv->globalEnv->getEnv(workerEnv->globalEnv);
+
+    /* Set up r->read_chunked flags for chunked encoding, if present */
+    if (rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "mod_jk2.handler() Can't setup client block %d\n", rc);
+        workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+        return rc;
+    }
+
+    worker = uriEnv->worker;
+
+    if (worker == NULL && uriEnv->workerName != NULL) {
+        worker = env->getByName(env, uriEnv->workerName);
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "mod_jk2.handler() finding worker for %#lx %#lx %s\n",
+                      worker, uriEnv, uriEnv->workerName);
+        uriEnv->worker = worker;
+    }
+
+    if (worker == NULL || worker->mbean == NULL
+        || worker->mbean->localName == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "mod_jk2.handle() No worker for %s\n", r->uri);
+        workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    if (uriEnv->mbean->debug > 0)
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "mod_jk2.handler() serving %s with %#lx %#lx %s\n",
+                      uriEnv->mbean->localName, worker, worker->mbean,
+                      worker->mbean->localName);
+
+    /* Get a pool for the request XXX move it in workerEnv to
+       be shared with other server adapters */
+    rPool = worker->rPoolCache->get(env, worker->rPoolCache);
+    if (rPool == NULL) {
+        rPool =
+            worker->mbean->pool->create(env, worker->mbean->pool,
+                                        HUGE_POOL_SIZE);
+        if (uriEnv->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "mod_jk2.handler(): new rpool %#lx\n", rPool);
+    }
+
+    s = (jk_ws_service_t *)rPool->calloc(env, rPool, sizeof(jk_ws_service_t));
+
+    /* XXX we should reuse the request itself !!! */
+    jk2_service_apache2_init(env, s);
+
+    s->pool = rPool;
+    s->init(env, s, worker, r);
+
+    /* reset the reco_status, will be set to INITED in LB mode */
+    s->reco_status = RECO_NONE;
+
+    s->is_recoverable_error = JK_FALSE;
+    s->uriEnv = uriEnv;
+
+    /* env->l->jkLog(env, env->l, JK_LOG_INFO,  */
+    /*              "mod_jk2.handler() Calling %s\n", worker->mbean->name); */
+
+    rc = worker->service(env, worker, s);
+
+    s->afterRequest(env, s);
+
+    rPool->reset(env, rPool);
+
+    rc1 = worker->rPoolCache->put(env, worker->rPoolCache, rPool);
+    if (rc1 == JK_OK) {
+        rPool = NULL;
+    }
+    if (rPool != NULL) {
+        rPool->close(env, rPool);
+    }
+
+    if (rc == JK_OK) {
+        workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+        return OK;              /* NOT r->status, even if it has changed. */
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                  "mod_jk2.handler() Error connecting to tomcat %d, status %d\n",
+                  rc, s->status);
+
+    workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+
+    /* In case of error, if service() set a status code, send it back */
+    /* Else fallback to HTTP_INTERNAL_SERVER_ERROR (500). */
+    if (s->status != 0)
+        return s->status;
+    else
+        return HTTP_INTERNAL_SERVER_ERROR;
+}
+
+/** Use the internal mod_jk2 mappings to find if this is a request for
+ *    tomcat and what worker to use. 
+ */
+static int jk2_translate(request_rec * r)
+{
+    jk_uriEnv_t *uriEnv;
+    jk_env_t *env;
+
+    if (r->proxyreq || workerEnv == NULL) {
+        return DECLINED;
+    }
+
+    /* For the JkUriSet */
+    uriEnv = ap_get_module_config(r->per_dir_config, &jk2_module);
+
+    /* This has been mapped to a location by apache
+     * In a previous ( experimental ) version we had a sub-map,
+     * but that's too complex for now.
+     */
+    if (uriEnv != NULL && uriEnv->workerName != NULL) {
+        /* get_env() */
+        env = workerEnv->globalEnv->getEnv(workerEnv->globalEnv);
+
+        if (uriEnv->mbean->debug > 0)
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "PerDir mapping  %s=%s\n", r->uri,
+                          uriEnv->workerName);
+
+        ap_set_module_config(r->request_config, &jk2_module, uriEnv);
+        r->handler = JK_HANDLER;
+        workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+
+        /* This could be a sub-request, possibly from mod_dir */
+        if (r->main) {
+            ap_set_module_config(r->main->request_config, &jk2_module,
+                                 uriEnv);
+            r->main->handler = JK_HANDLER;
+        }
+
+        return OK;
+    }
+
+    return DECLINED;
+}
+
+/* XXX Can we use type checker step to set our stuff ? */
+
+/* bypass the directory_walk and file_walk for non-file requests */
+static int jk2_map_to_storage(request_rec * r)
+{
+    jk_uriEnv_t *uriEnv;
+    jk_env_t *env;
+    const char *ptr;
+
+    if (r->proxyreq || workerEnv == NULL) {
+        return DECLINED;
+    }
+
+    /* If already mapped by translate just returns OK */
+    uriEnv = ap_get_module_config(r->request_config, &jk2_module);
+    if (uriEnv != NULL && uriEnv->workerName != NULL)
+        return OK;
+
+    /* From something like [uri:/examples/STAR] in workers2.properties */
+    env = workerEnv->globalEnv->getEnv(workerEnv->globalEnv);
+    ptr = ap_get_server_name(r);
+    if (strlen(ptr) > 1024 - 12) {
+        /* That is probably an invalid request, DECLINED could display jsp source code. */
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "jk2_map_to_storage Host too big %s\n", ptr);
+        return HTTP_BAD_REQUEST;
+    }
+    uriEnv = workerEnv->uriMap->mapUri(env, workerEnv->uriMap,
+                                       ptr, ap_get_server_port(r), r->uri);
+
+    if (uriEnv != NULL && uriEnv->workerName != NULL) {
+        ap_set_module_config(r->request_config, &jk2_module, uriEnv);
+        r->handler = JK_HANDLER;
+        workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+
+        /* This could be a sub-request, possibly from mod_dir */
+        if (r->main) {
+            ap_set_module_config(r->main->request_config, &jk2_module,
+                                 uriEnv);
+            r->main->handler = JK_HANDLER;
+        }
+
+        return OK;
+    }
+
+    workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+    return DECLINED;
+}
+
+static void jk2_register_hooks(apr_pool_t * p)
+{
+    ap_hook_handler(jk2_handler, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_post_config(jk2_post_config, NULL, NULL, APR_HOOK_MIDDLE);
+
+    /* Force the mpm to run before us and set the scoreboard image */
+    ap_hook_child_init(jk2_child_init, NULL, NULL, APR_HOOK_LAST);
+
+    ap_hook_translate_name(jk2_translate, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_map_to_storage(jk2_map_to_storage, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+module AP_MODULE_DECLARE_DATA jk2_module = {
+    STANDARD20_MODULE_STUFF,
+    jk2_create_dir_config,      /*  dir config creater */
+    jk2_merge_dir_config,       /* dir merger --- default is to override */
+    jk2_create_config,          /* server config */
+    jk2_merge_config,           /* merge server config */
+    jk2_cmds,                   /* command ap_table_t */
+    jk2_register_hooks          /* register hooks */
+};
+
+#ifdef WIN32
+
+BOOL WINAPI DllMain(HINSTANCE hInst,    // Instance Handle of the DLL
+                    ULONG ulReason,     // Reason why NT called this DLL
+                    LPVOID lpReserved)  // Reserved parameter for future use
+{
+    GetModuleFileName(hInst, file_name, sizeof(file_name));
+    return TRUE;
+}
+
+
+#endif
diff --git a/connectors/jk/native2/server/apache2/mod_jk2.dsp b/connectors/jk/native2/server/apache2/mod_jk2.dsp
new file mode 100644
index 0000000..3562736
--- /dev/null
+++ b/connectors/jk/native2/server/apache2/mod_jk2.dsp
@@ -0,0 +1,389 @@
+# Microsoft Developer Studio Project File - Name="mod_jk2" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_jk2 - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_jk2.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_jk2.mak" CFG="mod_jk2 - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_jk2 - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_jk2 - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "mod_jk2 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MOD_JK2_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "$(APACHE2_HOME)\include" /I "$(APACHE2_HOME)\os\win32" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MOD_JK2_EXPORTS" /D "HAVE_JNI" /D "HAS_APR" /D "HAS_AP_PCRE" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0xc0a /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 libhttpd.lib libapr.lib libaprutil.lib pcre.lib pcreposix.lib wsock32.lib advapi32.lib /nologo /dll /machine:I386 /out:"Release/mod_jk2.so" /libpath:"$(APACHE2_HOME)\lib"
+
+!ELSEIF  "$(CFG)" == "mod_jk2 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MOD_JK2_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "$(APACHE2_HOME)\include" /I "$(APACHE2_HOME)\os\win32" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MOD_JK2_EXPORTS" /D "HAVE_JNI" /D "HAS_APR" /D "HAS_AP_PCRE" /FR /YX /FD /GZ /c
+# SUBTRACT CPP /X
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0xc0a /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 libhttpd.lib libapr.lib libaprutil.lib pcre.lib pcreposix.lib wsock32.lib advapi32.lib /nologo /dll /debug /machine:I386 /out:"Debug/mod_jk2.so" /pdbtype:sept /libpath:"$(APACHE2_HOME)\lib"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_jk2 - Win32 Release"
+# Name "mod_jk2 - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\jk_apache2.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel_apr_socket.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel_jni.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel_un.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_config.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_config_file.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_endpoint.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_env.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_handler_logon.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_handler_response.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jni\jk_jni_aprImpl.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_logger_apache2.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_file.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_win32.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_map_aprtable.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_msg_ajp.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_mutex.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_mutex_proc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_mutex_thread.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_objCache.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_pool_apr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_registry.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_requtil.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_service_apache2.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_shm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_signal.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_uriEnv.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_uriMap.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_user.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_vm_default.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_ajp13.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_jni.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_lb.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_run.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_status.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_workerEnv.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_jk2.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\..\include\jk_channel.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_endpoint.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_env.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_global.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_handler.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_msg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_mt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_objCache.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_registry.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_requtil.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_service.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_shm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_uriEnv.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_uriMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_vm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_workerEnv.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jni\org_apache_jk_apr_AprImpl.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_win32_message.mc
+
+!IF  "$(CFG)" == "mod_jk2 - Win32 Release"
+
+# Begin Custom Build - Creating resources from $(InputPath)
+InputDir=\WRKPLACE\PROJECTS\jtc\jk\native2\common
+InputPath=..\..\common\jk_logger_win32_message.mc
+
+"..\..\common\jk_logger_win32_message.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	mc -h $(InputDir) -r $(InputDir) $(InputPath)
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "mod_jk2 - Win32 Debug"
+
+# Begin Custom Build - Creating resources from $(InputPath)
+InputDir=\WRKPLACE\PROJECTS\jtc\jk\native2\common
+InputPath=..\..\common\jk_logger_win32_message.mc
+
+"..\..\common\jk_logger_win32_message.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	mc -h $(InputDir) -r $(InputDir) $(InputPath)
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_win32_message.rc
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/connectors/jk/native2/server/dsapi/BUILD-LINUX.txt b/connectors/jk/native2/server/dsapi/BUILD-LINUX.txt
new file mode 100644
index 0000000..7ba8bee
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/BUILD-LINUX.txt
@@ -0,0 +1,73 @@
+$Id$
+
+Prerequisites
+-------------
+
+The common jk2 code requires APR and
+PCRE. Download a suitable version of APR from here:
+
+  http://apr.apache.org/
+  
+PCRE can be found here:
+
+  http://www.pcre.org/
+  
+Now you should be able to configure and build the redirector. cd to
+jakarta-tomcat-connectors/jk/native2 and
+
+$ autoconf
+
+to generate the configure script. Now  issue a ./configure command
+similar to this:
+
+$ ./configure \
+        --with-domino=/opt/lotus/notes/50120/linux \
+        --with-dsapi=/opt/tomcat/jakarta-tomcat-connectors/jk/native2 \
+        --with-apr=/opt/tomcat/apr/apr-0.9.4 \
+        --with-apr-util=/opt/tomcat/apr/apr-util-0.9.4
+
+Adjust the paths in the above to suit your setup. Assuming configure
+reports no problems do
+
+$ make
+
+to build redirector. Again note and fix any problems that are reported.
+
+If make is successful the redirector will be in
+
+../build/jk2/dsapi/libtomcat2.so
+
+Having built the redirector refer to INSTALL-LINUX.txt for installation
+instructions.
+
+Mailing Lists
+-------------
+
+There are two mailing lists dedicated to the Domino Tomcat redirector:
+
+domino-tomcat-l:
+ http://nomen.tagish.co.uk/mailman/listinfo/domino-tomcat-l
+
+domino-tomcat-l is a general discussion list for issues with the
+redirector and also wider Tomcat/Domino integration issues such as
+calling the Domino Java API from a Tomcat servlet. I expect it to be
+fairly low volume so please subscribe if you're actively using the
+redirector. If you have an issue with the redirector please post it to
+the list where it will be seen not only by me but by other users who may
+be able to help with any problems.
+
+domino-tomcat-announce-l:
+ http://nomen.tagish.co.uk/mailman/listinfo/domino-tomcat-announce-l:
+
+domino-tomcat-announce-l is for announcements about the Domino Tomcat
+redirector. Mainly this list will be used for new releases but serious
+bugs will also be posted to it. This list will be very low volume; only
+I can post to it. I wouldn't expect more than a few posts per month. For
+this reason if you'd like to keep track of new redirector versions
+please subscribe to this list. From now on this list will be the main
+place to find out about new versions.
+<
+Please let me know if you have any problems.
+
+Andy Armstrong, <andy@tagish.com>
+
diff --git a/connectors/jk/native2/server/dsapi/BUILD-WIN32.txt b/connectors/jk/native2/server/dsapi/BUILD-WIN32.txt
new file mode 100644
index 0000000..4ca34da
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/BUILD-WIN32.txt
@@ -0,0 +1,75 @@
+$Id$
+
+Prerequisites
+-------------
+
+You need to be able to build the jk2 code which requires both APR and
+PCRE. You can get a suitable Win32 build of APR from here:
+
+  http://apr.apache.org/
+  
+and a Win32 version of PCRE can be found here:
+
+  http://gnuwin32.sourceforge.net/packages/pcre.htm
+  
+To avoid messing around with the paths in the MSVC project place the APR
+and PCRE directories in the same parent directory as jakarta-tomcat-
+connectors. Here's what I have
+
+tomcat\
+    apr\
+    apr-iconv\
+    apr-util\
+    jakarta-tomcat-connectors\
+    pcre\
+
+The project also depends on the Lotus Notes C API which can be downloaded
+here:
+
+  http://www-10.lotus.com/ldd/toolkits
+  
+Set the environment variable NOTESAPI to the directory where you've
+installed it so the MSVC project can find it.
+
+You'll also need to have a suitable (for jk2) JVM installed and
+JAVA_HOME properly set.
+
+Once you've done all that you should be able to fire up MSVC, open the
+dsapi.dsw and build it. You'll most likely want to build the
+configuration 'Release Static' which will build a standalone
+dsapi_redirector2.dll.
+
+Having built the redirector refer to INSTALL-WIN32.txt for installation
+instructions.
+
+Mailing Lists
+-------------
+
+There are two mailing lists dedicated to the Domino Tomcat redirector:
+
+domino-tomcat-l:
+ http://nomen.tagish.co.uk/mailman/listinfo/domino-tomcat-l
+
+domino-tomcat-l is a general discussion list for issues with the
+redirector and also wider Tomcat/Domino integration issues such as
+calling the Domino Java API from a Tomcat servlet. I expect it to be
+fairly low volume so please subscribe if you're actively using the
+redirector. If you have an issue with the redirector please post it to
+the list where it will be seen not only by me but by other users who may
+be able to help with any problems.
+
+domino-tomcat-announce-l:
+ http://nomen.tagish.co.uk/mailman/listinfo/domino-tomcat-announce-l:
+
+domino-tomcat-announce-l is for announcements about the Domino Tomcat
+redirector. Mainly this list will be used for new releases but serious
+bugs will also be posted to it. This list will be very low volume; only
+I can post to it. I wouldn't expect more than a few posts per month. For
+this reason if you'd like to keep track of new redirector versions
+please subscribe to this list. From now on this list will be the main
+place to find out about new versions.
+
+Please let me know if you have any problems.
+
+Andy Armstrong, <andy@tagish.com>
+
diff --git a/connectors/jk/native2/server/dsapi/BUILD.txt b/connectors/jk/native2/server/dsapi/BUILD.txt
new file mode 100644
index 0000000..80e4769
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/BUILD.txt
@@ -0,0 +1,108 @@
+Information on building mod_jk2:
+
+  Starting with 2.0.4, APR is mandatory for jk2. For Apache 2.0
+  or greater jk2 will use APR that was used to build Apache 2.0.
+  For Apache 1.3, jk2 must build APR and APR_UTIL from source. 
+
+DSO build instructions for Unix-like systems:
+
+  The compiler used to build jk2 must match the one used to build
+  Apache. You may need to set an environment variable before 
+  configuring such as CC=cc. `apxs -q CC` will tell you what 
+  compiler was used for Apache.
+
+  The most straightforward way to configure jk2 is to use apxs 
+  that comes with Apache. Linux distributions may need to have 
+  additional rpm's installed such as Apache2 devel rpm, 
+  httpd-devel or apache2-devel or for Apache 13, Apache devel 
+  rpm, httpd-devel or apache-devel depending on your 
+  distribution.
+
+  Example Apache2 build and install:
+
+    cd jakarta-tomcat-connectors/jk/native2
+    ./configure --with-apxs2=/your/path/to/apxs
+    make
+    cd ../build/jk2/apache2
+    apxs -n jk2 -i mod_jk2.so
+
+  Example Apache13 build and install:
+
+    apr and apr-util will be configured and built for you while
+    configuring and building jk2. There is no need to separately
+    configure and build them. 
+
+      cd jakarta-tomcat-connectors/jk/native2
+      ./configure --with-apxs=/your/path/to/apxs \
+                  --with-apr=/absolute/path/to/apr-source \
+                  --with-apr-util=/absolute/path/to/apr-util-source
+      make
+      cd ../build/jk2/apache13
+      apxs -n jk2 -i mod_jk2.so
+
+    NOTE: pthread support may be automatically detected and built
+    into apr. If apache13 was not built with pthread support, you
+    can either disable it by adding --disable-apr-threads while
+    configuring, or load the pthread library in httpd.conf using
+    the LoadFile directive.
+
+  Optional configure arguments (for 1.3 and 2.0):
+
+    If you want to have JNI support, add --with-jni and be sure
+    to have the JAVA_HOME environment variable point to your Java
+    Environment. This will build inprocess jni support into
+    mod_jk2.so and additionally build libjkjni.so. libjkjni.so
+    can be used by tomcat to provide support for channel unix and
+    should be installed in the apache libexec dir. Use 
+    `apxs -q LIBEXECDIR` if you are unsure of its location. 
+    Libjkjni.so will be located in the same directory as 
+    mod_jk2.so after building with this option.
+
+    If you want to have PCRE (Perl Compatible Regular
+    Expressions) support for jk2 uri directives, add --with-pcre
+    while configuring.
+
+
+Quick information on building mod_jk2 :
+
+* IIS 
+
+There is a known issue with the latest APR 1.0 and MSVC6.
+If you want to use MSVC6, please use APR 0.9.x for now.
+MSVC7 doesn't have this issue, and could be used with APR 1.0.
+
+Isapi redirector requires the following libraries to build:
+apr, apr-util, apr-iconv and pcre.
+The easiest way to obtain all those libraries is to download
+the httpd-2.0.49-win32-src.zip from http://www.apache.org/dist/httpd or
+from any of the mirror sites.
+You will only need the srclib part (apr, apr-util, apr-iconv and pcre)
+Unzip the entire srclib folder to j-t-c native2 folder.
+Now open the isapi.dsw from MSVC6 and build.
+
+Building using VS.NET:
+Make sure that the required libraries are inside native2/srclib.
+Open the idapi.dsw and select 'Yes to all' when prompted to convert the project.
+During conversion the custom build adds extra quotations for
+jk_logger_win32_message.mc. Right click on that file and select Properties.
+For Custom Build Step remove all the quotations around ${InputDir}
+and ${InputPath}.
+
+
+
+* Netware
+
+Buid the JK2 connector for NetWare platform.
+
+The current NWNGUmakefile uses the same build system as Apache2 self for NetWare target.
+Simply extract the downloaded archive, and follow the guideline which describes compilation of Apache2 self. 
+
+After you have compiled Apache2 (this is mandatory for now since the prebuild process must have finished) 
+you can simply call the makefile with 'make -f NWGNUmakefile', this builds the connector for Apache2 in a 
+release or debug subdirectory, dependent if you specify to build a debug version or not.
+
+It is recommended to use Metrowerks CodeWarrior compiler for now; although the connector builds with GCC 
+for NetWare, it is not tested yet if it works - there are known issues with a bitfied and alignment which 
+have to be solved.
+
+
diff --git a/connectors/jk/native2/server/dsapi/INSTALL-LINUX.txt b/connectors/jk/native2/server/dsapi/INSTALL-LINUX.txt
new file mode 100644
index 0000000..35b03bf
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/INSTALL-LINUX.txt
@@ -0,0 +1,93 @@
+$Id$
+
+Installing the Domino Redirector on Linux
+-----------------------------------------
+
+If necessary build libtomcat2.so as per the instructions in BUILD.txt.
+Copy libtomcat2.so into the Domino program directory (this is the
+directory, which may be called something like
+/opt/lotus/notes/50120/linux, that contains a file called libnotes.so).
+Next edit libtomcat2.properies to reflect the location of your Tomcat
+installation and place the edited file in the domino data directory
+(typically something like /opt/lotus/notesdata). libtomcat2.properties
+looks like this:
+
+  serverRoot=/usr/local/apache/tomcat
+  workersFile=conf/workers2.properties
+  tomcatStart=bin/startup.sh
+  tomcatStop=bin/shutdown.sh
+  tomcatTimeout=30000
+
+Automatically Starting Tomcat
+-----------------------------
+
+The last three lines in libtomcat2.properties provide commands that the
+redirector will use to start and stop Tomcat when the Domino http server
+starts and stops respectively. If you don't require this behaviour these
+three lines can be removed. Bear in mind that if you /do/ start Tomcat
+from the redirector it will run with the same UID as Domino.
+
+The Workers File
+----------------
+
+If necessary take the sample workers2.properties file from jakarta-
+tomcat- connectors\jk\conf and place it in the location specified in
+libtomcat2.properties (typically the conf directory of your Tomcat
+installation). Edit the file to suit your Tomcat setup.
+
+Configuring Domino
+------------------
+
+Finally we need to configure Domino to use the DSAPI extension. For those
+who are unfamiliar with Domino server configuration most of a server's
+configurable behavior is dictated by a document called the "server
+document" in a database called the "Public Name and Address Book" or
+"NAB" for short (N.B. Lotus have renamed the NAB to "Domino Directory"
+from Domino 5 onwards). Each Domino server will have a NAB (called
+names.nsf) and each NAB will have a number of server documents including
+one for the current server. If you have not previously configured a
+Domino server you may need to refer to the supplied documentation, or you
+may need to pass this document to your tame Domino administrator.
+
+Assuming you know your way around a Domino server document what we're
+going to do is actually quite simple. Open the server document for this
+server, place it in Edit mode, then locate the DSAPI section and the
+'DSAPI filter file names' field on the Internet Protocols tab, HTTP sub-
+tab. Add "tomcat2" to the DSAPI field, then save and close the document.
+Not that Domino translates "tomcat2" into "libtomcat2.so".
+
+Restart Domino
+--------------
+
+Restart the Domino HTTP server and you should see the redirector load.
+
+Mailing Lists
+-------------
+
+There are two mailing lists dedicated to the Domino Tomcat redirector:
+
+domino-tomcat-l:
+ http://nomen.tagish.co.uk/mailman/listinfo/domino-tomcat-l
+
+domino-tomcat-l is a general discussion list for issues with the
+redirector and also wider Tomcat/Domino integration issues such as
+calling the Domino Java API from a Tomcat servlet. I expect it to be
+fairly low volume so please subscribe if you're actively using the
+redirector. If you have an issue with the redirector please post it to
+the list where it will be seen not only by me but by other users who may
+be able to help with any problems.
+
+domino-tomcat-announce-l:
+ http://nomen.tagish.co.uk/mailman/listinfo/domino-tomcat-announce-l:
+
+domino-tomcat-announce-l is for announcements about the Domino Tomcat
+redirector. Mainly this list will be used for new releases but serious
+bugs will also be posted to it. This list will be very low volume; only
+I can post to it. I wouldn't expect more than a few posts per month. For
+this reason if you'd like to keep track of new redirector versions
+please subscribe to this list. From now on this list will be the main
+place to find out about new versions.
+
+Please let me know if you have any problems.
+
+Andy Armstrong, <andy@tagish.com>
diff --git a/connectors/jk/native2/server/dsapi/INSTALL-WIN32.txt b/connectors/jk/native2/server/dsapi/INSTALL-WIN32.txt
new file mode 100644
index 0000000..57f6609
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/INSTALL-WIN32.txt
@@ -0,0 +1,125 @@
+$Id$
+
+Installing the Domino Redirector on Win32
+-----------------------------------------
+
+If necessary build dsapi_redirector2.dll as per the instructions in
+BUILD.txt. Copy the DLL into the Domino program directory (this is the
+directory, which may be called something like C:\Lotus\Domino, that
+contains a file called nlnotes.exe). Shortly we will tell Domino where
+to find this file, but before we do that we need to make some registry
+entries. The simplest way is to edit the supplied file
+dsapi_redirector2.reg, which initially will look like this
+
+    Windows Registry Editor Version 5.00
+
+    [HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Dsapi Redirector\2.0]
+    "serverRoot"="D:\\Works\\Tomcat\\jakarta-tomcat-4.1.27"
+    "workersFile"="conf\\workers2.properties"
+    "tomcatStart"="bin\\startup.bat"
+    "tomcatStop"="bin\\shutdown.bat"
+    "tomcatTimeout"="30000"
+
+Change serverRoot to reflect the location of your Tomcat installation.
+The other filename parameters can either be relative to serverRoot or
+absolute paths. Once edited double click on dsapi_redirector2.reg to
+enter it into the registry.
+
+Automatically Starting Tomcat
+-----------------------------
+
+The last three registry entries above provide commands that the
+redirector DLL will use to start and stop Tomcat when the Domino http
+server starts and stops respectively. If you don't require this
+behaviour these two lines can be omitted (or deleted if you've already
+placed them in the registry).
+
+The Workers File
+----------------
+
+If necessary take the sample workers2.properties file from jakarta-
+tomcat- connectors\jk\conf and place it in the location specified in the
+registry settings above (typically the conf directory of your Tomcat
+installation). Edit the file to suit your Tomcat setup.
+
+Configuring Domino
+------------------
+
+Finally we need to configure Domino to use the DSAPI extension DLL. For
+those who are unfamiliar with Domino server configuration most of a
+server's configurable behavior is dictated by a document called the
+"server document" in a database called the "Public Name and Address
+Book" or "NAB" for short (N.B. Lotus have renamed the NAB to "Domino
+Directory" from Domino 5 onwards). Each Domino server will have a NAB
+(called names.nsf) and each NAB will have a number of server documents
+including one for the current server. If you have not previously
+configured a Domino server you may need to refer to the supplied
+documentation, or you may need to pass this document to your tame Domino
+administrator.
+
+Assuming you know your way around a Domino server document what we're
+going to do is actually quite simple. Open the server document for this
+server, place it in Edit mode, then locate the DSAPI section and the
+'DSAPI filter file names' field on the Internet Protocols tab, HTTP sub-
+tab. Add "dsapi_redirector2.dll" to the DSAPI field, then save and close
+the document.
+
+Restart Domino
+--------------
+
+In order to get these settings to take effect and make sure that you
+haven't disrupted anything else you should now restart the Domino
+server. If the server is running as a service and you have changed any
+relevant system variables (JAVA_HOME, TOMCAT_HOME, CLASSPATH) since the
+last time you restarted the computer you should do a complete restart
+now because updates to system variables are not seen by services until
+after a reboot. If all goes well you should see something like this on
+the server console when the web server starts up.
+
+    14/11/2003 13:02:18   Attempting to start Tomcat: D:\Works\Tomcat\jakarta-tomcat-4.1.27\bin\startup.bat
+    Using CATALINA_BASE:   D:\Works\Tomcat\jakarta-tomcat-4.1.27
+    Using CATALINA_HOME:   D:\Works\Tomcat\jakarta-tomcat-4.1.27
+    Using CATALINA_TMPDIR: D:\Works\Tomcat\jakarta-tomcat-4.1.27\temp
+    Using JAVA_HOME:       C:\JBuilder8\jdk1.4
+    14/11/2003 13:02:18   Apache Tomcat Interceptor (Jakarta/DSAPI/2.0.0) loaded
+    14/11/2003 13:02:19   HTTP Web Server started
+
+At about the same time Tomcat should open in a new window (assuming you
+enabled the autostart option in the registry settings). You should now
+be able to visit a URL that is handled by Tomcat. Something like
+
+    http://name-of-server/servlet/SnoopServlet
+
+may be available, depending on how Tomcat is configured. If that all
+works you're done ;-)
+
+Mailing Lists
+-------------
+
+There are two mailing lists dedicated to the Domino Tomcat redirector:
+
+domino-tomcat-l:
+ http://nomen.tagish.co.uk/mailman/listinfo/domino-tomcat-l
+
+domino-tomcat-l is a general discussion list for issues with the
+redirector and also wider Tomcat/Domino integration issues such as
+calling the Domino Java API from a Tomcat servlet. I expect it to be
+fairly low volume so please subscribe if you're actively using the
+redirector. If you have an issue with the redirector please post it to
+the list where it will be seen not only by me but by other users who may
+be able to help with any problems.
+
+domino-tomcat-announce-l:
+ http://nomen.tagish.co.uk/mailman/listinfo/domino-tomcat-announce-l:
+
+domino-tomcat-announce-l is for announcements about the Domino Tomcat
+redirector. Mainly this list will be used for new releases but serious
+bugs will also be posted to it. This list will be very low volume; only
+I can post to it. I wouldn't expect more than a few posts per month. For
+this reason if you'd like to keep track of new redirector versions
+please subscribe to this list. From now on this list will be the main
+place to find out about new versions.
+
+Please let me know if you have any problems.
+
+Andy Armstrong, <andy@tagish.com>
diff --git a/connectors/jk/native2/server/dsapi/Makefile.in b/connectors/jk/native2/server/dsapi/Makefile.in
new file mode 100644
index 0000000..40d016c
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/Makefile.in
@@ -0,0 +1,119 @@
+# Gnu makefile and libtool are required
+# use -D options to overrides defaults
+CC=@CC@
+CP=@CP@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+APR_INCDIR=@APR_INCDIR@
+DOMINO_HOME=@DOMINO_HOME@
+NOTESAPI=@NOTESAPI@
+OS=@OS@
+DOMINO_INCL=@DOMINO_INCL@
+EXTRA_CFLAGS=@APXS_CFLAGS@
+EXTRA_CPPFLAGS=@APXS_CPPFLAGS@
+JAVA_HOME=@JAVA_HOME@
+APR_INCL=@APR_CFLAGS@
+APR_LDFLAGS=@APR_LDFLAGS@ `@APR_DIR@/apr-config --ldflags --libs` `@APR_UTIL_DIR@/apu-config --ldflags --libs`
+
+ifneq ($(strip $(JAVA_HOME)),)
+JAVA_INCL=-I ${JAVA_HOME}/include -I ${JAVA_HOME}/include/${OS}
+JAVA_LIB=-L ${JAVA_HOME}/jre/lib/${ARCH} -L ${JAVA_HOME}/lib/${ARCH}/native_threads
+endif
+
+NOTES_INCL=-I ${NOTESAPI}/include
+NOTES_CFLAGS=-DUNIX -DLINUX
+
+JK_DIR := ../..
+BUILD_DIR = ${JK_DIR}/../build/jk2/dsapi
+
+top_builddir=../..
+LIBTOOL=@LIBTOOL@
+
+# It doesn't hurt if we include all
+INCLUDES= -I${JK_DIR}/include \
+          ${APR_INCL} \
+          ${DOMINO_INCL} \
+	  ${JAVA_INCL} \
+	  ${NOTES_INCL}
+
+JK_CFLAGS=-DCHUNK_SIZE=4096 @APR_CFLAGS@ -DHAVE_MMAP @HAVE_JNI@ @HAS_PCRE@
+JK_LDFLAGS=-lcrypt ${APR_LDFLAGS} @PCRE_LIBS@
+
+###### Based on rules.mk ##########################################
+ALL_CFLAGS   = $(EXTRA_CFLAGS) $(NOTEST_CFLAGS) $(NOTES_CFLAGS) $(CFLAGS)
+ALL_CPPFLAGS = $(DEFS) $(EXTRA_CPPFLAGS) $(NOTEST_CPPFLAGS) $(CPPFLAGS)
+ALL_LDFLAGS  = $(EXTRA_LDFLAGS) $(NOTEST_LDFLAGS) $(LDFLAGS)
+ALL_LIBS     = $(EXTRA_LIBS) $(NOTEST_LIBS) $(LIBS)
+ALL_INCLUDES = $(INCLUDES) $(EXTRA_INCLUDES)
+
+# Compile commands
+COMPILE      = $(CC)  $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(ALL_INCLUDES)
+
+SH_COMPILE = $(LIBTOOL) --mode=compile $(COMPILE) $(JK_CFLAGS)
+MOD_LINK = $(LIBTOOL) --mode=link $(CC) -avoid-version -module -rpath ${DOMINO_HOME} $(LT_LDFLAGS) $(ALL_LDFLAGS)
+MOD_INSTALL = $(LIBTOOL) --mode=install $(CP)
+
+#############################################################################
+
+COMMON_C_FILES := $(wildcard ${JK_DIR}/common/*.c )
+
+# ---------- File list creation -------------------- 
+# Same behavior as ant - 'all files from a dir'. 
+# Excludes are not yet implemented.
+#JNI_C_FILES := $(wildcard ${JK_DIR}/jni/*.c )
+DSAPI_C_FILES := $(wildcard ${JK_DIR}/server/dsapi/*.c )
+H_FILES := $(wildcard ${JK_DIR}/include/*.h )
+
+COMMON_LO_FILES := $(patsubst ${JK_DIR}/common/%, ${BUILD_DIR}/%, \
+			 $(patsubst %c, %lo, ${COMMON_C_FILES} ))
+#JNI_LO_FILES := $(patsubst ${JK_DIR}/jni/%, ${BUILD_DIR}/%, \
+#			 $(patsubst %c, %lo, ${JNI_C_FILES} ))
+DSAPI_LO_FILES := $(patsubst ${JK_DIR}/server/dsapi/%, ${BUILD_DIR}/%, \
+			 $(patsubst %c, %lo, ${DSAPI_C_FILES} ))
+
+
+# ---------- Compile rules --------------------
+
+.PHONY: all
+
+
+VPATH=.:../../common
+
+.c.lo:
+	 ${SH_COMPILE} -c $< -o $>
+
+${BUILD_DIR}/%.lo: ${JK_DIR}/common/%.c
+	 ${SH_COMPILE} -c $< -o $@
+
+#${BUILD_DIR}/%.lo: ${JK_DIR}/jni/%.c
+#	 ${SH_COMPILE} -c $< -o $@
+
+${BUILD_DIR}/%.lo: ${JK_DIR}/server/dsapi/%.c
+	 ${SH_COMPILE} -c $< -o $@
+
+
+# ---------- Targets -------------------- 
+
+#all: prepare ${BUILD_DIR}/libtomcat2.so ${BUILD_DIR}/libjkjni.so
+all: prepare ${BUILD_DIR}/libtomcat2.so
+
+${BUILD_DIR}/libtomcat2.la: ${COMMON_LO_FILES} ${DSAPI_LO_FILES}
+	${MOD_LINK} -o $@ $^ ${JK_LDFLAGS}
+
+${BUILD_DIR}/libtomcat2.so: ${BUILD_DIR}/libtomcat2.la
+	$(MOD_INSTALL) $^ `pwd`/${BUILD_DIR}
+
+#${BUILD_DIR}/libjkjni.la: ${JNI_LO_FILES} ${COMMON_LO_FILES}
+#	$(MOD_LINK) -o $@ $(JK_LDFLAGS) $^
+
+#${BUILD_DIR}/libjkjni.so: ${BUILD_DIR}/libjkjni.la
+#	$(MOD_INSTALL) $^ `pwd`/${BUILD_DIR}
+
+${COMMON_C_FILES} ${DSAPI_C_FILES}: ${H_FILES}
+
+prepare: 
+	mkdir -p ${BUILD_DIR}
+
+clean: 
+	rm -rf ${BUILD_DIR}/*.lo ${BUILD_DIR}/*.la ${BUILD_DIR}/*.o ${BUILD_DIR}/.libs ${BUILD_DIR}/*.so
diff --git a/connectors/jk/native2/server/dsapi/config.h b/connectors/jk/native2/server/dsapi/config.h
new file mode 100644
index 0000000..6d9f06b
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/config.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: DSAPI plugin for Lotus Domino                              *
+ * Author:      Andy Armstrong <andy@tagish.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef __config_h
+#define __config_h
+
+#define NONBLANK(s) \
+    (NULL != (s) && '\0' != *(s))
+
+/* the _memicmp() function is available */
+#if defined(WIN32)
+
+#define HAVE_MEMICMP
+#define PATHSEP '\\'
+
+#else
+
+#undef HAVE_MEMICMP
+#define PATHSEP '/'
+#define MAX_PATH 512
+
+#endif
+
+#ifdef _DEBUG
+#define DEBUG(args) \
+    do { _printf args ; } while (0)
+#else
+#define DEBUG(args) \
+    do { } while (0)
+#endif
+
+#if !defined(DLLEXPORT)
+#if defined(WIN32) && !defined(TESTING)
+#define DLLEXPORT __declspec(dllexport)
+#else
+#define DLLEXPORT
+#endif
+#endif
+
+/* Configuration tags */
+#define SERVER_ROOT_TAG     "serverRoot"
+#define WORKER_FILE_TAG     "workersFile"
+#define TOMCAT_START_TAG    "tomcatStart"
+#define TOMCAT_STOP_TAG     "tomcatStop"
+#define TOMCAT_TIMEOUT_TAG  "tomcatTimeout"
+#define VERSION             "2.0.6"
+#define VERSION_STRING      "Jakarta/DSAPI/" VERSION
+#define FILTERDESC          "Apache Tomcat Interceptor (" VERSION_STRING ")"
+#define SERVERDFLT          "Lotus Domino"
+#define REGISTRY_LOCATION   "Software\\Apache Software Foundation\\Jakarta Dsapi Redirector\\2.0"
+#define TOMCAT_STARTSTOP_TO 30000               /* 30 seconds */
+#define CONTENT_LENGTH      "Content-length"    /* Name of CL header */
+#define PROPERTIES_EXT      ".properties"
+
+#endif /* __config_h */
diff --git a/connectors/jk/native2/server/dsapi/dsapi.dsp b/connectors/jk/native2/server/dsapi/dsapi.dsp
new file mode 100644
index 0000000..f3cb176
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/dsapi.dsp
@@ -0,0 +1,508 @@
+# Microsoft Developer Studio Project File - Name="dsapi" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=dsapi - Win32 Debug Static
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "dsapi.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "dsapi.mak" CFG="dsapi - Win32 Debug Static"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "dsapi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "dsapi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "dsapi - Win32 Debug Static" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "dsapi - Win32 Release Static" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "dsapi - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ISAPI_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "..\..\..\..\..\apr\include" /I "..\..\..\..\..\apr-util\include" /I "..\..\..\..\..\pcre\include" /I "$(NOTESAPI)\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "HAVE_JNI" /D "HAS_APR" /D "HAS_PCRE" /D "NT" /U "NOUSER" /FR /FD /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0xc0a /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 libapr.lib libaprutil.lib ws2_32.lib wsock32.lib advapi32.lib notes.lib libpcre.dll.a libpcreposix.dll.a /nologo /dll /machine:I386 /out:"Release/dsapi_redirector2.dll" /libpath:"..\..\..\..\..\apr\Release" /libpath:"..\..\..\..\..\pcre\lib" /libpath:"..\..\..\..\..\apr-util\Release" /libpath:"$(NOTESAPI)\lib\mswin32"
+# SUBTRACT LINK32 /nodefaultlib
+
+!ELSEIF  "$(CFG)" == "dsapi - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ISAPI_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "..\..\..\..\..\apr\include" /I "..\..\..\..\..\apr-util\include" /I "..\..\..\..\..\pcre\include" /I "$(NOTESAPI)\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "HAVE_JNI" /D "HAS_APR" /D "HAS_PCRE" /D "NT" /U "NOUSER" /FR /FD /GZ /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0xc0a /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 libapr.lib libaprutil.lib ws2_32.lib wsock32.lib advapi32.lib notes.lib libpcre.dll.a libpcreposix.dll.a /nologo /dll /debug /machine:I386 /out:"Debug/dsapi_redirector2.dll" /pdbtype:sept /libpath:"..\..\..\..\..\apr\Release" /libpath:"..\..\..\..\..\pcre\lib" /libpath:"..\..\..\..\..\apr-util\Release" /libpath:"$(NOTESAPI)\lib\mswin32"
+# SUBTRACT LINK32 /nodefaultlib
+
+!ELSEIF  "$(CFG)" == "dsapi - Win32 Debug Static"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "DebugS"
+# PROP BASE Intermediate_Dir "DebugS"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "DebugS"
+# PROP Intermediate_Dir "DebugS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "$(APACHE2_HOME)\include" /I "$(APACHE2_HOME)\os\win32" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ISAPI_EXPORTS" /D "HAVE_JNI" /D "HAS_APR" /FR /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "..\..\..\..\..\apr\include" /I "..\..\..\..\..\apr-util\include" /I "..\..\..\..\..\pcre\include" /I "$(NOTESAPI)\include" /D "_DEBUG" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "HAVE_JNI" /D "HAS_APR" /D "HAS_PCRE" /D "NT" /U "NOUSER" /FR /FD /GZ /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 libapr.lib libaprutil.lib wsock32.lib advapi32.lib /nologo /dll /debug /machine:I386 /out:"Debug/dsapi_redirector2.dll" /pdbtype:sept /libpath:"$(APACHE2_HOME)\lib"
+# SUBTRACT BASE LINK32 /nodefaultlib
+# ADD LINK32 apr.lib aprutil.lib ws2_32.lib wsock32.lib advapi32.lib libpcre.a libpcreposix.a notes.lib /nologo /dll /debug /machine:I386 /out:"DebugS/dsapi_redirector2.dll" /pdbtype:sept /libpath:"..\..\..\..\..\pcre\lib" /libpath:"..\..\..\..\..\apr\LibR" /libpath:"..\..\..\..\..\apr-util\LibR" /libpath:"..\..\..\..\..\apr-iconv\LibR" /libpath:"$(NOTESAPI)\lib\mswin32"
+# SUBTRACT LINK32 /nodefaultlib
+
+!ELSEIF  "$(CFG)" == "dsapi - Win32 Release Static"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "ReleaseS"
+# PROP BASE Intermediate_Dir "ReleaseS"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "ReleaseS"
+# PROP Intermediate_Dir "ReleaseS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "$(APACHE2_HOME)\include" /I "$(APACHE2_HOME)\os\win32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ISAPI_EXPORTS" /D "HAVE_JNI" /D "HAS_APR" /FR /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "..\..\..\..\..\apr\include" /I "..\..\..\..\..\apr-util\include" /I "..\..\..\..\..\pcre\include" /I "$(NOTESAPI)\include" /D "NDEBUG" /D "_WIN32" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "HAVE_JNI" /D "HAS_APR" /D "HAS_PCRE" /D "NT" /U "NOUSER" /FR /FD /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 libapr.lib libaprutil.lib wsock32.lib advapi32.lib /nologo /dll /machine:I386 /out:"Release/dsapi_redirector2.dll" /libpath:"$(APACHE2_HOME)\lib"
+# ADD LINK32 apr.lib aprutil.lib ws2_32.lib wsock32.lib advapi32.lib libpcre.a libpcreposix.a notes.lib /nologo /dll /machine:I386 /out:"ReleaseS/dsapi_redirector2.dll" /libpath:"..\..\..\..\..\pcre\lib" /libpath:"..\..\..\..\..\apr\LibR" /libpath:"..\..\..\..\..\apr-util\LibR" /libpath:"..\..\..\..\..\apr-iconv\LibR" /libpath:"$(NOTESAPI)\lib\mswin32"
+# SUBTRACT LINK32 /nodefaultlib
+
+!ENDIF 
+
+# Begin Target
+
+# Name "dsapi - Win32 Release"
+# Name "dsapi - Win32 Debug"
+# Name "dsapi - Win32 Debug Static"
+# Name "dsapi - Win32 Release Static"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Group "JK2 Common Source"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel_apr_socket.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel_jni.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel_un.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_config.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_config_file.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_endpoint.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_env.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_handler_logon.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_handler_response.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jni\jk_jni_aprImpl.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_file.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_win32.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_msg_ajp.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_mutex.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_mutex_proc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_mutex_thread.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_nwmain.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_objCache.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_pool_apr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_registry.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_requtil.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_shm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_signal.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_uriEnv.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_uriMap.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_user.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_vm_default.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_ajp13.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_jni.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_lb.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_run.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_status.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_workerEnv.c
+# End Source File
+# End Group
+# Begin Group "DSAPI Source"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\jk_dsapi_plugin.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_logger_domino.c
+# End Source File
+# End Group
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Group "JK2 Common Headers"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\include\jk_channel.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_endpoint.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_env.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_global.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_handler.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_win32_message.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_msg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_objCache.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_registry.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_requtil.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_service.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_shm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_uriEnv.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_uriMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_vm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_workerEnv.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jni\org_apache_jk_apr_AprImpl.h
+# End Source File
+# End Group
+# Begin Group "DSAPI Headers"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\config.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\dsapifilter.h
+# End Source File
+# End Group
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_win32_message.mc
+
+!IF  "$(CFG)" == "dsapi - Win32 Release"
+
+# Begin Custom Build - Creating resources from $(InputPath)
+InputDir=\Works\Tomcat\jakarta-tomcat-connectors\jk\native2\common
+InputPath=..\..\common\jk_logger_win32_message.mc
+
+"..\..\common\jk_logger_win32_message.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	mc -h $(InputDir) -r $(InputDir) $(InputPath)
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "dsapi - Win32 Debug"
+
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating resources from $(InputPath)
+InputDir=\Works\Tomcat\jakarta-tomcat-connectors\jk\native2\common
+InputPath=..\..\common\jk_logger_win32_message.mc
+
+"..\..\common\jk_logger_win32_message.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	mc -h $(InputDir) -r $(InputDir) $(InputPath)
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "dsapi - Win32 Debug Static"
+
+# PROP BASE Ignore_Default_Tool 1
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating resources from $(InputPath)
+InputDir=\Works\Tomcat\jakarta-tomcat-connectors\jk\native2\common
+InputPath=..\..\common\jk_logger_win32_message.mc
+
+"..\..\common\jk_logger_win32_message.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	mc -h $(InputDir) -r $(InputDir) $(InputPath)
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "dsapi - Win32 Release Static"
+
+# Begin Custom Build - Creating resources from $(InputPath)
+InputDir=\Works\Tomcat\jakarta-tomcat-connectors\jk\native2\common
+InputPath=..\..\common\jk_logger_win32_message.mc
+
+"..\..\common\jk_logger_win32_message.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	mc -h $(InputDir) -r $(InputDir) $(InputPath)
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_win32_message.rc
+# End Source File
+# End Group
+# Begin Group "Other files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\dsapi_redirector2.reg
+# End Source File
+# Begin Source File
+
+SOURCE=.\todo.txt
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/connectors/jk/native2/server/dsapi/dsapi.dsw b/connectors/jk/native2/server/dsapi/dsapi.dsw
new file mode 100644
index 0000000..2d7f0ca
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/dsapi.dsw
@@ -0,0 +1,41 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "dsapi"=".\dsapi.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "test"=".\test\test.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/connectors/jk/native2/server/dsapi/dsapi_redirector2.reg b/connectors/jk/native2/server/dsapi/dsapi_redirector2.reg
new file mode 100644
index 0000000..9a41c0d
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/dsapi_redirector2.reg
@@ -0,0 +1,8 @@
+Windows Registry Editor Version 5.00
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Dsapi Redirector\2.0]
+"serverRoot"="D:\\Works\\Tomcat\\jakarta-tomcat-4.1.27"
+"workersFile"="conf\\workers2.properties"
+"tomcatStart"="bin\\startup.bat"
+"tomcatStop"="bin\\shutdown.bat"
+"tomcatTimeout"="30000"
diff --git a/connectors/jk/native2/server/dsapi/dsapifilter.h b/connectors/jk/native2/server/dsapi/dsapifilter.h
new file mode 100644
index 0000000..47df62d
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/dsapifilter.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright 1999-2001,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: DSAPI plugin for Lotus Domino                              *
+ * Author:      Andy Armstrong <andy@tagish.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#ifndef __dsapifilter_h
+#define __dsapifilter_h
+
+#define kInterfaceVersion   2
+#define kMaxFilterDesc      255
+
+typedef enum {
+    kFilterNotHandled       = 0,
+    kFilterHandledRequest   = 1,
+    kFilterHandledEvent     = 2,
+    kFilterError            = 3
+} FilterReturnCode;
+
+typedef enum {
+    kFilterRawRequest       = 0x01,
+    kFilterParsedRequest    = 0x02,
+    kFilterAuthUser         = 0x04,
+    kFilterUserNameList     = 0x08,
+    kFilterMapURL           = 0x10,
+    kFilterResponse         = 0x20,
+    kFilterRawWrite         = 0x40,
+    kFilterEndRequest       = 0x80,
+    kFilterAny              = 0xFF
+} EventFlags;
+
+typedef struct {
+    unsigned int    serverFilterVersion;
+    unsigned int    appFilterVersion;
+    unsigned int    eventFlags;
+    unsigned int    initFlags;
+    char            filterDesc[kMaxFilterDesc + 1];
+} FilterInitData;
+
+typedef struct {
+    unsigned int    method;
+    char            *URL;
+    char            *version;
+    char            *userName;
+    char            *password;
+    unsigned char   *clientCert;
+    unsigned int    clientCertLen;
+    char            *contentRead;
+    unsigned int    contentReadLen;
+} FilterRequest;
+
+typedef struct _FilterContext FilterContext;
+
+struct _FilterContext {
+    unsigned int    contextSize;
+    unsigned int    revision;
+    void            *serverContext;
+    unsigned int    serverReserved;
+    unsigned int    securePort;
+    void            *privateContext;
+
+    int (*GetRequest)(FilterContext *context, FilterRequest * request,  unsigned int *errID);
+    int (*GetRequestContents)(FilterContext *context, char **contents, unsigned int *errID);
+    int (*GetServerVariable)(FilterContext *context, char *name, void *buffer, unsigned int bufferSize, unsigned int *errID);
+    int (*WriteClient)(FilterContext *context, char *buffer, unsigned int bufferLen, unsigned int reserved, unsigned int *errID);
+    void *(*AllocMem)(FilterContext *context, unsigned int size, unsigned int reserved, unsigned int *errID);
+    int (*ServerSupport)(FilterContext *context, unsigned int funcType, void *data1, void *data2, unsigned int other, unsigned int *errID);
+};
+
+typedef enum {
+    kRequestNone    = 0,
+    kRequestHEAD    = 1,
+    kRequestGET     = 2,
+    kRequestPOST    = 3,
+    kRequestPUT     = 4,
+    kRequestDELETE  = 5
+} RequestMethod;
+
+typedef enum {
+    kWriteResponseHeaders = 1
+} ServerSupportTypes;
+
+typedef struct {
+    unsigned int    responseCode;
+    char            *reasonText;
+    char            *headerText;
+} FilterResponseHeaders;
+
+typedef struct {
+    unsigned int    requestMethod;
+
+    int (*GetAllHeaders)(FilterContext *context, char **headers, unsigned int *errID);
+    int (*GetHeader)(FilterContext *context, char *name, char *buffer, unsigned int bufferSize, unsigned int *errID);
+    int (*SetHeader)(FilterContext *context, char *name, char *value, unsigned int *errID);
+    int (*AddHeader)(FilterContext *context, char *header, unsigned int *errID);
+
+    unsigned int    reserved;
+} FilterRawRequest;
+
+typedef struct {
+    unsigned int    requestMethod;
+
+    int (*GetAllHeaders)(FilterContext *context, char **headers, unsigned int *errID);
+    int (*GetHeader)(FilterContext *context, char *name, char *buffer, unsigned int bufferSize, unsigned int *errID);
+
+    unsigned int    reserved;
+} FilterParsedRequest;
+
+typedef struct {
+    const char      *url;
+    char            *pathBuffer;
+    unsigned int    bufferSize;
+    unsigned int    mapType;
+} FilterMapURL;
+
+typedef enum {
+    kURLMapUnknown  = 0,
+    kURLMapPass     = 1,
+    kURLMapExec     = 2,
+    kURLMapRedirect = 3,
+    kURLMapService  = 4,
+    kURLMapDomino   = 5
+} FilterULMapTypes;
+
+typedef struct {
+    unsigned char   *userName;
+    unsigned char   *password;
+    unsigned char   *clientCert;
+    unsigned int    clientCertLen;
+    unsigned int    authFlags;
+    unsigned int    preAuthenticated;
+    unsigned int    foundInCache;
+    unsigned int    authNameSize;
+    unsigned char   *authName;
+    unsigned int    authType;
+
+    int (*GetUserNameList)(FilterContext *context, unsigned char * buffer, unsigned int bufferSize, unsigned int *numNames, unsigned int reserved, unsigned int *errID);
+    int (*GetHeader)(FilterContext *context, char *name, char *buffer, unsigned int bufferSize, unsigned int *errID);
+} FilterAuthenticate;
+
+typedef enum {
+    kNotAuthentic           = 0,
+    kAuthenticBasic         = 1,
+    kAuthenticClientCert    = 2
+} FilterAuthenticationTypes;
+
+typedef enum {
+    kAuthAllowBasic         = 0x01,
+    kAuthAllowAnonymous     = 0x02,
+    kAuthAllowSSLCert       = 0x04,
+    kAuthAllowSSLBasic      = 0x08,
+    kAuthAllowSSLAnonymous  = 0x10,
+    kAuthRedirectToSSL      = 0x20
+} FilterAuthConfigFlags;
+
+typedef struct {
+    const unsigned char     *userName;
+
+    int (*GetUserNameList)(FilterContext *context, unsigned char * buffer, unsigned int bufferSize, unsigned int *numNames, unsigned int reserved, unsigned int *errID);
+    int (*PopulateUserNameList)(FilterContext *context, unsigned char * buffer, unsigned int bufferSize, unsigned int *numNames, unsigned int reserved, unsigned int *errID);
+    int (*AddGroupsToList)(FilterContext *context, unsigned char * groupNames, unsigned int numGroupNames, unsigned int reserved, unsigned int *errID);
+    int (*RemoveGroupsFromList)(FilterContext *context, unsigned int reserved, unsigned int *errID);
+
+    unsigned int            reserved;
+} FilterUserNameList;
+
+typedef struct {
+    unsigned int            responseCode;
+    char                    *reasonText;
+
+    int (*GetAllHeaders)(FilterContext *context, char **headers, unsigned int *errID);
+    int (*GetHeader)(FilterContext *context, char *name, char *buffer, unsigned int bufferSize, unsigned int *errID);
+    int (*SetHeader)(FilterContext *context, char *name, char *value, unsigned int *errID);
+    int (*AddHeader)(FilterContext *context, char *header, unsigned int *errID);
+
+    unsigned int            reserved;
+} FilterResponse;
+
+typedef struct {
+    char            *content;
+    unsigned int    contentLen;
+    unsigned int    reserved;
+} FilterRawWrite;
+
+/* Non DSAPI stuff here for convenience */
+
+#define NOERROR 0
+
+void AddInLogMessageText(char *string, unsigned short err, ...);
+
+#endif  /* __dsapi_filter_h */
diff --git a/connectors/jk/native2/server/dsapi/jk_dsapi_plugin.c b/connectors/jk/native2/server/dsapi/jk_dsapi_plugin.c
new file mode 100644
index 0000000..2565d5b
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/jk_dsapi_plugin.c
@@ -0,0 +1,1339 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: DSAPI plugin for Lotus Domino                              *
+ * Author:      Andy Armstrong <andy@tagish.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+/* Based on the IIS redirector by Gal Shachor <shachor@il.ibm.com> */
+
+/* Standard headers */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+/* If we're building under Windows get windows.h. This must be included
+ * before any APR includes because APR itself does a #include <windows.h>
+ * after turning off some features that we need.
+ */
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#include "config.h"
+
+/* JK stuff */
+#include "jk_global.h"
+#include "jk_requtil.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_logger.h"
+#include "jk_env.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+#include "apr_general.h"
+
+/* Domino DSAPI filter definitions */
+#if !defined(TESTING)
+#include <global.h>
+#include <addin.h>
+#include <osmem.h>
+#include <lookup.h>
+#endif
+#include <dsapi.h>
+
+int JK_METHOD jk2_logger_domino_factory(jk_env_t *env, jk_pool_t *pool, jk_bean_t *result, const char *type, const char *name);
+
+#if defined(TESTING)
+#define LOGGER              "logger.printf"
+int JK_METHOD jk2_logger_printf_factory(jk_env_t *env, jk_pool_t *pool, jk_bean_t *result, const char *type, const char *name);
+#else
+#define LOGGER              "logger.domino"
+#endif
+
+#define NULLSTR(s) \
+	(NULL == (s) || '\0' == (s)[0])
+
+#ifdef WIN32
+static char  libFileName[MAX_PATH];
+static char  iniFileName[MAX_PATH];
+#define INI_NAME iniFileName
+#else
+#define INI_NAME "libtomcat2" PROPERTIES_EXT
+#endif
+static int   iniFileUsed        = JK_FALSE;
+static int   isInited           = JK_FALSE;
+
+static const char *tomcatStart  = NULL;
+static const char *tomcatStop   = NULL;
+static const char *workersFile  = NULL;
+static const char *serverRoot   = NULL;
+static int tomcatTimeout        = TOMCAT_STARTSTOP_TO;
+
+static const char *crlf         = "\r\n";
+static const char *filterdesc   = FILTERDESC;
+
+#define WORKPOOL globalPool
+
+static jk_workerEnv_t *workerEnv;
+static apr_pool_t *jk_globalPool;
+
+/* Per request private data */
+typedef struct private_ws {
+    /* These get passed in by Domino and are used to access various
+     * Domino methods and data.
+     */
+    FilterContext       *context;
+    FilterParsedRequest *reqData;
+
+    /* True iff the response headers have been sent
+     */
+    int                 responseStarted;
+
+    /* Current pointer into and remaining size
+     * of request body data
+     */
+    char                *reqBuffer;
+    unsigned int        reqSize;
+
+} private_ws_t;
+
+#if defined(TESTING)
+static void AddInLogMessageText(char *fmt, unsigned short err, ...) {
+    va_list ap;
+    va_start(ap, err);
+
+    printf("Debug: ");
+    vprintf(fmt, ap);
+    printf("\n");
+
+    va_end(ap);
+}
+#endif
+
+/* Case insentive memcmp() clone
+ */
+#ifdef HAVE_MEMICMP
+#define noCaseMemCmp(ci, cj, l) _memicmp((void *) (ci), (void *) (cj), (l))
+#else
+static int noCaseMemCmp(const char *ci, const char *cj, int len) {
+    if (0 == memcmp(ci, cj, len)) {
+        return 0;
+    }
+
+    while (len > 0) {
+        int cmp = tolower(*ci) - tolower(*cj);
+        if (cmp != 0) {
+            return cmp;
+        }
+        ci++;
+        cj++;
+        len--;
+    }
+    return 0;
+}
+#endif
+
+/* Case insentive strcmp() clone
+ */
+#ifdef HAVE_STRICMP
+#define noCaseStrCmp(si, sj) _stricmp((void *) (si), (void *) (sj))
+#else
+static int noCaseStrCmp(const char *si, const char *sj) {
+    if (0 == strcmp(si, sj)) {
+        return 0;
+    }
+
+    while (*si && tolower(*si) == tolower(*sj)) {
+        si++;
+        sj++;
+    }
+
+    return tolower(*si) - tolower(*sj);
+}
+#endif
+
+/* Case insensitive substring search.
+ * str      string to search
+ * slen     length of string to search
+ * ptn      pattern to search for
+ * plen     length of pattern
+ * returns  1 if there's a match otherwise 0
+ */
+static int scanPath(const char *str, int slen, const char *ptn, int plen) {
+    const char *sp = str;
+
+    while (slen >= plen) {
+        /* We're looking for a match for the specified string bounded by
+         * the start of the string, \ or / at the left and the end of the
+         * string, \ or / at the right. We look for \ as well as / on the
+         * suspicion that a Windows hosted server might accept URIs
+         * containing \.
+         */
+        if (noCaseMemCmp(sp, ptn, plen) == 0 &&
+            (sp == str || sp[-1] == '\\' || sp[-1] == '/') &&
+            (slen == plen || sp[plen] == '\\' || sp[plen] == '/')) {
+            return 1;
+        }
+
+        slen--;
+        sp++;
+    }
+    return 0;
+}
+
+#ifdef _DEBUG
+static void _printf(const char *msg, ...) {
+    char buf[512];      /* dangerous fixed size buffer */
+    va_list ap;
+    va_start(ap, msg);
+    vsprintf(buf, msg, ap);
+    va_end(ap);
+    AddInLogMessageText("Debug: %s", NOERROR, buf);
+}
+#endif
+
+/* Get the value of a server (CGI) variable as a string
+ */
+static int getVariable(struct jk_env *env, jk_ws_service_t *s, char *hdrName,
+                         char *buf, unsigned int bufsz, char **dest, const char *dflt) {
+    int errID;
+    private_ws_t *ws = (private_ws_t *) s->ws_private;
+
+    if (ws->context->GetServerVariable(ws->context, hdrName, buf, bufsz, &errID)) {
+        *dest = s->pool->pstrdup(env, s->pool, buf);
+    } else {
+        *dest = s->pool->pstrdup(env, s->pool, dflt);
+    }
+
+    return JK_TRUE;
+}
+
+/* Get the value of a server (CGI) variable as an integer
+ */
+static int getVariableInt(struct jk_env *env, jk_ws_service_t *s, char *hdrName,
+                        char *buf, unsigned int bufsz, long *dest, long dflt) {
+    int errID;
+    private_ws_t *ws = (private_ws_t *) s->ws_private;
+
+    if (ws->context->GetServerVariable(ws->context, hdrName, buf, bufsz, &errID)) {
+        *dest = atol(buf);
+    } else {
+        *dest = dflt;
+    }
+
+    return JK_TRUE;
+}
+
+/* Get the value of a server (CGI) variable as an integer
+ */
+static int getVariableBool(struct jk_env *env, jk_ws_service_t *s, char *hdrName,
+                            char *buf, unsigned int bufsz, int *dest, int dflt) {
+    int errID;
+    private_ws_t *ws = (private_ws_t *) s->ws_private;
+
+    if (ws->context->GetServerVariable(ws->context, hdrName, buf, bufsz, &errID)) {
+        if (isdigit(buf[0])) {
+            *dest = atoi(buf) != 0;
+        } else if (noCaseStrCmp(buf, "yes") == 0 || noCaseStrCmp(buf, "on") == 0) {
+            *dest = 1;
+        } else {
+            *dest = 0;
+        }
+    } else {
+        *dest = dflt;
+    }
+
+    return JK_TRUE;
+}
+
+/* A couple of utility macros to supply standard arguments to getVariable() and
+ * getVariableInt().
+ */
+#define GETVARIABLE(name, dest, dflt)       \
+    getVariable(env, s, (name), workBuf, sizeof(workBuf), (dest), (dflt))
+#define GETVARIABLEINT(name, dest, dflt)    \
+    getVariableInt(env, s, (name), workBuf, sizeof(workBuf), (dest), (dflt))
+#define GETVARIABLEBOOL(name, dest, dflt)   \
+    getVariableBool(env, s, (name), workBuf, sizeof(workBuf), (dest), (dflt))
+
+/* Return 1 iff the supplied string contains "web-inf" (in any case
+ * variation. We don't allow URIs containing web-inf, although
+ * scanPath() actually looks for the string bounded by path punctuation
+ * or the ends of the string, so web-inf must appear as a single element
+ * of the supplied URI
+ */
+static int badURI(const char *uri) {
+    static const char *wi = "web-inf";
+    return scanPath(uri, strlen(uri), wi, strlen(wi));
+}
+
+/* Replacement for strcat() that updates a buffer pointer. It's
+ * probably marginal, but this should be more efficient that strcat()
+ * in cases where the string being concatenated to gets long because
+ * strcat() has to count from start of the string each time.
+ */
+static void append(char **buf, const char *str) {
+    int l = strlen(str);
+    memcpy(*buf, str, l);
+    (*buf)[l] = '\0';
+    *buf += l;
+}
+
+/* Allocate space for a string given a start pointer and an end pointer
+ * and return a pointer to the allocated, copied string.
+ */
+static char *subStr(jk_env_t *env, jk_pool_t *pool, const char *start, const char *end) {
+    char *out = NULL;
+
+    if (start != NULL && end != NULL && end > start) {
+        int len = end - start;
+        if (out = pool->alloc(env, pool, len + 1), NULL != out) {
+            memcpy(out, start, len);
+            out[len] = '\0';
+        }
+    }
+    return out;
+}
+
+/* Like subStr() but use a static buffer if possible.
+ */
+static char *smartSubStr(jk_env_t *env, jk_pool_t *pool, char **bufp, int *bufSz,
+                            const char *start, const char *end) {
+    int len = end - start;
+    if (len < *bufSz) {
+        char *rv = *bufp;
+        memcpy(rv, start, len);
+        rv[len++] = '\0';
+        /* Adjust buffer pointer, length */
+        *bufp  += len;
+        *bufSz -= len;
+        return rv;
+    } else {
+        return subStr(env, pool, start, end);
+    }
+}
+
+static int JK_METHOD cbInit(struct jk_env *env, jk_ws_service_t *s,
+                            struct jk_worker *w, void *context) {
+    return JK_TRUE;
+}
+
+/* Post request cleanup.
+ */
+static void JK_METHOD cbAfterRequest( struct jk_env *env, jk_ws_service_t *_this) {
+
+}
+
+/* Set the response head in the server structures. This will be called
+ * before the first write.
+ */
+static int JK_METHOD cbHead(struct jk_env *env, jk_ws_service_t *s) {
+    if (s->status < 100 || s->status >= 1000) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, "jk_ws_service_t::cbHead, invalid status %d\n", s->status);
+        return JK_ERR;
+    }
+
+    if (s && s->ws_private) {
+        private_ws_t *p = s->ws_private;
+
+        if (!p->responseStarted) {
+            char *hdrBuf;
+            FilterResponseHeaders frh;
+            int rc, errID;
+            int hdrCount;
+            const char *reason;
+
+            p->responseStarted = JK_TRUE;
+
+            reason = (NULL == s->msg) ? "" : s->msg;
+            hdrCount = s->headers_out->size(env, s->headers_out);
+
+            /* Build a single string containing all the headers
+             * because that's what Domino needs.
+             */
+            if (hdrCount > 0) {
+                int i;
+                int hdrLen;
+                char *bufp;
+
+                for (i = 0, hdrLen = 3; i < hdrCount; i++) {
+                    hdrLen += strlen(s->headers_out->nameAt(env, s->headers_out, i));
+                    hdrLen += strlen(s->headers_out->valueAt(env, s->headers_out, i));
+                    hdrLen += 4;
+                }
+
+                hdrBuf = s->pool->alloc(env, s->pool, hdrLen);
+                bufp = hdrBuf;
+
+                for (i = 0; i < hdrCount; i++) {
+                    append(&bufp, s->headers_out->nameAt(env, s->headers_out, i));
+                    append(&bufp, ": ");
+                    append(&bufp, s->headers_out->valueAt(env, s->headers_out, i));
+                    append(&bufp, crlf);
+                }
+
+                append(&bufp, crlf);
+            } else {
+                hdrBuf = (char *) crlf;
+            }
+
+            frh.responseCode    = s->status;
+            frh.reasonText      = (char *) reason;
+            frh.headerText      = hdrBuf;
+
+            /* Send the headers */
+            rc = p->context->ServerSupport(p->context, kWriteResponseHeaders, &frh, NULL, 0, &errID);
+        }
+        return JK_OK;
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_ERROR, "jk_ws_service_t::cbHead, NULL parameters\n");
+
+    return JK_ERR;
+}
+
+/*
+ * Read a chunk of the request body into a buffer.  Attempt to read len
+ * bytes into the buffer.  Write the number of bytes actually read into
+ * nRead.
+ */
+static int JK_METHOD cbRead(struct jk_env *env, jk_ws_service_t *s,
+                              void *bytes, unsigned len, unsigned *nRead) {
+
+    if (s && s->ws_private && bytes && nRead) {
+        private_ws_t *p = s->ws_private;
+
+        /* Copy data from Domino's buffer. Although it seems slightly
+         * improbably we're believing that Domino always buffers the
+         * entire request in memory. Not properly tested yet.
+         */
+        if (len > p->reqSize) {
+            len = p->reqSize;
+        }
+        memcpy(bytes, p->reqBuffer, len);
+        p->reqBuffer += len;
+        p->reqSize -= len;
+        *nRead = len;
+
+        return JK_OK;
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_ERROR, "jk_ws_service_t::Read, NULL parameters\n");
+
+    return JK_ERR;
+}
+
+/*
+ * Write a chunk of response data back to the browser.
+ */
+static int JK_METHOD cbWrite(struct jk_env *env, jk_ws_service_t *s,
+                             const void *bytes, unsigned len) {
+
+    /* env->l->jkLog(env, env->l, JK_LOG_DEBUG, "Into jk_ws_service_t::Write\n"); */
+
+    if (s && s->ws_private && bytes) {
+        private_ws_t *p = s->ws_private;
+        int errID, rc;
+
+        /* Send the data */
+        if (len > 0) {
+            rc = p->context->WriteClient(p->context, (char *) bytes, len, 0, &errID);
+        }
+
+        return JK_OK;
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_ERROR, "jk_ws_service_t::Write, NULL parameters\n");
+
+    return JK_ERR;
+}
+
+/*
+ * Flush the output buffers.
+ */
+static int JK_METHOD cbFlush(struct jk_env *env, jk_ws_service_t *s) {
+    return JK_OK;
+}
+
+/* Return TRUE iff the specified filename is absolute. Note that this is only
+ * called in cases where the definition of 'absolute' is not security sensitive. I'm
+ * sure there are ways of constructing absolute Win32 paths that it doesn't catch.
+ */
+static int isAbsolutePath(const char *fn) {
+#ifdef WIN32
+    return fn[0] == '\\' || (isalpha(fn[0]) && fn[1] == ':');
+#else
+    return fn[0] == '/';
+#endif
+}
+
+static const char *makeAbsolutePath(jk_env_t *env, const char *base, const char *name) {
+    if (base == NULL || isAbsolutePath(name)) {
+        return name;
+    } else {
+        int bsz = strlen(base);
+        int nsz = strlen(name);
+        int ads = (base[bsz-1] != PATHSEP) ? 1 : 0;
+        char *buf;
+
+        if (buf = env->WORKPOOL->alloc(env, env->WORKPOOL, bsz + ads + nsz + 1), NULL == buf) {
+            return NULL;
+        }
+        memcpy(buf, base, bsz);
+        if (ads) {
+            buf[bsz] = PATHSEP;
+        }
+        memcpy(buf + bsz + ads, name, nsz);
+        buf[bsz + ads + nsz] = '\0';
+
+        return buf;
+    }
+}
+
+#ifdef WIN32
+static const char *readRegistry(jk_env_t *env, HKEY hkey, const char *key, const char *base) {
+    unsigned int type = 0;
+    unsigned int sz = 0;
+    LONG rc;
+    char *val;
+
+    rc = RegQueryValueEx(hkey, key, (unsigned int) 0, &type, NULL, &sz);
+    if (rc != ERROR_SUCCESS || type != REG_SZ) {
+        return NULL;
+    }
+
+    if (val = env->WORKPOOL->alloc(env, env->WORKPOOL, sz), NULL == val) {
+        return NULL;
+    }
+
+    rc = RegQueryValueEx(hkey, key, (unsigned int) 0, &type, val, &sz);
+    if (rc == ERROR_SUCCESS) {
+        return makeAbsolutePath(env, base, val);
+    }
+
+    return NULL;
+}
+#endif
+
+static int readFromRegistry(jk_env_t *env) {
+#ifdef WIN32
+    HKEY hkey;
+    long rc;
+    const char *timeout;
+
+    rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_LOCATION, (unsigned int) 0, KEY_READ, &hkey);
+    if (ERROR_SUCCESS != rc) {
+        return JK_FALSE;
+    }
+
+    serverRoot  = readRegistry(env, hkey, SERVER_ROOT_TAG,      NULL);
+    workersFile = readRegistry(env, hkey, WORKER_FILE_TAG,      serverRoot);
+    tomcatStart = readRegistry(env, hkey, TOMCAT_START_TAG,     serverRoot);
+    tomcatStop  = readRegistry(env, hkey, TOMCAT_STOP_TAG,      serverRoot);
+    timeout     = readRegistry(env, hkey, TOMCAT_TIMEOUT_TAG,   NULL);
+    if (timeout != NULL) {
+        tomcatTimeout = atoi(timeout);
+    }
+
+    RegCloseKey(hkey);
+
+    iniFileUsed = JK_FALSE;
+
+    return  NULL != serverRoot &&
+            NULL != workersFile;
+
+#else
+    return JK_FALSE;
+#endif
+}
+
+/* Read an entry from a map and return a newly allocated copy of it
+ * on success or NULL on failure.
+ */
+static const char *readMap(jk_env_t *env, jk_map_t *map, const char *name, const char *base)
+{
+    const char *s = map->get(env, map, name);
+    if (s) {
+        return makeAbsolutePath(env, base, env->WORKPOOL->pstrdup(env, env->WORKPOOL, s));
+    } else {
+        return NULL;
+    }
+}
+
+/* Read parameters from an ini file or the registry
+ */
+static int readConfigData(jk_env_t *env) {
+    jk_map_t *map;
+
+    /* Attempt to read from an ini file */
+    if (JK_OK == jk2_map_default_create(env, &map, env->WORKPOOL )) {
+        if (JK_OK == jk2_config_file_read(env, map, INI_NAME)) {
+            const char *timeout;
+
+            serverRoot  = readMap(env, map, SERVER_ROOT_TAG,    NULL);
+            workersFile = readMap(env, map, WORKER_FILE_TAG,    serverRoot);
+            tomcatStart = readMap(env, map, TOMCAT_START_TAG,   serverRoot);
+            tomcatStop  = readMap(env, map, TOMCAT_STOP_TAG,    serverRoot);
+            timeout     = readMap(env, map, TOMCAT_TIMEOUT_TAG, NULL);
+
+            if (timeout != NULL) {
+                tomcatTimeout = atoi(timeout);
+            }
+
+            iniFileUsed = JK_TRUE;
+            return  NULL != serverRoot &&
+                    NULL != workersFile;
+        }
+    } else {
+        /* can't log here */
+        /*env->l->jkLog(env, env->l, JK_LOG_ERROR,
+               "read_registry_init_data, Failed to create map \n");*/
+    }
+
+    return readFromRegistry(env);
+}
+
+/* Send a simple response. Used when we don't want to bother Tomcat,
+ * which in practice means for various error conditions that we can
+ * detect internally.
+ */
+static void simpleResponse(FilterContext *context, int status, char *reason, char *body) {
+    FilterResponseHeaders frh;
+    int rc, errID;
+    char hdrBuf[35];
+
+    sprintf(hdrBuf, "Content-type: text/html%s%s", crlf, crlf);
+
+    frh.responseCode    = status;
+    frh.reasonText      = reason;
+    frh.headerText      = hdrBuf;
+
+    rc = context->ServerSupport(context, kWriteResponseHeaders, &frh, NULL, 0, &errID);
+    rc = context->WriteClient(context, body, strlen(body), 0, &errID);
+}
+
+/* Called to reject a URI that contains the string "web-inf". We block
+ * these because they may indicate an attempt to invoke arbitrary code.
+ */
+static unsigned int rejectBadURI(FilterContext *context) {
+    static char *msg = "<html><body><h1>Access is Forbidden</h1></body></html>";
+
+    simpleResponse(context, 403, "Forbidden", msg);
+
+    return kFilterHandledRequest;
+}
+
+/* Called to generate a generic error response.
+ */
+static unsigned int rejectWithError(FilterContext *context, const char *diagnostic) {
+    static char *msg = "<html><body><h1>Error in Filter</h1><p>%s</p></body></html>";
+    char *mbuf;
+    int errID;
+    size_t bufSz;
+
+    if (!NONBLANK(diagnostic)) {
+        diagnostic = "Unspecified error";
+    }
+
+    /* This assumes that we're just going to expand the diagnostic into
+     * the page body.
+     */
+    bufSz = strlen(msg) + strlen(diagnostic) + 1;
+
+    mbuf = context->AllocMem(context, bufSz, 0, &errID);
+    sprintf(mbuf, msg, diagnostic);
+
+    simpleResponse(context, 500, "Error in Filter", mbuf);
+
+    return kFilterHandledRequest;
+}
+
+#if !defined(TESTING)
+/* Get the info from the lookup buffer
+ */
+static int getLookupInfo(FilterContext *context, char *pMatch, WORD itemNumber, char **pInfo) {
+    unsigned int reserved = 0;
+    unsigned int errID;
+    char *pValue = NULL;
+    WORD lValue, type;
+    STATUS error;
+
+    if (NULL == pMatch || NULL == pInfo) {
+        return -1;
+    }
+
+    /* Initialize output */
+    *pInfo = NULL;
+
+    /* Check the type and length of the info */
+    pValue = (char *) NAMELocateItem(pMatch, itemNumber, &type, &lValue);
+
+    if (NULL == pValue || lValue == 0) {
+        return -1;
+    }
+
+    lValue -= sizeof(WORD); /* remove datatype word included in the list length */
+    lValue++; /* Include length of terminator */
+
+    /* check the value type */
+    if (type != TYPE_TEXT_LIST && type != TYPE_TEXT) {
+        return -1;
+    }
+
+    /* Allocate space for the info. This memory will be freed automatically when the thread terminates */
+    if (*pInfo = context->AllocMem(context, lValue, reserved, &errID), NULL == *pInfo) {
+        return -1;
+    }
+
+    /* Get the info */
+    if (error = NAMEGetTextItem(pMatch, itemNumber, 0, *pInfo, lValue), !error) {
+        return 0;
+    }
+
+    return -1;
+}
+#endif
+
+/* Lookup the user and return the user's full name
+ */
+static int getUserName(FilterContext *context, char *userName, char **pUserName) {
+#if defined(TESTING)
+	*pUserName = userName;
+	return NOERROR;
+#else
+    STATUS error = NOERROR;
+    HANDLE hLookup = NULLHANDLE;
+    unsigned short nMatches = 0;
+    char *pLookup = NULL;
+    char *pName = NULL;
+    char *pMatch = NULL;
+    int rc = -1;
+
+    if (NULL == userName || NULL == pUserName) {
+        return rc;
+    }
+
+    /* Initializae output */
+    *pUserName = NULL;
+
+    /* do the name lookup */
+    error = NAMELookup(NULL, 0, 1, "$Users", 1, userName, 2, "FullName", &hLookup);
+
+    if (error || (NULLHANDLE == hLookup)) {
+        goto done;
+    }
+
+    pLookup = (char *) OSLockObject(hLookup);
+
+    /* Get pointer to our entry. */
+    pName = (char *) NAMELocateNextName(pLookup, NULL, &nMatches);
+
+    /* If we didn't find the entry, the quit */
+    if (NULL == pName || nMatches <= 0) {
+        goto done;
+    }
+
+    if (pMatch = (char *) NAMELocateNextMatch(pLookup, pName, NULL), NULL == pMatch) {
+        goto done;
+    }
+
+    /* Get the full name from the info we got back */
+    if (getLookupInfo(context, pMatch, 0, pUserName)) {
+        goto done;
+    }
+
+    rc = 0;
+
+done:
+    if(NULLHANDLE != hLookup) {
+        if (NULL != pLookup) {
+            OSUnlock(hLookup);
+        }
+        OSMemFree(hLookup);
+    }
+    return rc;
+#endif
+}
+
+/* Given all the HTTP headers as a single string parse them into individual
+ * name, value pairs.
+ */
+static int parseHeaders(jk_env_t *env, jk_ws_service_t *s, const char *hdrs, int hdrSz) {
+    int hdrCount = 0;
+    const char *limit = hdrs + hdrSz;
+    const char *name, *nameEnd;
+    const char *value, *valueEnd;
+    int gotContentLength = JK_FALSE;
+    char buf[256];      /* Static buffer used for headers that are short enough to fit
+                         * in it. A dynamic buffer is used for any longer headers.
+                         */
+
+    while (hdrs < limit) {
+        /* buf is re-used for each header */
+        char *bufp = buf;
+        char *hdrName, *hdrValue;
+        int sz = sizeof(buf);
+
+        /* Skip line *before* doing anything, cos we want to lose the first line which
+         * contains the request. This code also moves to the next line after each header.
+         */
+        while (hdrs < limit && (*hdrs != '\n' && *hdrs != '\r')) {
+            hdrs++;
+        }
+
+        while (hdrs < limit && (*hdrs == '\n' || *hdrs == '\r')) {
+            hdrs++;
+        }
+
+        if (hdrs >= limit) {
+            break;
+        }
+
+        name = nameEnd = value = valueEnd = NULL;
+        name = hdrs;
+        while (hdrs < limit && *hdrs >= ' ' && *hdrs != ':') {
+            hdrs++;
+        }
+
+        nameEnd = hdrs;
+        if (hdrs < limit && *hdrs == ':') {
+            hdrs++;
+            while (hdrs < limit && (*hdrs == ' ' || *hdrs == '\t')) {
+                hdrs++;
+            }
+            value = hdrs;
+            while (hdrs < limit && *hdrs >= ' ') {
+                hdrs++;
+            }
+            valueEnd = hdrs;
+        }
+
+        hdrName     = smartSubStr(env, s->pool, &bufp, &sz, name, nameEnd);
+        /* Need to strdup the value because map->put doesn't for some reason */
+        hdrValue    = subStr(env, s->pool, value, valueEnd);
+
+        s->headers_in->put(env, s->headers_in, hdrName, hdrValue, NULL);
+
+        gotContentLength |= (noCaseStrCmp(hdrName, CONTENT_LENGTH) == 0);
+
+        hdrCount++;
+    }
+
+    /* Add a zero length content-length header if none was found in the
+     * request.
+     */
+    if (!gotContentLength) {
+        s->headers_in->put(env, s->headers_in, CONTENT_LENGTH, "0", NULL);
+        hdrCount++;
+    }
+
+    return hdrCount;
+}
+
+/* Initialize the service structure
+ */
+static int processRequest(struct jk_env *env, jk_ws_service_t *s,
+                    struct jk_worker *w, FilterRequest *fr) {
+    /* This is the only fixed size buffer left. It won't be overflowed
+     * because the Domino API that reads into the buffer accepts a length
+     * constraint, and it's unlikely ever to be exhausted because the
+     * strings being will typically be short, but it's still aesthetically
+     * troublesome.
+     */
+    char workBuf[16 * 1024];
+    private_ws_t *ws = (private_ws_t *) s->ws_private;
+    char *hdrs;
+    int hdrsz;
+    int errID = 0;
+    int hdrCount;
+    long port;
+
+    static char *methodName[] = { "", "HEAD", "GET", "POST", "PUT", "DELETE" };
+
+    s->jvm_route = NULL;
+
+    GETVARIABLE("AUTH_TYPE", &s->auth_type, "");
+    GETVARIABLE("REMOTE_USER", &s->remote_user, "");
+
+    /* If the REMOTE_USER CGI variable doesn't work try asking Domino */
+    if (NULLSTR(s->remote_user) && !NULLSTR(fr->userName)) {
+        getUserName(ws->context, fr->userName, &s->remote_user);
+    }
+
+    GETVARIABLE("SERVER_PROTOCOL", &s->protocol, "");
+    GETVARIABLE("REMOTE_HOST", &s->remote_host, "");
+    GETVARIABLE("REMOTE_ADDR", &s->remote_addr, "");
+    GETVARIABLE("SERVER_NAME", &s->server_name, "");
+    GETVARIABLEINT("SERVER_PORT", &port, 80);
+    GETVARIABLE("SERVER_SOFTWARE", &s->server_software, SERVERDFLT);
+    GETVARIABLEINT("CONTENT_LENGTH", &s->content_length, 0);
+
+    s->server_port = (int) port;
+
+    /* SSL Support
+     */
+    GETVARIABLEBOOL("HTTPS", &s->is_ssl, 0);
+
+    if (ws->reqData->requestMethod < 0 ||
+        ws->reqData->requestMethod >= sizeof(methodName) / sizeof(methodName[0])) {
+        return JK_ERR;
+    }
+
+    s->method           = methodName[ws->reqData->requestMethod];
+    s->ssl_cert_len     = fr->clientCertLen;
+    s->ssl_cert         = fr->clientCert;
+    s->ssl_cipher       = NULL;
+    s->ssl_session      = NULL;
+    s->ssl_key_size     = -1;
+
+    if (JK_OK != jk2_map_default_create(env, &s->headers_out, s->pool)) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, "jk_ws_service_t::init, Failed to create headers_out map\n");
+        return JK_ERR;
+    }
+
+    if (JK_OK != jk2_map_default_create(env, &s->attributes, s->pool)) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, "jk_ws_service_t::init, Failed to create attributes map\n");
+        return JK_ERR;
+    }
+
+    if (JK_OK != jk2_map_default_create(env, &s->headers_in, s->pool)) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, "jk_ws_service_t::init, Failed to create headers_in map\n");
+        return JK_ERR;
+    }
+
+    if (s->is_ssl) {
+        int i;
+        long dummy;
+
+        /* It seems that Domino doesn't actually expose many of these but we live in hope.
+         */
+        char *sslNames[] = {
+            "CERT_ISSUER", "CERT_SUBJECT", "CERT_COOKIE", "CERT_FLAGS", "CERT_SERIALNUMBER",
+            "HTTPS_SERVER_SUBJECT", "HTTPS_SECRETKEYSIZE", "HTTPS_SERVER_ISSUER", "HTTPS_KEYSIZE"
+        };
+
+        /* env->l->jkLog(env, env->l, JK_LOG_DEBUG, "Request is SSL\n"); */
+
+        /* Read the variable into a dummy variable: we do this for the side effect of
+         * reading it into workBuf.
+         */
+        GETVARIABLEINT("HTTPS_KEYSIZE", &dummy, 0);
+        if (workBuf[0] == '[') {
+            s->ssl_key_size = atoi(workBuf+1);
+        }
+
+        /* Should also try to make suitable values for s->ssl_cipher and
+         * s->ssl_session
+         */
+        for (i = 0; i < sizeof(sslNames) / sizeof(sslNames[0]); i++) {
+            char *value = NULL;
+            GETVARIABLE(sslNames[i], &value, NULL);
+            if (value) {
+                s->attributes->put(env, s->attributes, sslNames[i], value, NULL);
+            }
+        }
+    }
+
+    /* Duplicate all the headers now
+     */
+    hdrsz = ws->reqData->GetAllHeaders(ws->context, &hdrs, &errID);
+    if (0 == hdrsz) {
+        return JK_ERR;
+    }
+
+    hdrCount = parseHeaders(env, s, hdrs, hdrsz);
+
+    return JK_OK;
+}
+
+/* Handle an HTTP request. Works out whether Tomcat will be interested then either
+ * despatches it to Tomcat or passes it back to Domino.
+ */
+static unsigned int parsedRequest(FilterContext *context, FilterParsedRequest *reqData) {
+    unsigned int errID;
+    int rc;
+    FilterRequest fr;
+    int result = kFilterNotHandled;
+    char *h = NULL;
+
+    /* TODO: presumably this return code should be checked */
+    rc = context->GetRequest(context, &fr, &errID);
+
+    if (NONBLANK(fr.URL)) {
+        char *uri = fr.URL;
+        char *qp, *turi;
+        jk_uriEnv_t *uriEnv = NULL;
+        int errID;
+        char buf[256];  /* enough for the server's name */
+        char *colon;
+        char *serverName;
+        size_t serverNameSz;
+        int serverPort;
+        char *uriBuf;
+        size_t uriSz, uriBufSz;
+        jk_env_t *env = workerEnv->globalEnv->getEnv(workerEnv->globalEnv);
+
+        /* env->l->jkLog(env, env->l, JK_LOG_DEBUG, "parsedRequest() - %s\n", uri); */
+
+        /* We used to call the context->GetServerVariable() API here but doing so
+         * seems to clobber some of the server's CGI variables in the case where
+         * we don't handle the request.
+         *
+         * Note also that we're using a static buffer for the host header. Presumably
+         * hostnames longer than 255 characters are either rare or illegal and there's
+         * no buffer overrun risk because Domino errors if the supplied buffer is too
+         * small.
+         */
+        if (!reqData->GetHeader(context, "host", buf, sizeof(buf), &errID)) {
+            return rejectWithError(context, "Failed to retrieve host");
+        }
+
+        serverName = buf;
+        /* Parse out the port number */
+        if (colon = strchr(serverName, ':'), NULL != colon) {
+            *colon++ = '\0';
+            serverPort = atoi(colon);
+        } else {
+            serverPort = 80;
+        }
+
+        serverNameSz = strlen(serverName) + 1;
+
+        uriBuf   = serverName + serverNameSz;
+        uriBufSz = sizeof(buf) - serverNameSz;
+        uriSz    = strlen(uri) + 1;
+
+        /* Use the stack buffer for sufficiently short URIs */
+        if (uriSz <= uriBufSz) {
+            turi = uriBuf;
+        } else {
+            turi = context->AllocMem(context, uriSz, 0, &errID);
+        }
+        memcpy(turi, uri, uriSz);
+
+        if (qp = strchr(turi, '?'), qp != NULL) {
+            *qp++ = '\0';
+        }
+
+        rc = jk_requtil_unescapeUrl(turi);
+        if (rc < 0) {
+            return rejectBadURI(context);
+        }
+
+        jk_requtil_getParents(turi);
+
+        if (badURI(turi)) {
+            return rejectBadURI(context);
+        }
+
+        uriEnv = workerEnv->uriMap->mapUri(env, workerEnv->uriMap, serverName, serverPort, turi);
+
+        if (NULL != uriEnv) {
+            // Here we go
+            private_ws_t ws;
+            jk_ws_service_t s;
+            jk_pool_t *rPool = NULL;
+            jk_worker_t *worker = uriEnv->worker;
+
+            rPool = worker->rPoolCache->get(env, worker->rPoolCache);
+            if (NULL == rPool) {
+                rPool = worker->mbean->pool->create(env, worker->mbean->pool, HUGE_POOL_SIZE);
+                /* env->l->jkLog(env, env->l, JK_LOG_DEBUG, "HttpExtensionProc: new rpool\n"); */
+            }
+
+            jk2_requtil_initRequest(env, &s);
+
+            /* reset the reco_status, will be set to INITED in LB mode */
+            s.reco_status           = RECO_NONE;
+
+            s.pool                  = rPool;
+            s.is_recoverable_error  = JK_FALSE;
+            s.response_started      = JK_FALSE;
+            s.content_read          = 0;
+            s.ws_private            = &ws;
+            s.workerEnv             = workerEnv;
+            s.head                  = cbHead;
+            s.read                  = cbRead;
+            s.write                 = cbWrite;
+            s.init                  = cbInit;           /* never seems to be used */
+            s.afterRequest          = cbAfterRequest;
+
+            if (workerEnv->options == JK_OPT_FWDURICOMPATUNPARSED) {
+                size_t sz = strlen(uri) + 1;
+                s.req_uri = context->AllocMem(context, sz, 0, &errID);
+                memcpy(s.req_uri, uri, sz);
+                /* Find the query string again in the original URI */
+                if (qp = strchr(s.req_uri, '?'), NULL != qp) {
+                    *qp++ = '\0';
+                }
+            } else if (workerEnv->options == JK_OPT_FWDURIESCAPED) {
+                /* Nasty static buffer */
+                char euri[256];
+                if (jk_requtil_escapeUrl(turi, buf, sizeof(euri))) {
+                    turi = euri;
+                }
+                s.req_uri = turi;
+            } else {
+                s.req_uri = turi;
+            }
+
+            s.query_string      = qp;
+
+            /* Init our private structure
+             */
+            ws.responseStarted  = JK_FALSE;
+            ws.context          = context;
+            ws.reqData          = reqData;
+
+            /* Fetch info about the request
+             */
+            ws.reqSize          = context->GetRequestContents(context, &ws.reqBuffer, &errID);
+
+            rc = processRequest(env, &s, worker, &fr);
+
+            if (JK_OK == rc) {
+                rc = worker->service(env, uriEnv->worker, &s);
+            }
+
+            if (JK_OK == rc) {
+                result = kFilterHandledRequest;
+                /* env->l->jkLog(env, env->l, JK_LOG_DEBUG, "HttpExtensionProc service() returned OK\n"); */
+            } else {
+                result = kFilterError;
+                env->l->jkLog(env, env->l, JK_LOG_ERROR, "HttpExtensionProc error, service() failed\n");
+            }
+
+            rPool->reset(env, rPool);
+            rc = worker->rPoolCache->put(env, worker->rPoolCache, rPool);
+        }
+
+        workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+    }
+
+    return result;
+}
+
+static int runProg(const char *cmd) {
+#ifdef WIN32
+    STARTUPINFO si;
+    PROCESS_INFORMATION pi;
+
+    memset(&si, 0, sizeof(si));
+    si.cb           = sizeof(si);    // Start the child process.
+    si.dwFlags      = STARTF_USESHOWWINDOW;
+    si.wShowWindow  = SW_SHOWMAXIMIZED;
+
+    if (!CreateProcess(NULL, (char *) cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
+        DWORD err = GetLastError();
+        AddInLogMessageText("Command \"%s\" failed (error %u)", NOERROR, cmd, err);
+        return JK_FALSE;
+    }
+
+    if (WAIT_OBJECT_0 == WaitForSingleObject(pi.hProcess, tomcatTimeout)) {
+        return JK_TRUE;
+    }
+
+    AddInLogMessageText("Command \"%s\" didn't complete in time", NOERROR, cmd);
+    return JK_FALSE;
+#else
+    int err = system(cmd);
+    if (0 == err) {
+        return 1;
+    }
+    AddInLogMessageText("Command \"%s\" failed (error %d)", NOERROR, cmd, err);
+    return 0;
+#endif
+}
+
+/* Main entry point for the filter. Called by Domino for every HTTP request.
+ */
+DLLEXPORT unsigned int HttpFilterProc(FilterContext *context, unsigned int eventType, void *eventData) {
+    switch (eventType) {
+    case kFilterParsedRequest:
+        return parsedRequest(context, (FilterParsedRequest *) eventData);
+    default:
+        break;
+    }
+
+    return kFilterNotHandled;
+}
+
+/* Called when the filter is unloaded. Free various resources and
+ * display a banner.
+ */
+DLLEXPORT unsigned int TerminateFilter(unsigned int reserved) {
+    /*jk_env_t *env = workerEnv->globalEnv->getEnv(workerEnv->globalEnv);*/
+
+    // TODO: Work out if we're doing everything we need to here
+    if (isInited) {
+        if (workerEnv) {
+            jk_env_t *env = workerEnv->globalEnv;
+            workerEnv->close(env, workerEnv);
+        }
+
+        isInited = JK_FALSE;
+    }
+
+#if !defined(TESTING)
+    if (NONBLANK(tomcatStop)) {
+        AddInLogMessageText("Attempting to stop Tomcat: %s", NOERROR, tomcatStop);
+        runProg(tomcatStop);
+    }
+#endif
+
+    AddInLogMessageText("%s unloaded", NOERROR, filterdesc);
+
+    apr_pool_destroy(jk_globalPool);
+
+    return kFilterHandledEvent;
+}
+
+/* Called when Domino loads the filter. Reads a load of config data from
+ * the registry and elsewhere and displays a banner.
+ */
+DLLEXPORT unsigned int FilterInit(FilterInitData *filterInitData) {
+    jk_logger_t *l;
+    jk_pool_t *globalPool;
+    jk_bean_t *jkb;
+    jk_env_t *env;
+
+    apr_initialize();
+    apr_pool_create(&jk_globalPool, NULL);
+
+    jk2_pool_apr_create(NULL, &globalPool, NULL, jk_globalPool);
+
+    /* Create the global environment. This will register the default
+     * factories
+     */
+    env = jk2_env_getEnv(NULL, globalPool);
+
+    if (!readConfigData(env)) {
+        goto initFailed;
+    }
+
+    /* Create the workerEnv.
+     */
+    jkb = env->createBean2(env, env->globalPool,"workerEnv", "");
+    workerEnv = jkb->object;
+    env->alias(env, "workerEnv:", "workerEnv");
+    workerEnv->childId = 0;
+
+    if (NULL == workerEnv) {
+        goto initFailed;
+    }
+
+    /* Create the logger
+     */
+#if defined(TESTING)
+    env->registerFactory(env, "logger.printf", jk2_logger_printf_factory);
+#else
+    env->registerFactory(env, "logger.domino", jk2_logger_domino_factory );
+#endif
+
+    jkb = env->createBean2(env, env->globalPool, LOGGER, "");
+    env->alias(env, LOGGER ":", "logger");
+    l = jkb->object;
+    env->l = l;
+
+#ifdef WIN32
+    env->soName = env->globalPool->pstrdup(env, env->globalPool, libFileName);
+    if (NULL == env->soName) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR, "Error creating env->soName\n");
+        goto initFailed;
+    }
+#else
+    env->soName = NULL;
+#endif
+
+    workerEnv->initData->add(env, workerEnv->initData, "serverRoot",
+                             workerEnv->pool->pstrdup(env, workerEnv->pool, serverRoot));
+
+    /* Note: we cast away the const qualifier on workersFile here
+     */
+    if (JK_OK != workerEnv->config->setPropertyString(env, workerEnv->config,
+                                                        "config.file", (char *) workersFile)) {
+        goto initFailed;
+    }
+
+    env->l->init(env, env->l);
+
+    workerEnv->init(env, workerEnv);
+
+#if !defined(TESTING)
+    /* Attempt to launch Tomcat
+     */
+    if (NONBLANK(tomcatStart)) {
+        AddInLogMessageText("Attempting to start Tomcat: %s", NOERROR, tomcatStart);
+        runProg(tomcatStart);
+    }
+#endif
+
+    filterInitData->appFilterVersion    = kInterfaceVersion;
+    filterInitData->eventFlags          = kFilterParsedRequest;
+
+    strncpy(filterInitData->filterDesc, filterdesc, sizeof(filterInitData->filterDesc));
+    isInited = JK_TRUE;
+
+    /* Display banner
+     */
+    AddInLogMessageText("%s loaded", NOERROR, filterInitData->filterDesc);
+
+    return kFilterHandledEvent;
+
+initFailed:
+    AddInLogMessageText("Error loading %s", NOERROR, filterdesc);
+    return kFilterError;
+}
+
+#ifdef WIN32
+/* Handle DLL initialisation (on WIN32) by working out what the INI file
+ * should be called.
+ */
+BOOL WINAPI DllMain(HINSTANCE hInst, ULONG ulReason, LPVOID lpReserved) {
+    BOOL fReturn = TRUE;
+    char drive[_MAX_DRIVE];
+    char dir[_MAX_DIR];
+    char fname[_MAX_FNAME];
+
+    switch (ulReason) {
+    case DLL_PROCESS_ATTACH:
+        if (GetModuleFileName(hInst, libFileName, sizeof(libFileName))) {
+            _splitpath(libFileName, drive, dir, fname, NULL);
+            _makepath(iniFileName, drive, dir, fname, PROPERTIES_EXT);
+        } else {
+            fReturn = FALSE;
+        }
+        break;
+    default:
+        break;
+    }
+    return fReturn;
+}
+#endif
+
+#if defined(TESTING)
+/* Handle initialisation in the test harness environment.
+ */
+void TestMain(void) {
+    strcpy(libFileName, "test.exe");
+    strcpy(iniFileName, "test" PROPERTIES_EXT);
+}
+#endif
diff --git a/connectors/jk/native2/server/dsapi/jk_logger_domino.c b/connectors/jk/native2/server/dsapi/jk_logger_domino.c
new file mode 100644
index 0000000..63f2496
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/jk_logger_domino.c
@@ -0,0 +1,172 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Utility functions (mainly configuration)                   *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Version:     $Revision$                                          *
+ ***************************************************************************/
+
+
+#include "jk_env.h"
+#include "jk_map.h"
+#include "jk_logger.h"
+
+#include <global.h>
+#include <addin.h>
+
+static int JK_METHOD jk2_logger_domino_log(jk_env_t *env, jk_logger_t *l,
+                                         int level, const char *what) {
+
+    if (l && l->level <= level && NULL != what) {
+        AddInLogMessageText("%s", NOERROR, what);
+        return JK_OK;
+    }
+
+    return JK_ERR;
+}
+
+int jk2_logger_domino_parseLogLevel(jk_env_t *env, const char *level) {
+
+    if (!level)
+        return JK_LOG_INFO_LEVEL;
+
+    if (!strcasecmp(level, JK_LOG_INFO_VERB))
+        return JK_LOG_INFO_LEVEL;
+
+    if (!strcasecmp(level, JK_LOG_ERROR_VERB))
+        return JK_LOG_ERROR_LEVEL;
+
+    if (!strcasecmp(level, JK_LOG_EMERG_VERB))
+        return JK_LOG_EMERG_LEVEL;
+
+    return JK_LOG_DEBUG_LEVEL;
+}
+
+static int JK_METHOD jk2_logger_domino_init(jk_env_t *env, jk_logger_t *_this) {
+    return JK_OK;
+}
+
+static int jk2_logger_domino_close(jk_env_t *env, jk_logger_t *_this) {
+    return JK_OK;
+}
+
+static int JK_METHOD
+jk2_logger_domino_setProperty(jk_env_t *env, jk_bean_t *mbean,
+                            char *name, void *valueP) {
+
+    jk_logger_t *_this = mbean->object;
+    char *value = valueP;
+    if (!strcmp(name, "level")) {
+        _this->level = jk2_logger_domino_parseLogLevel(env, value);
+        if (_this->level == 0) {
+            _this->jkLog(env, _this, JK_LOG_INFO,
+                         "Level %s %d \n", value, _this->level);
+        }
+    }
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_logger_domino_jkVLog(jk_env_t *env, jk_logger_t *l,
+                                            const char *file,
+                                            int line,
+                                            int level,
+                                            const char *fmt, va_list args) {
+
+    char *buf;
+    char *fmt1;
+    apr_pool_t *aprPool = env->tmpPool->_private;
+
+    if (!file || !args) {
+        return -1;
+    }
+
+    if (l->level <= level) {
+        char *f = (char *)(file + strlen(file) - 1);
+        char *slevel;
+        switch (level) {
+        case JK_LOG_INFO_LEVEL:
+            slevel = JK_LOG_INFO_VERB;
+            break;
+        case JK_LOG_ERROR_LEVEL:
+            slevel = JK_LOG_ERROR_VERB;
+            break;
+        case JK_LOG_EMERG_LEVEL:
+            slevel = JK_LOG_EMERG_VERB;
+            break;
+        case JK_LOG_DEBUG_LEVEL:
+        default:
+            slevel = JK_LOG_DEBUG_VERB;
+            break;
+        }
+        while (f != file && *f != '\\' && *f != '/') {
+            f--;
+        }
+        if (f != file) {
+            ++f;
+        }
+
+        fmt1 = apr_psprintf(aprPool, "(%5s ) [%s (%d)] %s", slevel, f, line, fmt);
+        buf = apr_pvsprintf(aprPool, fmt1, args);
+
+        l->log(env, l, level, buf);
+    }
+
+    return 0;
+}
+
+
+static int jk2_logger_domino_jkLog(jk_env_t *env, jk_logger_t *l,
+                                 const char *file,
+                                 int line, int level, const char *fmt, ...) {
+
+    va_list args;
+    int rc;
+
+    va_start(args, fmt);
+    rc = jk2_logger_domino_jkVLog(env, l, file, line, level, fmt, args);
+    va_end(args);
+
+    return rc;
+}
+
+int JK_METHOD jk2_logger_domino_factory(jk_env_t *env, jk_pool_t *pool,
+                                      jk_bean_t *result,
+                                      const char *type, const char *name) {
+
+    jk_logger_t *log =
+        (jk_logger_t *)pool->calloc(env, pool, sizeof(jk_logger_t));
+
+    if (log == NULL) {
+        fprintf(stderr, "loggerDomino.factory(): OutOfMemoryException\n");
+        return JK_ERR;
+    }
+
+    log->log = jk2_logger_domino_log;
+    log->logger_private = NULL;
+    log->init = jk2_logger_domino_init;
+    log->jkLog = jk2_logger_domino_jkLog;
+    log->jkVLog = jk2_logger_domino_jkVLog;
+    log->level = JK_LOG_ERROR_LEVEL;
+
+    result->object = log;
+    log->mbean = result;
+
+    result->setAttribute = jk2_logger_domino_setProperty;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/server/dsapi/libtomcat2.properties b/connectors/jk/native2/server/dsapi/libtomcat2.properties
new file mode 100644
index 0000000..6f73fa5
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/libtomcat2.properties
@@ -0,0 +1,5 @@
+serverRoot=/usr/local/apache/tomcat
+workersFile=conf/workers2.properties
+tomcatStart=bin/startup.sh
+tomcatStop=bin/shutdown.sh
+tomcatTimeout=30000
diff --git a/connectors/jk/native2/server/dsapi/test/printf_logger.c b/connectors/jk/native2/server/dsapi/test/printf_logger.c
new file mode 100644
index 0000000..3e17178
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/test/printf_logger.c
@@ -0,0 +1,219 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Utility functions (mainly configuration)                   *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Henri Gomez <hgomez@apache.org>                            *
+ * Author:      Andy Armstrong <andy@tagish.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+
+#include "jk_env.h"
+#include "jk_map.h"
+#include "jk_logger.h"
+#include <stdio.h>
+#include <fcntl.h>
+
+#define LOG_FORMAT          ("log_format")
+
+#define HUGE_BUFFER_SIZE (8*1024)
+#define LOG_LINE_SIZE    (1024)
+
+
+/* 
+ * define the log format, we're using by default the one from error.log 
+ *
+ * [Mon Mar 26 19:44:48 2001] [jk_uri_worker_map.c (155)]: Into jk_uri_worker_map_t::uri_worker_map_alloc
+ * log format used by apache in error.log
+ */
+#ifndef JK_TIME_FORMAT 
+#define JK_TIME_FORMAT "[%a %b %d %H:%M:%S %Y] "
+#endif
+
+static const char * jk2_logger_printf_logFmt = JK_TIME_FORMAT;
+
+static void jk2_logger_printf_setTimeStr(jk_env_t *env, char *str, int len)
+{
+    apr_time_exp_t gmt;
+    apr_size_t     l;
+
+    apr_time_exp_gmt(&gmt, apr_time_now());
+    apr_strftime(str, &l, len, jk2_logger_printf_logFmt, &gmt);
+}
+
+static int JK_METHOD jk2_logger_printf_log(jk_env_t *env, jk_logger_t *l,                                 
+                               int level,
+                               const char *what) {
+
+    if (what != NULL) {
+        fprintf(stderr, "%s", what);
+	}
+
+    return JK_OK;
+}
+
+int jk2_logger_printf_parseLogLevel(jk_env_t *env, const char *level)
+{
+    if (!level)
+        return JK_LOG_INFO_LEVEL;
+    
+    if (!strcasecmp(level, JK_LOG_INFO_VERB))
+        return JK_LOG_INFO_LEVEL;
+
+    if (!strcasecmp(level, JK_LOG_ERROR_VERB))
+        return JK_LOG_ERROR_LEVEL;
+
+    if (!strcasecmp(level, JK_LOG_EMERG_VERB))
+        return JK_LOG_EMERG_LEVEL;
+
+    return JK_LOG_DEBUG_LEVEL;
+}
+
+static int JK_METHOD jk2_logger_printf_init(jk_env_t *env, jk_logger_t *_this )
+{
+    return JK_OK;
+}
+
+static int jk2_logger_printf_close(jk_env_t *env,jk_logger_t *_this)
+{
+	fflush(stderr);
+    return JK_OK;
+}
+
+static int JK_METHOD
+jk2_logger_printf_setProperty(jk_env_t *env, jk_bean_t *mbean, 
+                            char *name,  void *valueP )
+{
+#if 0
+    jk_logger_t *_this = mbean->object;
+    char *value = valueP;
+    if (!strcmp(name, "name"))
+        _this->name = (char *)value;
+    else if (!strcmp(name, "file")) {
+        _this->name = (char *)value;
+        /* Set the file imediately */
+        jk2_logger_printf_init(env, (jk_logger_t *)mbean->object);
+    } 
+    else if (!strcmp(name, "timeFormat"))
+        jk2_logger_printf_logFmt = value;
+    else if (!strcmp(name, "level")) {
+        _this->level = jk2_logger_printf_parseLogLevel(env, value);
+        if( _this->level == 0) {
+            _this->jkLog(env, _this, JK_LOG_INFO,
+                         "Level %s %d \n", value, _this->level);
+        }
+    }
+#endif
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_logger_printf_jkVLog(jk_env_t *env, jk_logger_t *l,
+                                  const char *file,
+                                  int line,
+                                  int level,
+                                  const char *fmt,
+                                  va_list args)
+{
+    int rc = 0;
+    char *buf;
+    char *fmt1;
+    apr_pool_t *aprPool = env->tmpPool->_private;
+    char rfctime[APR_RFC822_DATE_LEN];
+    apr_time_t time = apr_time_now();
+    
+    if (!file || !args)
+        return -1;
+
+    if (l->logger_private == NULL ||
+        l->level <= level) {
+        char *f = (char *)(file + strlen(file) - 1);
+        char *slevel;
+        switch (level){
+            case JK_LOG_INFO_LEVEL:
+                slevel = JK_LOG_INFO_VERB;
+                break;
+            case JK_LOG_ERROR_LEVEL:
+                slevel = JK_LOG_ERROR_VERB;
+                break;
+            case JK_LOG_EMERG_LEVEL:
+                slevel = JK_LOG_EMERG_VERB;
+                break;
+            case JK_LOG_DEBUG_LEVEL:
+            default:
+                slevel = JK_LOG_DEBUG_VERB;
+                break;
+        }
+        while (f != file && *f != '\\' && *f != '/')
+            f--;
+        if (f != file)
+            ++f;
+        
+        /* XXX rfc822_date or apr_ctime ? */
+        apr_ctime(rfctime, time);
+        fmt1 = apr_psprintf(aprPool, "[%s] (%5s ) [%s (%d)]  %s", rfctime, slevel, f, line, fmt);
+        buf = apr_pvsprintf(aprPool, fmt1, args);
+
+        l->log(env, l, level, buf);        
+    }
+    
+    return rc;
+}
+
+
+static int jk2_logger_printf_jkLog(jk_env_t *env, jk_logger_t *l,
+                                 const char *file,
+                                 int line,
+                                 int level,
+                                 const char *fmt, ...)
+{
+    va_list args;
+    int rc;
+    
+    va_start(args, fmt);
+    rc = jk2_logger_printf_jkVLog(env, l, file, line, level, fmt, args);
+    va_end(args);
+
+    return rc;
+}
+
+int JK_METHOD jk2_logger_printf_factory(jk_env_t *env, jk_pool_t *pool, 
+                                      jk_bean_t *result,
+                                      const char *type, const char *name)
+{
+    jk_logger_t *log = (jk_logger_t *)pool->calloc(env, pool, sizeof(jk_logger_t));
+
+    if (log == NULL) {
+        fprintf(stderr, "loggerFile.factory(): OutOfMemoryException\n"); 
+        return JK_ERR;
+    }
+
+    log->log = jk2_logger_printf_log;
+    log->logger_private = NULL;
+    log->init =jk2_logger_printf_init;
+    log->jkLog = jk2_logger_printf_jkLog;
+    log->jkVLog = jk2_logger_printf_jkVLog;
+    log->level = JK_LOG_ERROR_LEVEL;
+    jk2_logger_printf_logFmt = JK_TIME_FORMAT;
+    
+    result->object = log;
+    log->mbean = result;
+    
+    result->setAttribute = jk2_logger_printf_setProperty;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/native2/server/dsapi/test/test.c b/connectors/jk/native2/server/dsapi/test/test.c
new file mode 100644
index 0000000..a23b697
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/test/test.c
@@ -0,0 +1,193 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Test harness for DSAPI plugin for Lotus Domino             *
+ * Author:      Andy Armstrong <andy@tagish.com>                           *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#include <stdio.h>
+#include <string.h>	
+#include <stdlib.h>
+
+#include "config.h"
+#include "dsapifilter.h"
+
+unsigned int FilterInit(FilterInitData *filterInitData);
+unsigned int TerminateFilter(unsigned int reserved);
+unsigned int HttpFilterProc(FilterContext *context, unsigned int eventType, void *eventData);
+void TestMain(void);
+
+/* Hardwired HTTP request.
+ */
+
+#define SERVER		"localhost"
+#define PORT		"80"
+#define URI			"/examples/jsp/snp/snoop.jsp"
+#define HTTPVERSION	"1.1"
+
+typedef struct _ServerContext {
+
+	char *hdrs;
+
+} ServerContext;
+
+/* FilterContext functions */
+static int fcGetRequest(struct _FilterContext *context, FilterRequest *fr,
+						unsigned int *errID) {
+	printf("fc.GetRequest(%p, %p, %p)\n", context, fr, errID);
+	
+	fr->method			= kRequestGET;
+	fr->URL				= URI;
+	fr->version			= HTTPVERSION;
+	fr->userName		= NULL;
+	fr->password		= NULL;
+	fr->clientCert		= NULL;
+	fr->clientCertLen	= 0;
+	fr->contentRead		= NULL;
+	fr->contentReadLen	= 0;
+
+	return 1;
+}
+
+static int fcGetRequestContents(struct _FilterContext *context, char **contents,
+								unsigned int *errID) {
+	printf("fc.GetRequestContents(%p, %p, %p)\n", context, contents, errID);
+	*contents = "";
+	return 0;
+}
+
+static int fcGetServerVariable(struct _FilterContext *context, char *name, void *buffer,
+								unsigned int bufferSize, unsigned int *errID) {
+	printf("fc.GetServerVariable(%p, \"%s\", %p, %u, %p)\n", context, name, buffer, bufferSize, errID);
+
+	if (strcmp(name, "SERVER_NAME") == 0) {
+		strcpy((char *) buffer, SERVER);
+	} else if (strcmp(name, "SERVER_PORT") == 0) {
+		strcpy((char *) buffer, PORT);
+	} else if (strcmp(name, "SERVER_PROTOCOL") == 0) {
+		strcpy((char *) buffer, "HTTP/" VERSION);
+	} else if (strcmp(name, "REMOTE_ADDR") == 0) {
+		strcpy((char *) buffer, "127.0.0.1");
+	} else if (strcmp(name, "SERVER_SOFTWARE") == 0) {
+		strcpy((char *) buffer, "TestHarness/1.0");
+	} else {
+		return 0;
+	}
+
+	return 1;
+}
+
+static int fcWriteClient(struct _FilterContext * context, char *buffer, unsigned int bufferLen,
+							unsigned int reserved, unsigned int *errID) {
+	/* printf("fc.WriteClient(%p, %p, %u, %u, %p)\n", context, buffer, bufferLen, reserved, errID); */
+
+	while (bufferLen > 0) {
+		fputc(*buffer++, stdout);
+		bufferLen--;
+	}
+	
+	return 1;
+}
+
+static void *fcAllocMem(struct _FilterContext *context, unsigned int size,
+						unsigned int reserved, unsigned int *errID) {
+	printf("fc.AllocMem(%p, %d, %d, %p)\n", context, size, reserved, errID);
+	return malloc(size);
+}
+
+static int fcServerSupport(struct _FilterContext *context, unsigned int funcType, void *data1,
+							void *data2, unsigned int other, unsigned int *errID) {
+	if (funcType == kWriteResponseHeaders) {
+		FilterResponseHeaders *frh = (FilterResponseHeaders *) data1;
+		printf("%d %s\n%s\n", frh->responseCode, frh->reasonText, frh->headerText);
+		return 1;
+	} else {
+		printf("fc.ServerSupport(%p, %u, %p, %p, %u, %p)\n", context, funcType, data1, data2, other, errID);
+		return 0;
+	}
+	
+}
+
+static int fprGetAllHeaders(FilterContext *context, char **headers, unsigned int *errID) {
+	ServerContext *sc = (ServerContext *) context->serverContext;
+	printf("fpr.GetAllHeaders(%p, %p, %p)\n", context, headers, errID);
+
+	*headers = sc->hdrs;
+	return strlen(sc->hdrs) + 1;
+}
+
+static int fprGetHeader(FilterContext *context, char *name, char *buffer,
+						unsigned int bufferSize, unsigned int *errID) {
+	printf("fpr.GetHeader(%p, \"%s\", %p, %u, %p)\n", context, name, buffer, bufferSize, errID);
+	if (stricmp(name, "host") == 0) {
+		strcpy(buffer, SERVER ":" PORT);
+		return strlen(buffer) + 1;
+	}
+	return 0;
+}
+
+static void SendRequest(void) {
+	FilterContext		fc;
+	FilterParsedRequest fpr;
+	ServerContext		sc;
+	int					rc;
+
+	sc.hdrs =	"Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\n"
+				"Accept-Language: en-us\n"
+				"User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT)\n"
+				"Host: " SERVER "\n"
+				"Content-length: 0\n";
+				"Connection: Keep-Alive\n";
+
+	fc.contextSize			= sizeof(fc);
+	fc.revision				= 0;			/* or whatever */
+	fc.serverContext		= &sc;
+	fc.serverReserved		= 0;
+	fc.securePort			= 443;
+	fc.privateContext		= NULL;
+
+	fc.GetRequest			= fcGetRequest;
+	fc.GetRequestContents	= fcGetRequestContents;
+	fc.GetServerVariable	= fcGetServerVariable;
+	fc.WriteClient			= fcWriteClient;
+	fc.AllocMem				= fcAllocMem;
+	fc.ServerSupport		= fcServerSupport;
+
+	fpr.requestMethod		= kRequestGET;
+	fpr.GetAllHeaders		= fprGetAllHeaders;
+	fpr.GetHeader			= fprGetHeader;
+	fpr.reserved			= 0;
+
+	rc = HttpFilterProc(&fc, kFilterParsedRequest, &fpr);
+}
+
+int main(void) {
+	FilterInitData init;
+	int rc;
+
+	TestMain();
+
+	memset(&init, 0, sizeof(init));
+	rc = FilterInit(&init);
+
+	SendRequest();
+
+	rc = TerminateFilter(0);
+
+	return 0;
+}
diff --git a/connectors/jk/native2/server/dsapi/test/test.dsp b/connectors/jk/native2/server/dsapi/test/test.dsp
new file mode 100644
index 0000000..dd4cddc
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/test/test.dsp
@@ -0,0 +1,380 @@
+# Microsoft Developer Studio Project File - Name="test" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=test - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "test.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "test.mak" CFG="test - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "test - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "test - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "test - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I "$(NOTESAPI)\include" /I ".." /I "..\..\..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "..\..\..\..\..\..\apr\include" /I "..\..\..\..\..\..\apr-util\include" /I "..\..\..\..\..\..\pcre\include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "_USRDLL" /D "HAVE_JNI" /D "HAS_APR" /D "HAS_PCRE" /D "NT" /D "TESTING" /U "NOUSER" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib wsock32.lib pcre.lib pcreposix.lib libapr.lib libaprutil.lib /nologo /subsystem:console /machine:I386 /libpath:"..\..\..\..\..\..\pcre\lib" /libpath:"..\..\..\..\..\..\apr\Release" /libpath:"..\..\..\..\..\..\apr-util\Release"
+
+!ELSEIF  "$(CFG)" == "test - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /I "..\..\..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "..\..\..\..\..\..\apr\include" /I "..\..\..\..\..\..\apr-util\include" /I "..\..\..\..\..\..\pcre\include" /I "$(NOTESAPI)\include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "_USRDLL" /D "HAVE_JNI" /D "HAS_APR" /D "HAS_PCRE" /D "NT" /D "TESTING" /D "NO_CAPI" /U "NOUSER" /FR /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib wsock32.lib pcre.lib pcreposix.lib libapr.lib libaprutil.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"..\..\..\..\..\..\pcre\lib" /libpath:"..\..\..\..\..\..\apr\Release" /libpath:"..\..\..\..\..\..\apr-util\Release"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "test - Win32 Release"
+# Name "test - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Group "JK2 Common Source"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_channel.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_channel_apr_socket.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_channel_jni.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_channel_un.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_config.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_config_file.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_endpoint.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_env.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_handler_logon.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_handler_response.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\jni\jk_jni_aprImpl.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_logger_file.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_logger_win32.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_msg_ajp.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_mutex.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_mutex_proc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_mutex_thread.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_nwmain.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_objCache.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_pool_apr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_registry.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_requtil.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_shm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_signal.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_uriEnv.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_uriMap.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_user.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_vm_default.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_worker_ajp13.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_worker_jni.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_worker_lb.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_worker_run.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_worker_status.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_workerEnv.c
+# End Source File
+# End Group
+# Begin Group "DSAPI Source"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\jk_dsapi_plugin.c
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\printf_logger.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\test.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Group "JK2 Common Headers"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_channel.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_endpoint.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_env.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_global.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_handler.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_logger_win32_message.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_msg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_objCache.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_registry.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_requtil.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_service.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_shm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_uriEnv.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_uriMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_vm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\include\jk_workerEnv.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\jni\org_apache_jk_apr_AprImpl.h
+# End Source File
+# End Group
+# Begin Group "DSAPI Headers"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\dsapifilter.h
+# End Source File
+# End Group
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_logger_win32_message.mc
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\common\jk_logger_win32_message.rc
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/connectors/jk/native2/server/dsapi/todo.txt b/connectors/jk/native2/server/dsapi/todo.txt
new file mode 100644
index 0000000..3cbe0bb
--- /dev/null
+++ b/connectors/jk/native2/server/dsapi/todo.txt
@@ -0,0 +1,5 @@
+$Id$
+
+Test SSL
+Produce Linux version
+Resolve any licence issues with dsapifilter.h
diff --git a/connectors/jk/native2/server/isapi/.cvsignore b/connectors/jk/native2/server/isapi/.cvsignore
new file mode 100644
index 0000000..8a9e509
--- /dev/null
+++ b/connectors/jk/native2/server/isapi/.cvsignore
@@ -0,0 +1,5 @@
+isapi.ncb
+isapi.opt
+isapi.plg
+Debug
+Release
diff --git a/connectors/jk/native2/server/isapi/install4iis.js b/connectors/jk/native2/server/isapi/install4iis.js
new file mode 100644
index 0000000..ad7fc04
--- /dev/null
+++ b/connectors/jk/native2/server/isapi/install4iis.js
@@ -0,0 +1,516 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Description: Install script for Tomcat JK2 ISAPI redirector
+ * Author:      Mladen Turk <mturk@apache.org>                           
+ * Version:     $Revision: 1.0 $                                           
+ */
+
+
+/**
+ * Global variables
+ */
+//var IIsWebService;
+//var IIsWebServer;
+//var IIsROOT;
+//var AppParams;
+
+/**
+ * Defaults
+ */
+_DEFAULT_SERVER_NAME = "Default Web Site";
+_DEFAULT_DESCRIPTION = "JK2 ISAPI Redirector";
+_DEFAULT_FILTER_NAME = "jakarta";
+_DEFAULT_HEADERS     = "X-Powered-By: Apache Software Foundation"
+_DEFAULT_OPTIONS     = "rbdi";
+_DEFAULT_REGISTRY    = "HKLM\\SOFTWARE\\Apache Software Foundation\\Jakarta Isapi Redirector\\2.0";
+_DEFAULT_FILTERLIB   = "isapi_redirector2.dll";
+_DEFAULT_WORKERS2    = "\\conf\\workers2.properties";
+
+/**
+ * Set this to false to disable TRACE messages;
+ */
+_DEBUG = true;
+_TRACE_COUNTER = 1;
+
+/**
+ * Constants variables
+ */
+_APP_INPROC  = 0;
+_APP_OUTPROC = 1;
+_APP_POOLED  = 2
+_IIS_OBJECT  = "IIS://LocalHost/W3SVC";
+_IIS_SERVER  = "IIsWebServer";
+_IIS_WEBDIR  = "IIsWebVirtualDir";
+_IIS_FILTERS = "IIsFilters";
+_IIS_FILTER  = "IIsFilter";
+
+function ERROR(args, sMsg)
+{
+    WScript.Echo("Error processing " + args.script + "\n" + sMsg);
+    WScript.Quit(-1);
+}
+
+function RPAD(str, n)
+{
+    var p;
+    p = str;
+    for (i = str.length; i < n; i++)
+        p += " ";
+    return p;
+}
+
+function HEX(num)
+{
+    var digits = "0123456789ABCDEF";
+    var n = num;
+    var h = "";
+    for (i = 0; i < 8; i++) {
+        h = digits.charAt(n & 15) + h;
+        n = n >>> 4;
+    }
+    return h;
+}
+
+function TRACE(sMsg)
+{
+    if (_DEBUG) {
+        var line = _TRACE_COUNTER + "    ";
+        WScript.Echo(line.substring(0, 4) + sMsg);   
+        ++_TRACE_COUNTER;
+    }
+}
+
+function EXCEPTION(exception, func)
+{
+    WScript.Echo(exception + " In function '" + 
+                 func + "'\nError number: " +
+                 HEX(exception.number) + " - " + exception.description + "");
+    WScript.Quit(-1);                     
+}
+
+
+function Parameters()
+{
+    this.ServerName     = _DEFAULT_SERVER_NAME;
+    this.WebDescription = _DEFAULT_DESCRIPTION;
+    this.FilterName     = _DEFAULT_FILTER_NAME;
+    this.FilterDesc     = _DEFAULT_DESCRIPTION;
+    this.WebName        = _DEFAULT_FILTER_NAME;
+    this.Headers        = _DEFAULT_HEADERS;
+    this.WebOptions     = _DEFAULT_OPTIONS;
+    this.RegistryKey    = _DEFAULT_REGISTRY;
+    this.AppProtection  = _APP_POOLED;
+    this.WebPath        = "C:";
+    this.FilterLib      = _DEFAULT_FILTERLIB;
+}
+
+function findWebServiceObject(clsName, objName)
+{
+    var webService;
+    var webObjects;
+    try {
+        webService = GetObject(_IIS_OBJECT);
+        if (!clsName || !objName)
+            return webService;
+        webObjects = new Enumerator(webService);
+        while (!webObjects.atEnd()) {
+            TRACE(RPAD(webObjects.item().Class, 18) + 
+                  RPAD(webObjects.item().Name, 15) + webObjects.item().AdsPath);
+            if (webObjects.item().Class == clsName &&
+                webObjects.item().Name  == objName)
+                return webObjects.item();
+            
+            webObjects.moveNext();            
+        }        
+    }
+    catch(exception) {
+        EXCEPTION(exception, "findWebServiceObject");
+    } 
+    
+    return null;
+}
+
+function findWebServer(serverComment)
+{
+    var webService;
+    var webObjects;
+    try {
+        webService = GetObject(_IIS_OBJECT);
+
+        webObjects = new Enumerator(webService);
+        while (!webObjects.atEnd()) {
+            if (webObjects.item().Class == _IIS_SERVER &&
+                webObjects.item().ServerComment  == serverComment)
+                return webObjects.item();
+            
+            webObjects.moveNext();            
+        }        
+    }
+    catch(exception) {
+        EXCEPTION(exception, "findWebServer");
+    } 
+    
+    return null;
+}
+
+function findDefaultWebServer()
+{
+    return findWebServer(_DEFAULT_SERVER_NAME);
+}
+
+
+function findADSIObject(adsiObject, clsName, objName)
+{
+    var adsiObjects;
+    try {
+        adsiObjects = new Enumerator(adsiObject);
+        while (!adsiObjects.atEnd()) {
+            TRACE(RPAD(adsiObjects.item().Class, 18) + 
+                  RPAD(adsiObjects.item().Name, 15) + adsiObjects.item().AdsPath);
+            if (adsiObjects.item().Class == clsName &&
+                adsiObjects.item().Name  == objName)
+                return adsiObjects.item();
+            
+            adsiObjects.moveNext();            
+        }        
+    }
+    catch(exception) {        
+        EXCEPTION(exception, "findADSIObject");
+    } 
+    
+    return null;
+        
+}
+
+function hasOption(optString, optName)
+{
+    if (optString.indexOf(optName) == -1)
+        return false;
+    else {
+        /* Check if the option is dissabled using '-' prefix */
+        if (optString.indexOf("-" + optName) == -1)
+            return true;
+        else
+            return false;
+    }
+}
+
+function createVirtualDir(webRootDir, appParams)
+{
+    var newDir;
+    try {
+        newDir = findADSIObject(webRootDir, _IIS_WEBDIR, appParams.WebName);
+        if (newDir == null) {
+            
+            TRACE("Creating new directory...");
+            
+            newDir = webRootDir.Create(_IIS_WEBDIR, appParams.WebName);
+            newDir.AppFriendlyName = appParams.WebDescription;
+            newDir.Path = appParams.WebPath;
+            newDir.AppCreate2(appParams.AppProtection);
+            newDir.HttpCustomHeaders = appParams.Headers;
+        }
+        else {
+            TRACE("Updating existing directory...");            
+        }
+        TRACE("Setting directory options...");
+        newDir.AccessExecute  = hasOption(appParams.WebOptions, "x");
+        newDir.AccessRead     = hasOption(appParams.WebOptions, "r");
+        newDir.AccessWrite    = hasOption(appParams.WebOptions, "w");
+        newDir.AccessScript   = hasOption(appParams.WebOptions, "s");
+        newDir.ContentIndexed = hasOption(appParams.WebOptions, "i");
+        newDir.EnableDirBrowsing = hasOption(appParams.WebOptions, "b");
+        newDir.EnableDefaultDoc  = hasOption(appParams.WebOptions, "d");
+        newDir.SetInfo();
+        
+        TRACE("Virtual directory [/" + appParams.WebName + "] set.");
+        return newDir;
+    }
+    catch(exception) {        
+        EXCEPTION(exception, "createVirtualDir");
+    } 
+    
+    return null;        
+}
+
+function createISAPIFilter(webServer, appParams)
+{
+    var filters;
+    var newFilter;
+    try {
+        filters = findADSIObject(webServer, _IIS_FILTERS, "Filters");
+        if (filters == null) {
+            //may have to create the website-level filters container
+            filters = webServer.create(_IIS_FILTERS, "Filters");
+        }
+        newFilter = findADSIObject(filters, _IIS_FILTER, appParams.FilterName);
+        if (newFilter == null) {
+            
+            TRACE("Creating new ISAPI filter...");
+            
+            newFilter = filters.Create(_IIS_FILTER, appParams.FilterName);
+        }
+        else {
+            TRACE("Updating existing filter...");            
+        }
+        TRACE("Setting filter options...");
+
+        TRACE("Filters order " + filters.FilterLoadOrder);
+        newFilter.FilterPath  = appParams.WebPath + "\\" + appParams.FilterLib;
+        newFilter.FilterDescription  = appParams.FilterDesc;
+        newFilter.SetInfo();
+        if (filters.FilterLoadOrder.indexOf(appParams.FilterName) == -1) {
+            filters.FilterLoadOrder += ", " +  appParams.FilterName;   
+            filters.SetInfo();
+        }
+        TRACE("Filter [" + appParams.FilterName + "] set.");
+        return newFilter;
+    }
+    catch(exception) {        
+        EXCEPTION(exception, "createISAPIFilter");
+    } 
+    
+    return null;    
+}
+
+function createVirtualExecDir(webRootDir, appParams)
+{
+    var op, rv;
+    op = appParams.WebOptions;
+    appParams.WebOptions = op + "+x-r-i-b-d";
+    rv = createVirtualDir(webRootDir, appParams);
+    appParams.WebOptions = op;        
+    return rv;
+}
+
+function deleteADSIObject(adsiObject, clsName, objName)
+{
+    var rv = false;
+    try {
+        adsiObject.Delete(clsName, objName);
+        rv = true;
+    }
+    catch(exception) {  
+        /* 
+         * Exception is thrown if the object doesn't exist
+         * Just ignore...
+         */
+    } 
+    
+    return rv;
+}
+
+function Arguments()
+{
+    this.argv = WScript.Arguments;    
+    this.argc = WScript.Arguments.length;
+    this.optarg = null;
+    this.optind = 0;
+    this.optopt = null;
+    this.opterr = null;
+
+    this.program = WScript.FullName.toLowerCase();
+    this.program = this.program.substr(this.program.lastIndexOf("\\") + 1);
+    if (this.program.indexOf("wscript.exe") == -1)
+        _DEBUG = false;
+    this.script = WScript.ScriptName;
+}
+
+function getopt(args, ostr)
+{
+    if (args.optind >= args.argc) {
+        return null;    
+    }
+    try {
+        var opt = args.argv(args.optind);
+        if (opt.charAt(0) == "-") {
+            var oi = ostr.indexOf(opt.charAt(1));
+            if (oi == -1) {
+                args.opterr = "Invalid option switch " + opt;                
+                args.optopt = null;
+                args.optarg = null;
+                return null;                
+            }
+            ++args.optind;
+            if (ostr.charAt(oi + 1) == ":") {
+                if (args.optind < args.argc) {
+                    args.optarg = args.argv(args.optind);
+                    ++args.optind;
+                }
+                else {
+                    args.opterr = "Missing required argument value for " + opt;
+                    args.optopt = null;
+                    args.optarg = null;
+                    return null;                                    
+                }
+            }
+            args.optopt = ostr.charAt(oi);
+            return args.optopt;
+        }
+    }
+    catch(exception) {        
+        EXCEPTION(exception, "getopt");
+    }
+    return null; 
+}
+
+function checkFileExists(fname)
+{
+    var fso;
+    fso = new ActiveXObject("Scripting.FileSystemObject");
+   
+    if (fso.FileExists(fname))
+        return true;
+    else
+        return false;    
+}
+
+function checkFilterExists(params)
+{
+    return checkFileExists(params.WebPath + "\\" +  params.FilterLib);
+}
+
+
+function Usage(args)
+{
+    var prn;
+    prn = "Usage: " + args.program + " " + args.script + " [option]... " + "[path] [tomcat_home]\n\n" +
+          "  -s   WEBSERVER     Use the WEBSERVER     [" + _DEFAULT_SERVER_NAME + "]\n" +
+          "  -f   FILTERNAME    Use the FILTERNAME    [" + _DEFAULT_FILTER_NAME + "]\n" +
+          "  -d   DESCRIPTION   Filter description    [" + _DEFAULT_DESCRIPTION + "]\n" +
+          "  -v   VIRTUALDIR    Create the VIRTUALDIR [/" + _DEFAULT_FILTER_NAME + "]\n" +
+          "  -l   ISAPILIB      Use the ISAPILIB      [" + _DEFAULT_FILTERLIB + "]\n" +          
+          "  -h                 display this help and exit\n" +
+          "\n  [path]             Virtual directory path" +
+          "\n                     Set this to directory containing " + _DEFAULT_FILTERLIB +
+          "\n  [tomcat_home]      Path to the tomcat home directory";
+
+    WScript.Echo(prn);             
+}
+
+function Main(args)
+{
+    var params;
+    var opt;
+    var IIsWebService;
+    var IIsWebServer;
+    var IIsROOT;
+    var IIsFilters;
+    var IIsFilter;
+    var tchome   = null;
+    var workers2 = null;
+    
+    params = new Parameters();
+    
+    while ((opt = getopt(args, "s:f:d:v:l:h"))) {
+        switch (opt) {
+            case "s":
+                params.ServerName = args.optarg;   
+                break;                                     
+            case "f":
+                params.FilterName = args.optarg;                        
+                break;                                     
+            case "d":
+                params.FilterDesc = args.optarg;                        
+                break;                                     
+            case "l":
+                params.FilterLib  = args.optarg;                        
+                break;                                     
+            case "v":
+                params.WebName    = args.optarg;                        
+                break;                                     
+            case "h":
+            default:
+                Usage(args);
+                return 0;                        
+                break;                                                 
+        }        
+    }
+    TRACE("argc " + args.argc + " optind " + args.optind);
+    if (args.optind >= args.argc) {
+        /* Case when isapi_redirector2.dll is inside TOMCAT_HOME\bin */
+        params.WebPath = WScript.ScriptFullName.substr(0,
+                                 WScript.ScriptFullName.lastIndexOf("\\"));
+        tchome = params.WebPath.substr(0, params.WebPath.lastIndexOf("\\"));                                         
+    }
+    else {
+        params.WebPath = args.argv(args.optind);
+        ++args.optind;
+    }
+    if (!checkFilterExists(params)) {
+        ERROR(args, "The specified filter library could not be found...\n" +
+              "File " + params.WebPath + "\\" + params.FilterLib + " does not exist.");
+        
+    }
+    if (args.optind < args.argc)
+        tchome = args.argv(args.optind);
+    else if (!tchome)
+        tchome = params.WebPath;                
+    workers2 = tchome + _DEFAULT_WORKERS2 ;
+    if (!checkFileExists(workers2)) {
+        ERROR(args, "The specified configuration file could not be found...\n" +
+              "File " + workers2 + " does not exist.");
+        
+    }
+
+    if ((IIsWebService =  findWebServiceObject(null, null)) == null) {
+        ERROR(args, "Unable to find Web Service ADSI object\n" +
+              "Check the security settings...");            
+    }
+    
+    if ((IIsWebServer =  findWebServer(params.ServerName)) == null) {
+        ERROR(args, "Unable to find Web Server ADSI object...\n" +
+              "The '" + params.ServerName + "' does not exists.");            
+    
+    }
+
+    if ((IIsROOT = findADSIObject(IIsWebServer, _IIS_WEBDIR, "ROOT")) == null) {
+        ERROR(args, "Unable to find Web Server ROOT direcrory.");
+    }
+
+    if (!createVirtualExecDir(IIsROOT, params)) {
+        ERROR(args, "Unable to create virual directory /" + params.WebName);        
+    }
+
+    if (!createISAPIFilter(IIsWebServer, params)) {
+        /* TODO: roll-back virtual dir */
+        ERROR(args, "Unable to create the '" + params.FilterName + "' filter.");        
+    }
+        
+    /** Finaly set the registry entries 
+     */
+    var WshShell = WScript.CreateObject("WScript.Shell");
+     
+    WshShell.RegWrite(_DEFAULT_REGISTRY + "\\extensionUri",
+                      "/" + params.FilterName + "/" + params.FilterLib,
+                      "REG_SZ");
+    WshShell.RegWrite(_DEFAULT_REGISTRY + "\\serverRoot",
+                      tchome, "REG_SZ");
+    WshShell.RegWrite(_DEFAULT_REGISTRY + "\\workersFile",
+                      workers2, "REG_SZ");
+    WshShell.RegWrite(_DEFAULT_REGISTRY + "\\authComplete", "0", "REG_SZ");
+    WshShell.RegWrite(_DEFAULT_REGISTRY + "\\threadPool", "20", "REG_SZ");
+    
+            
+    return 0;
+}
+
+/* The main program */
+var args = new Arguments();
+rv = Main(args);
+WScript.Quit(rv);
diff --git a/connectors/jk/native2/server/isapi/isapi.def b/connectors/jk/native2/server/isapi/isapi.def
new file mode 100644
index 0000000..1d7103f
--- /dev/null
+++ b/connectors/jk/native2/server/isapi/isapi.def
@@ -0,0 +1,9 @@
+LIBRARY	     "isapi_redirector2"
+
+EXPORTS
+	HttpFilterProc
+	GetFilterVersion
+	GetExtensionVersion
+	HttpExtensionProc	
+	TerminateFilter
+	TerminateExtension
diff --git a/connectors/jk/native2/server/isapi/isapi.dsp b/connectors/jk/native2/server/isapi/isapi.dsp
new file mode 100644
index 0000000..279c806
--- /dev/null
+++ b/connectors/jk/native2/server/isapi/isapi.dsp
@@ -0,0 +1,402 @@
+# Microsoft Developer Studio Project File - Name="isapi" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=isapi - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "isapi.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "isapi.mak" CFG="isapi - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "isapi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "isapi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "isapi - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "DebugS"
+# PROP BASE Intermediate_Dir "DebugS"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "$(APACHE2_HOME)\include" /I "$(APACHE2_HOME)\os\win32" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ISAPI_EXPORTS" /D "HAVE_JNI" /D "HAS_APR" /FR /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "..\..\srclib\apr\include" /I "..\..\srclib\apr-util\include" /I "..\..\srclib\apr-iconv\include" /I "..\..\srclib\pcre" /D "_DEBUG" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /D "API_DECLARE_STATIC" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ISAPI_EXPORTS" /D "HAVE_JNI" /D "HAS_APR" /D "HAS_PCRE" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 libapr.lib libaprutil.lib wsock32.lib advapi32.lib /nologo /dll /debug /machine:I386 /out:"Debug/isapi_redirector2.dll" /pdbtype:sept /libpath:"$(APACHE2_HOME)\lib"
+# SUBTRACT BASE LINK32 /nodefaultlib
+# ADD LINK32 ws2_32.lib wsock32.lib advapi32.lib /nologo /dll /debug /machine:I386 /out:"Debug/isapi_redirector2.dll" /pdbtype:sept
+# SUBTRACT LINK32 /nodefaultlib
+
+!ELSEIF  "$(CFG)" == "isapi - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "$(APACHE2_HOME)\include" /I "$(APACHE2_HOME)\os\win32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ISAPI_EXPORTS" /D "HAVE_JNI" /D "HAS_APR" /FR /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /I "..\..\srclib\apr\include" /I "..\..\srclib\apr-util\include" /I "..\..\srclib\apr-iconv\include" /I "..\..\srclib\pcre" /D "NDEBUG" /D "_WIN32" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /D "API_DECLARE_STATIC" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ISAPI_EXPORTS" /D "HAVE_JNI" /D "HAS_APR" /D "HAS_PCRE" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 libapr.lib libaprutil.lib wsock32.lib advapi32.lib /nologo /dll /machine:I386 /out:"Release/isapi_redirector2.dll" /libpath:"$(APACHE2_HOME)\lib"
+# ADD LINK32 ws2_32.lib wsock32.lib advapi32.lib /nologo /dll /machine:I386 /out:"Release/isapi_redirector2.dll"
+
+!ENDIF 
+
+# Begin Target
+
+# Name "isapi - Win32 Debug"
+# Name "isapi - Win32 Release"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\isapi.def
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel_apr_socket.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel_jni.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_channel_un.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_config.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_config_file.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_endpoint.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_env.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_handler_logon.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_handler_response.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_iis_thread_pool.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_isapi_plugin.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jni\jk_jni_aprImpl.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_file.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_win32.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_map.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_msg_ajp.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_mutex.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_mutex_proc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_mutex_thread.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_nwmain.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_objCache.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_pool_apr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_registry.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_requtil.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_service_iis.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_shm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_signal.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_uriEnv.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_uriMap.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_user.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_vm_default.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_ajp13.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_jni.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_lb.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_run.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_worker_status.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_workerEnv.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\..\include\jk_channel.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_endpoint.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_env.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_global.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_handler.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\jk_iis.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_win32_message.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_map.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_msg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_mt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_objCache.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_pool.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_registry.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_requtil.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_service.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_shm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_uriEnv.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_uriMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_vm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_worker.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\include\jk_workerEnv.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\jni\org_apache_jk_apr_AprImpl.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_win32_message.mc
+
+!IF  "$(CFG)" == "isapi - Win32 Debug"
+
+# PROP BASE Ignore_Default_Tool 1
+# PROP Ignore_Default_Tool 1
+# Begin Custom Build - Creating resources from $(InputPath)
+InputDir=\WRKPLACE\PROJECTS\jtc\jk\native2\common
+InputPath=..\..\common\jk_logger_win32_message.mc
+
+"..\..\common\jk_logger_win32_message.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	mc -h $(InputDir) -r $(InputDir) $(InputPath)
+
+# End Custom Build
+
+!ELSEIF  "$(CFG)" == "isapi - Win32 Release"
+
+# Begin Custom Build - Creating resources from $(InputPath)
+InputDir=\WRKPLACE\PROJECTS\jtc\jk\native2\common
+InputPath=..\..\common\jk_logger_win32_message.mc
+
+"..\..\common\jk_logger_win32_message.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+	mc -h $(InputDir) -r $(InputDir) $(InputPath)
+
+# End Custom Build
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\common\jk_logger_win32_message.rc
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/connectors/jk/native2/server/isapi/isapi.dsw b/connectors/jk/native2/server/isapi/isapi.dsw
new file mode 100644
index 0000000..789d556
--- /dev/null
+++ b/connectors/jk/native2/server/isapi/isapi.dsw
@@ -0,0 +1,155 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "apr"=..\..\srclib\apr\apr.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "apriconv"="..\..\srclib\apr-iconv\apriconv.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name pcre
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "aprutil"="..\..\srclib\apr-util\aprutil.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name gen_uri_delims
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name xml
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "dftables"=..\..\srclib\pcre\dftables.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "gen_uri_delims"="..\..\srclib\apr-util\uri\gen_uri_delims.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "isapi"=.\isapi.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name apr
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name aprutil
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pcre
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pcreposix
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name apriconv
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "pcre"=..\..\srclib\pcre\pcre.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name dftables
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pcreposix
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "pcreposix"=..\..\srclib\pcre\pcreposix.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "xml"="..\..\srclib\apr-util\xml\expat\lib\xml.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/connectors/jk/native2/server/isapi/isapi_redirector2.reg b/connectors/jk/native2/server/isapi/isapi_redirector2.reg
new file mode 100644
index 0000000..802acdc
--- /dev/null
+++ b/connectors/jk/native2/server/isapi/isapi_redirector2.reg
@@ -0,0 +1,8 @@
+Windows Registry Editor Version 5.00
+
+[HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\2.0]
+"serverRoot"="E:\\tomcat\\jakarta-tomcat\\build\\tomcat"
+"extensionUri"="/jakarta/isapi_redirector2.dll"
+"workersFile"="E:\\tomcat\\jakarta-tomcat\\build\\tomcat\\conf\\workers2.properties"
+"authComplete"="0"
+"threadPool"="20"
diff --git a/connectors/jk/native2/server/isapi/jk_iis.h b/connectors/jk/native2/server/isapi/jk_iis.h
new file mode 100644
index 0000000..eb4bdbb
--- /dev/null
+++ b/connectors/jk/native2/server/isapi/jk_iis.h
@@ -0,0 +1,145 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef IIS_H
+#define IIS_H
+
+#define _WIN32_WINNT 0x0400
+
+#include <httpext.h>
+#include <httpfilt.h>
+#include <wininet.h>
+
+#include "jk_global.h"
+//#include "jk_util.h"
+#include "jk_pool.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif                          /* __cplusplus */
+
+
+#define VERSION_STRING "Jakarta/ISAPI/2.0Dev"
+
+#define DEFAULT_WORKER_NAME ("ajp13")
+/*
+ * We use special headers to pass values from the filter to the
+ * extension. These values are:
+ *
+ * 1. The real URI before redirection took place
+ * 2. The name of the worker to be used.
+ * 3. The contents of the Translate header, if any
+ *
+ */
+#define URI_HEADER_NAME          ("TOMCATURI:")
+#define QUERY_HEADER_NAME        ("TOMCATQUERY:")
+#define WORKER_HEADER_NAME       ("TOMCATWORKER:")
+#define TOMCAT_TRANSLATE_HEADER_NAME ("TOMCATTRANSLATE:")
+#define CONTENT_LENGTH           ("CONTENT_LENGTH:")
+
+#define HTTP_URI_HEADER_NAME     ("HTTP_TOMCATURI")
+#define HTTP_QUERY_HEADER_NAME   ("HTTP_TOMCATQUERY")
+#define HTTP_WORKER_HEADER_NAME  ("HTTP_TOMCATWORKER")
+
+#define SERVER_NAME              ("SERVER_NAME" )
+
+#define SERVER_SOFTWARE          ("SERVER_SOFTWARE")
+
+#define REGISTRY_LOCATION        ("Software\\Apache Software Foundation\\Jakarta Isapi Redirector\\2.0")
+#define W3SVC_REGISTRY_KEY       ("SYSTEM\\CurrentControlSet\\Services\\W3SVC\\Parameters")
+
+#define BAD_REQUEST             -1
+#define BAD_PATH                -2
+#define MAX_SERVERNAME          128
+
+#define HTML_ERROR_400          "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" \
+                                "<HTML><HEAD><TITLE>Bad request!</TITLE></HEAD>" \
+                                "<BODY><H1>Bad request!</H1><DL><DD>\n" \
+                                "Your browser (or proxy) sent a request that " \
+                                "this server could not understand.</DL></DD></BODY></HTML>"
+
+#define HTML_ERROR_403          "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" \
+                                "<HTML><HEAD><TITLE>Access forbidden!!</TITLE></HEAD>" \
+                                "<BODY><H1>Access forbidden!</H1><DL><DD>\n" \
+                                "You don't have permission to access the requested object." \
+                                "It is either read-protected or not readable by the server.</DL></DD></BODY></HTML>"
+
+
+#define GET_SERVER_VARIABLE_VALUE(pool, name, place) {    \
+    (place) = NULL;                                 \
+    huge_buf_sz = sizeof(huge_buf);                 \
+    if (get_server_value(lpEcb,                      \
+                        (name),                     \
+                        huge_buf,                   \
+                        huge_buf_sz,                \
+                        "")) {                      \
+        (place) = (pool)->pstrdup(env,(pool),huge_buf);   \
+    }   \
+}
+
+#define GET_SERVER_VARIABLE_VALUE_INT(name, place, def) {   \
+    huge_buf_sz = sizeof(huge_buf);                 \
+    if (get_server_value(lpEcb,                     \
+                        (name),                     \
+                        huge_buf,                   \
+                        huge_buf_sz,                \
+                        "")) {                      \
+        (place) = atoi(huge_buf);                   \
+        if (0 == (place)) {                         \
+            (place) = def;                          \
+        }                                           \
+    } else {    \
+        (place) = def;  \
+    }           \
+}
+
+
+    static int JK_METHOD jk2_service_iis_head(jk_env_t *env,
+                                              jk_ws_service_t *s);
+
+    static int JK_METHOD jk2_service_iis_read(jk_env_t *env,
+                                              jk_ws_service_t *s, void *b,
+                                              unsigned len,
+                                              unsigned *actually_read);
+
+    static int JK_METHOD jk2_service_iis_write(jk_env_t *env,
+                                               jk_ws_service_t *s,
+                                               const void *b, unsigned l);
+
+    static int JK_METHOD jk2_service_iis_init_ws_service(struct jk_env *env,
+                                                         jk_ws_service_t
+                                                         *_this,
+                                                         struct jk_worker *w,
+                                                         void *serverObj);
+
+    int jk2_service_iis_init(jk_env_t *env, jk_ws_service_t *s);
+
+    int get_server_value(LPEXTENSION_CONTROL_BLOCK lpEcb,
+                         char *name, char *buf, DWORD bufsz, char *def_val);
+
+    DWORD WINAPI HttpExtensionProcWorker(LPEXTENSION_CONTROL_BLOCK lpEcb,
+                                         jk_ws_service_t *service);
+
+    int jk2_iis_init_pool(jk_env_t *env);
+    int jk2_iis_thread_pool(LPEXTENSION_CONTROL_BLOCK lpEcb);
+    int jk2_iis_close_pool(jk_env_t *env);
+
+#ifdef __cplusplus
+}
+#endif                          /* __cplusplus */
+
+#endif
diff --git a/connectors/jk/native2/server/isapi/jk_iis_thread_pool.c b/connectors/jk/native2/server/isapi/jk_iis_thread_pool.c
new file mode 100644
index 0000000..155f2ae
--- /dev/null
+++ b/connectors/jk/native2/server/isapi/jk_iis_thread_pool.c
@@ -0,0 +1,253 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: IIS Jk2 Thread Pool                                        *
+ * Author:      Mladen Turk <mturk@apache.org>                             *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+#define _WIN32_WINNT 0x0400
+
+#include <httpext.h>
+#include <httpfilt.h>
+#include <wininet.h>
+
+#include "jk_global.h"
+#include "jk_requtil.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_logger.h"
+#include "jk_env.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+#include "apr_general.h"
+#include "jk_iis.h"
+
+extern int use_thread_pool;
+extern apr_pool_t *jk_globalPool;
+
+typedef struct iis_thread_pool_t iis_thread_pool_t;
+typedef struct iis_thread_t iis_thread_t;
+
+struct iis_thread_pool_t
+{
+    HANDLE manager_port;
+    HANDLE manager_thread;
+    DWORD manager_id;
+    HANDLE worker_port;
+    int worker_threads;
+    int thread_count;
+    iis_thread_t **threads;
+    HANDLE *handles;
+    CRITICAL_SECTION cs;
+    apr_pool_t *pool;
+};
+
+struct iis_thread_t
+{
+    DWORD thread_id;
+    jk_ws_service_t service;
+    int busy;
+};
+
+static iis_thread_pool_t global_thread_pool = { 0 };
+
+#define THREAD_POOL_SHUTDOWN ((OVERLAPPED *)0xFFFFFFFF)
+#define THREAD_POOL_RECYCLE  ((OVERLAPPED *)0xFFFFFFFE)
+/* Manager service timeout 10 seconds */
+#define MANAGER_TIMEOUT      10000
+/* Timeout for threads shutdown 2 minutes */
+#define SHUTDOWN_TIMEOUT     120000
+
+VOID WINAPI thread_pool_manager(void *p)
+{
+    ULONG n1, n2;
+    OVERLAPPED *pOverLapped;
+    BOOL work = TRUE;
+
+    while (work) {
+        work = FALSE;
+        while (GetQueuedCompletionStatus(global_thread_pool.manager_port,
+                                         &n1, &n2, &pOverLapped,
+                                         MANAGER_TIMEOUT)) {
+            if (pOverLapped == THREAD_POOL_SHUTDOWN) {
+
+                break;
+            }
+            else {
+                /* XXX If something else received should be error */
+            }
+        }
+        if (GetLastError() == WAIT_TIMEOUT) {
+            /* XXX Dynamic management */
+
+            work = TRUE;
+        }
+    }
+    /* Clean up and die. */
+    ExitThread(0);
+}
+
+VOID WINAPI thread_worker(void *p)
+{
+    ULONG n1, n2;
+    OVERLAPPED *pOverLapped;
+    iis_thread_t *thread = (iis_thread_t *) p;
+
+    while (GetQueuedCompletionStatus(global_thread_pool.worker_port,
+                                     &n1, &n2, &pOverLapped, INFINITE)) {
+        if (pOverLapped == THREAD_POOL_SHUTDOWN) {
+            jk_ws_service_t *s = &thread->service;
+            if (s->workerEnv && s->realWorker) {
+                struct jk_worker *w = s->realWorker;
+                jk_env_t *env =
+                    s->workerEnv->globalEnv->getEnv(s->workerEnv->globalEnv);
+                if (w != NULL && w->channel != NULL
+                    && w->channel->afterRequest != NULL) {
+                    w->channel->afterRequest(env, w->channel, w, NULL, s);
+                }
+            }
+            break;
+        }
+        else if (pOverLapped == THREAD_POOL_RECYCLE) {
+            /* XXX Kill ourself, will be used for dynamic thread pool */
+
+            break;
+        }
+        else {
+            /* do the job */
+            LPEXTENSION_CONTROL_BLOCK lpEcb = (LPEXTENSION_CONTROL_BLOCK) n1;
+
+            InterlockedIncrement(&thread->busy);
+            HttpExtensionProcWorker(lpEcb, &thread->service);
+            InterlockedDecrement(&thread->busy);
+        }
+    }
+    /* Clean up and die. */
+    ExitThread(0);
+}
+
+int jk2_iis_init_pool(jk_env_t *env)
+{
+    int i, workers;
+    /* Do nothing if the thread pool is not used */
+    if (use_thread_pool <= 0)
+        return JK_OK;
+    else
+        workers = use_thread_pool;
+
+    global_thread_pool.worker_threads = workers;
+    apr_pool_create(&global_thread_pool.pool, jk_globalPool);
+    if (!global_thread_pool.pool) {
+
+        return JK_ERR;
+    }
+    global_thread_pool.threads =
+        (iis_thread_t **) apr_palloc(global_thread_pool.pool,
+                                     workers * sizeof(iis_thread_t *));
+    for (i = 0; i < workers; i++) {
+        global_thread_pool.threads[i] =
+            (iis_thread_t *) apr_pcalloc(global_thread_pool.pool,
+                                         workers * sizeof(iis_thread_t));
+
+    }
+
+    InitializeCriticalSection(&global_thread_pool.cs);
+    global_thread_pool.manager_port = CreateIoCompletionPort((HANDLE)
+                                                             INVALID_HANDLE_VALUE,
+                                                             NULL, 0, 0);
+    /* Create the ThreadPool manager thread */
+    global_thread_pool.manager_thread = CreateThread(NULL,
+                                                     0,
+                                                     (LPTHREAD_START_ROUTINE)
+                                                     thread_pool_manager,
+                                                     NULL, 0,
+                                                     &global_thread_pool.
+                                                     manager_id);
+
+    /* Create the ThreadPool worker port */
+    global_thread_pool.worker_port = CreateIoCompletionPort((HANDLE)
+                                                            INVALID_HANDLE_VALUE,
+                                                            NULL, 0, 0);
+
+    global_thread_pool.handles =
+        (HANDLE *) apr_palloc(global_thread_pool.pool,
+                              workers * sizeof(HANDLE));
+    /* Start the worker threads */
+    for (i = 0; i < workers; i++) {
+        DWORD id;
+        global_thread_pool.handles[i] = CreateThread(NULL,
+                                                     0,
+                                                     (LPTHREAD_START_ROUTINE)
+                                                     thread_worker,
+                                                     global_thread_pool.
+                                                     threads[i], 0, &id);
+        global_thread_pool.threads[i]->thread_id = id;
+    }
+
+    return JK_OK;
+}
+
+int jk2_iis_close_pool(jk_env_t *env)
+{
+    int i, workers, rc;
+    /* Do nothing if the thread pool is not used */
+    if (use_thread_pool <= 0)
+        return JK_OK;
+    else
+        workers = use_thread_pool;
+    EnterCriticalSection(&global_thread_pool.cs);
+    PostQueuedCompletionStatus(global_thread_pool.manager_port,
+                               (DWORD) 0, (DWORD) 0, THREAD_POOL_SHUTDOWN);
+    WaitForSingleObject(global_thread_pool.manager_port, INFINITE);
+    CloseHandle(global_thread_pool.manager_port);
+    CloseHandle(global_thread_pool.manager_thread);
+
+    /* Send shutdown event to each worker thread */
+    for (i = 0; i < workers; i++) {
+        PostQueuedCompletionStatus(global_thread_pool.worker_port,
+                                   (DWORD) 0,
+                                   (DWORD) 0, THREAD_POOL_SHUTDOWN);
+    }
+    /* Wait for threads to die */
+    rc = WaitForMultipleObjects(workers,
+                                global_thread_pool.handles,
+                                TRUE, SHUTDOWN_TIMEOUT);
+
+    CloseHandle(global_thread_pool.worker_port);
+
+    if (rc == WAIT_TIMEOUT) {
+        DWORD exitCode;
+        /* Terminate the threads that didn't respont to the shutdown event */
+        for (i = 0; i < workers; i++)
+            if (GetExitCodeThread(global_thread_pool.handles[i],
+                                  &exitCode) == STILL_ACTIVE)
+                TerminateThread(global_thread_pool.handles[i], -1);
+    }
+
+    LeaveCriticalSection(&global_thread_pool.cs);
+    DeleteCriticalSection(&global_thread_pool.cs);
+    return JK_OK;
+}
+
+int jk2_iis_thread_pool(LPEXTENSION_CONTROL_BLOCK lpEcb)
+{
+
+    return PostQueuedCompletionStatus(global_thread_pool.worker_port,
+                                      (DWORD) lpEcb, (DWORD) 0, NULL);
+
+}
diff --git a/connectors/jk/native2/server/isapi/jk_isapi_plugin.c b/connectors/jk/native2/server/isapi/jk_isapi_plugin.c
new file mode 100644
index 0000000..675c26c
--- /dev/null
+++ b/connectors/jk/native2/server/isapi/jk_isapi_plugin.c
@@ -0,0 +1,881 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: ISAPI plugin for IIS/PWS                                   *
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           *
+ * Author:      Larry Isaacs <larryi@apache.org>                           *
+ * Author:      Ignacio J. Ortega <nacho@apache.org>                       *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+// This define is needed to include wincrypt,h, needed to get client certificates
+#define _WIN32_WINNT 0x0400
+
+#include <httpext.h>
+#include <httpfilt.h>
+#include <wininet.h>
+
+#include "jk_global.h"
+#include "jk_requtil.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_logger.h"
+#include "jk_env.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+#include "apr_general.h"
+#include "jk_iis.h"
+
+#define SERVER_ROOT_TAG         ("serverRoot")
+#define EXTENSION_URI_TAG       ("extensionUri")
+#define WORKERS_FILE_TAG        ("workersFile")
+#define USE_AUTH_COMP_TAG       ("authComplete")
+#define THREAD_POOL_TAG         ("threadPool")
+#define SEND_GROUPS_TAG         ("sendGroups")
+
+
+static char file_name[_MAX_PATH];
+static char ini_file_name[MAX_PATH];
+static int using_ini_file = JK_FALSE;
+static int is_inited = JK_FALSE;
+static int is_mapread = JK_FALSE;
+static int was_inited = JK_FALSE;
+static DWORD auth_notification_flags = 0;
+static int use_auth_notification_flags = 0;
+int send_groups = 0;
+
+static jk_workerEnv_t *workerEnv;
+apr_pool_t *jk_globalPool;
+
+static char extension_uri[INTERNET_MAX_URL_LENGTH] =
+    "/jakarta/isapi_redirector2.dll";
+static char worker_file[MAX_PATH * 2] = "";
+static char server_root[MAX_PATH * 2] = "";
+
+
+static int init_jk(char *serverName);
+
+static int initialize_extension();
+
+static int read_registry_init_data(jk_env_t *env);
+extern int jk_jni_status_code;
+
+static int get_registry_config_parameter(HKEY hkey,
+                                         const char *tag, char *b, DWORD sz);
+
+
+static jk_env_t *jk2_create_config();
+static int get_auth_flags();
+
+/*  ThreadPool support
+ *
+ */
+int use_thread_pool = 0;
+
+static void write_error_response(PHTTP_FILTER_CONTEXT pfc, char *status,
+                                 char *msg)
+{
+    char crlf[3] = { (char)13, (char)10, '\0' };
+    char *ctype = "Content-Type:text/html\r\n\r\n";
+    DWORD len = strlen(msg);
+
+    /* reject !!! */
+    pfc->ServerSupportFunction(pfc,
+                               SF_REQ_SEND_RESPONSE_HEADER,
+                               status, (DWORD) crlf, (DWORD) ctype);
+    pfc->WriteClient(pfc, msg, &len, 0);
+}
+
+HANDLE jk2_starter_event;
+HANDLE jk2_inited_event;
+HANDLE jk2_starter_thread = NULL;
+
+VOID jk2_isapi_starter(LPVOID lpParam)
+{
+    Sleep(1000);
+
+    apr_initialize();
+    apr_pool_create(&jk_globalPool, NULL);
+    initialize_extension();
+    if (is_inited) {
+        if (init_jk(NULL))
+            is_mapread = JK_TRUE;
+    }
+    SetEvent(jk2_inited_event);
+    WaitForSingleObject(jk2_starter_event, INFINITE);
+
+    if (is_inited) {
+        was_inited = JK_TRUE;
+        is_inited = JK_FALSE;
+        if (workerEnv) {
+            jk_env_t *env = workerEnv->globalEnv;
+            jk2_iis_close_pool(env);
+            workerEnv->close(env, workerEnv);
+        }
+        is_mapread = JK_FALSE;
+    }
+    apr_pool_destroy(jk_globalPool);
+    apr_terminate();
+    /* Clean up and die. */
+    ExitThread(0);
+}
+
+BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
+{
+    DWORD dwThreadId;
+    ULONG http_filter_revision = HTTP_FILTER_REVISION;
+
+    jk2_inited_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+    jk2_starter_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+    jk2_starter_thread = CreateThread(NULL, 0,
+                                      (LPTHREAD_START_ROUTINE)
+                                      jk2_isapi_starter, NULL, 0,
+                                      &dwThreadId);
+
+    WaitForSingleObject(jk2_inited_event, INFINITE);
+    if (!is_inited || !is_mapread) {
+        return FALSE;
+    }
+    pVer->dwFilterVersion = pVer->dwServerFilterVersion;
+    if (pVer->dwFilterVersion > http_filter_revision) {
+        pVer->dwFilterVersion = http_filter_revision;
+    }
+    auth_notification_flags = get_auth_flags();
+
+    if (auth_notification_flags == SF_NOTIFY_AUTH_COMPLETE) {
+        pVer->dwFlags = SF_NOTIFY_ORDER_HIGH |
+            SF_NOTIFY_SECURE_PORT |
+            SF_NOTIFY_NONSECURE_PORT |
+            SF_NOTIFY_PREPROC_HEADERS | SF_NOTIFY_AUTH_COMPLETE;
+    }
+    else {
+        pVer->dwFlags = SF_NOTIFY_ORDER_HIGH |
+            SF_NOTIFY_SECURE_PORT |
+            SF_NOTIFY_NONSECURE_PORT | SF_NOTIFY_PREPROC_HEADERS;
+    }
+
+    strcpy(pVer->lpszFilterDesc, VERSION_STRING);
+
+    return TRUE;
+}
+
+DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc,
+                            DWORD dwNotificationType, LPVOID pvNotification)
+{
+    jk_env_t *env = NULL;
+    jk_uriEnv_t *uriEnv = NULL;
+
+    if (!is_inited) {
+        initialize_extension();
+    }
+
+    /* Initialise jk */
+    if (is_inited && !is_mapread) {
+        char serverName[MAX_SERVERNAME];
+        DWORD dwLen = sizeof(serverName);
+
+        if (pfc->GetServerVariable(pfc, SERVER_NAME, serverName, &dwLen)) {
+            if (dwLen > 0) {
+                serverName[dwLen - 1] = '\0';
+            }
+            if (init_jk(serverName)) {
+                is_mapread = JK_TRUE;
+            }
+        }
+        /* If we can't read the map we become dormant */
+        if (!is_mapread)
+            is_inited = JK_FALSE;
+    }
+    if (is_inited && is_mapread) {
+        env = workerEnv->globalEnv->getEnv(workerEnv->globalEnv);
+
+        if (auth_notification_flags == dwNotificationType) {
+            char uri[INTERNET_MAX_URL_LENGTH];
+            char snuri[INTERNET_MAX_URL_LENGTH] = "/";
+            char Host[INTERNET_MAX_URL_LENGTH];
+            char Translate[INTERNET_MAX_URL_LENGTH];
+            char Port[INTERNET_MAX_URL_LENGTH];
+            BOOL(WINAPI * GetHeader)
+                (struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,
+                 LPVOID lpvBuffer, LPDWORD lpdwSize);
+            BOOL(WINAPI * SetHeader)
+                (struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,
+                 LPSTR lpszValue);
+            BOOL(WINAPI * AddHeader)
+                (struct _HTTP_FILTER_CONTEXT * pfc, LPSTR lpszName,
+                 LPSTR lpszValue);
+            char *query;
+            DWORD sz = sizeof(uri);
+            DWORD szHost = sizeof(Host);
+            DWORD szTranslate = sizeof(Translate);
+            DWORD szPort = sizeof(Port);
+            int nPort;
+
+            if (auth_notification_flags == SF_NOTIFY_AUTH_COMPLETE) {
+                GetHeader =
+                    ((PHTTP_FILTER_AUTH_COMPLETE_INFO) pvNotification)->
+                    GetHeader;
+                SetHeader =
+                    ((PHTTP_FILTER_AUTH_COMPLETE_INFO) pvNotification)->
+                    SetHeader;
+                AddHeader =
+                    ((PHTTP_FILTER_AUTH_COMPLETE_INFO) pvNotification)->
+                    AddHeader;
+            }
+            else {
+                GetHeader =
+                    ((PHTTP_FILTER_PREPROC_HEADERS) pvNotification)->
+                    GetHeader;
+                SetHeader =
+                    ((PHTTP_FILTER_PREPROC_HEADERS) pvNotification)->
+                    SetHeader;
+                AddHeader =
+                    ((PHTTP_FILTER_PREPROC_HEADERS) pvNotification)->
+                    AddHeader;
+            }
+
+            env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                          "HttpFilterProc started\n");
+
+
+            /*
+             * Just in case somebody set these headers in the request!
+             */
+            SetHeader(pfc, URI_HEADER_NAME, NULL);
+            SetHeader(pfc, QUERY_HEADER_NAME, NULL);
+            SetHeader(pfc, WORKER_HEADER_NAME, NULL);
+            SetHeader(pfc, TOMCAT_TRANSLATE_HEADER_NAME, NULL);
+
+            if (!GetHeader(pfc, "url", (LPVOID) uri, (LPDWORD) & sz)) {
+                env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                              "HttpFilterProc error while getting the url\n");
+                workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+                return SF_STATUS_REQ_ERROR;
+            }
+
+            if (strlen(uri)) {
+                int rc;
+                char *worker = 0;
+                query = strchr(uri, '?');
+                if (query) {
+                    *query++ = '\0';
+                }
+
+                rc = jk_requtil_unescapeUrl(uri);
+                jk_requtil_getParents(uri);
+                Host[0] = '\0';
+                if (pfc->
+                    GetServerVariable(pfc, SERVER_NAME, (LPVOID) Host,
+                                      (LPDWORD) & szHost)) {
+                    if (szHost > 0) {
+                        Host[szHost - 1] = '\0';
+                    }
+                }
+                Port[0] = '\0';
+                if (pfc->
+                    GetServerVariable(pfc, "SERVER_PORT", (LPVOID) Port,
+                                      (LPDWORD) & szPort)) {
+                    if (szPort > 0) {
+                        Port[szPort - 1] = '\0';
+                    }
+                }
+                nPort = atoi(Port);
+                if (strlen(Host) > 1012 || nPort < 0 || nPort > 65535) {
+                    env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                                  "HttpFilterProc [%s] contains invalid host or port value.\n",
+                                  uri);
+                    write_error_response(pfc, "400 Bad Request",
+                                         HTML_ERROR_400);
+                    workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv,
+                                                     env);
+                    return SF_STATUS_REQ_FINISHED;
+                }
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "In HttpFilterProc Virtual Host redirection of %s : %s\n",
+                              Host, Port);
+                uriEnv =
+                    workerEnv->uriMap->mapUri(env, workerEnv->uriMap, Host,
+                                              nPort, uri);
+
+                if (uriEnv != NULL) {
+                    char *forwardURI;
+
+                    /* This is a servlet, should redirect ... */
+                    /* First check if the request was invalidated at decode */
+                    if (rc == BAD_REQUEST) {
+                        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                                      "HttpFilterProc [%s] contains one or more invalid escape sequences.\n",
+                                      uri);
+                        write_error_response(pfc, "400 Bad Request",
+                                             HTML_ERROR_400);
+                        workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv,
+                                                         env);
+                        return SF_STATUS_REQ_FINISHED;
+                    }
+                    else if (rc == BAD_PATH) {
+                        env->l->jkLog(env, env->l, JK_LOG_EMERG,
+                                      "HttpFilterProc [%s] contains forbidden escape sequences.\n",
+                                      uri);
+                        write_error_response(pfc, "403 Forbidden",
+                                             HTML_ERROR_403);
+                        workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv,
+                                                         env);
+                        return SF_STATUS_REQ_FINISHED;
+                    }
+                    env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                  "HttpFilterProc [%s] is a servlet url - should redirect to %s\n",
+                                  uri, uriEnv->workerName);
+
+                    /* get URI we should forward */
+
+                    if (workerEnv->options == JK_OPT_FWDURICOMPATUNPARSED) {
+                        /* get original unparsed URI */
+                        GetHeader(pfc, "url", (LPVOID) uri, (LPDWORD) & sz);
+                        /* restore terminator for uri portion */
+                        if (query)
+                            *(query - 1) = '\0';
+                        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                      "HttpFilterProc fowarding original URI [%s]\n",
+                                      uri);
+                        forwardURI = uri;
+                    }
+                    else if (workerEnv->options == JK_OPT_FWDURIESCAPED) {
+                        if (jk_requtil_escapeUrl
+                           (uri,snuri,INTERNET_MAX_URL_LENGTH) != JK_OK) {
+                            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                                          "HttpFilterProc [%s] re-encoding request exceeds maximum buffer size.\n",
+                                          uri);
+                            write_error_response(pfc, "400 Bad Request",
+                                                 HTML_ERROR_400);
+                            workerEnv->globalEnv->releaseEnv(workerEnv->
+                                                             globalEnv, env);
+                            return SF_STATUS_REQ_FINISHED;
+                        }
+                        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                      "HttpFilterProc fowarding escaped URI [%s]\n",
+                                      snuri);
+                        forwardURI = snuri;
+                    }
+                    else {
+                        forwardURI = uri;
+                    }
+
+                    if (!AddHeader(pfc, URI_HEADER_NAME, forwardURI) ||
+                        ((query != NULL && strlen(query) > 0)
+                         ? !AddHeader(pfc, QUERY_HEADER_NAME, query) : FALSE)
+                        || !AddHeader(pfc, WORKER_HEADER_NAME,
+                                      uriEnv->workerName)
+                        || !SetHeader(pfc, "url", extension_uri)) {
+                        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                                      "HttpFilterProc error while adding request headers\n");
+                        workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv,
+                                                         env);
+                        return SF_STATUS_REQ_ERROR;
+                    }
+
+                    /* Move Translate: header to a temporary header so
+                     * that the extension proc will be called.
+                     * This allows the servlet to handle 'Translate: f'.
+                     */
+                    if (GetHeader
+                        (pfc, "Translate:", (LPVOID) Translate,
+                         (LPDWORD) & szTranslate) && Translate != NULL
+                        && szTranslate > 0) {
+                        if (!AddHeader
+                            (pfc, TOMCAT_TRANSLATE_HEADER_NAME, Translate)) {
+                            env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                                          "HttpFilterProc error while adding Tomcat-Translate headers\n");
+                            workerEnv->globalEnv->releaseEnv(workerEnv->
+                                                             globalEnv, env);
+                            return SF_STATUS_REQ_ERROR;
+                        }
+                        SetHeader(pfc, "Translate:", NULL);
+                    }
+                }
+                else {
+                    char *jsessionid =
+                        strstr(uri, JK_PATH_SESSION_IDENTIFIER);
+                    if (jsessionid) {
+                        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                                      "HttpFilterProc removing session identifier [%s] for non servlet url\n",
+                                      jsessionid);
+                        *jsessionid = '\0';
+                        SetHeader(pfc, "url", uri);
+                    }
+                    env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                  "HttpFilterProc [%s] is not a servlet url\n",
+                                  uri);
+                }
+
+                /*
+                 * Check if somebody is feeding us with his own TOMCAT data headers.
+                 * We reject such postings !
+                 */
+                env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                              "HttpFilterProc check if [%s] is pointing to the web-inf directory\n",
+                              uri);
+
+                if (jk_requtil_uriIsWebInf(uri)) {
+                    env->l->jkLog(env, env->l, JK_LOG_EMERG,
+                                  "HttpFilterProc [%s] points to the web-inf or meta-inf directory.\nSomebody try to hack into the site!!!\n",
+                                  uri);
+
+                    write_error_response(pfc, "403 Forbidden",
+                                         HTML_ERROR_403);
+                    workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv,
+                                                     env);
+                    return SF_STATUS_REQ_FINISHED;
+                }
+            }
+        }
+        workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+    }
+    return SF_STATUS_REQ_NEXT_NOTIFICATION;
+}
+
+
+BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO * pVer)
+{
+    pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
+
+    strcpy(pVer->lpszExtensionDesc, VERSION_STRING);
+
+    return TRUE;
+}
+
+DWORD WINAPI HttpExtensionProcWorker(LPEXTENSION_CONTROL_BLOCK lpEcb,
+                                     jk_ws_service_t *service)
+{
+    DWORD rc = HSE_STATUS_ERROR;
+    jk_env_t *env;
+    jk_ws_service_t sOnStack;
+    jk_ws_service_t *s;
+    char *worker_name;
+    char huge_buf[16 * 1024];   /* should be enough for all */
+    DWORD huge_buf_sz;
+    jk_worker_t *worker;
+    jk_pool_t *rPool = NULL;
+    int rc1;
+
+    if (service)
+        s = service;
+    else
+        s = &sOnStack;
+
+    lpEcb->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
+    env = workerEnv->globalEnv->getEnv(workerEnv->globalEnv);
+    env->l->jkLog(env, env->l, JK_LOG_DEBUG, "HttpExtensionProc started\n");
+
+    huge_buf_sz = sizeof(huge_buf);
+    get_server_value(lpEcb, HTTP_WORKER_HEADER_NAME, huge_buf, huge_buf_sz,
+                     "");
+    worker_name = huge_buf;
+
+    worker = env->getByName(env, worker_name);
+
+    env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                  "HttpExtensionProc %s a worker for name %s\n",
+                  worker ? "got" : "could not get", worker_name);
+
+    if (worker == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "HttpExtensionProc worker is NULL\n");
+        return rc;
+    }
+    /* Get a pool for the request XXX move it in workerEnv to
+       be shared with other server adapters */
+    rPool = worker->rPoolCache->get(env, worker->rPoolCache);
+    if (rPool == NULL) {
+        rPool =
+            worker->mbean->pool->create(env, worker->mbean->pool,
+                                        HUGE_POOL_SIZE);
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "HttpExtensionProc: new rpool\n");
+    }
+
+    jk2_service_iis_init(env, s);
+    s->pool = rPool;
+
+    /* reset the reco_status, will be set to INITED in LB mode */
+    s->reco_status = RECO_NONE;
+
+    s->is_recoverable_error = JK_FALSE;
+    s->response_started = JK_FALSE;
+    s->content_read = 0;
+    s->ws_private = lpEcb;
+    s->workerEnv = workerEnv;
+
+    /* Initialize the ws_service structure */
+    s->init(env, s, worker, lpEcb);
+
+    if (JK_OK == worker->service(env, worker, s)) {
+        rc = HSE_STATUS_SUCCESS;
+        lpEcb->dwHttpStatusCode = HTTP_STATUS_OK;
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "HttpExtensionProc service() returned OK\n");
+    }
+    else {
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "HttpExtensionProc service() Failed\n");
+    }
+
+    s->afterRequest(env, s);
+
+    if (service != NULL) {
+        lpEcb->ServerSupportFunction(lpEcb->ConnID,
+                                     HSE_REQ_DONE_WITH_SESSION,
+                                     NULL, NULL, NULL);
+    }
+    rPool->reset(env, rPool);
+    rc1 = worker->rPoolCache->put(env, worker->rPoolCache, rPool);
+
+    workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
+
+    return rc;
+}
+
+DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpEcb)
+{
+    if (is_inited) {
+        if (!use_thread_pool) {
+            return HttpExtensionProcWorker(lpEcb, NULL);
+        }
+        else {
+            /* Pass the request to the thread pool */
+            jk2_iis_thread_pool(lpEcb);
+            return HSE_STATUS_PENDING;
+        }
+    }
+    return HSE_STATUS_ERROR;
+}
+
+BOOL WINAPI TerminateExtension(DWORD dwFlags)
+{
+    return TerminateFilter(dwFlags);
+}
+
+BOOL WINAPI TerminateFilter(DWORD dwFlags)
+{
+    /* detatch the starter thread */
+    SetEvent(jk2_starter_event);
+    WaitForSingleObject(jk2_starter_thread, 3000);
+    CloseHandle(jk2_starter_thread);
+    jk2_starter_thread = INVALID_HANDLE_VALUE;
+    return TRUE;
+}
+
+
+
+BOOL WINAPI DllMain(HINSTANCE hInst,    // Instance Handle of the DLL
+                    ULONG ulReason,     // Reason why NT called this DLL
+                    LPVOID lpReserved)  // Reserved parameter for future use
+{
+    BOOL fReturn = TRUE;
+    char drive[_MAX_DRIVE];
+    char dir[_MAX_DIR];
+    char fname[_MAX_FNAME];
+
+    switch (ulReason) {
+    case DLL_PROCESS_ATTACH:
+        if (GetModuleFileName(hInst, file_name, sizeof(file_name))) {
+            _splitpath(file_name, drive, dir, fname, NULL);
+            _makepath(ini_file_name, drive, dir, fname, ".properties");
+
+        }
+        else {
+            fReturn = JK_FALSE;
+        }
+        break;
+    default:
+        break;
+    }
+    return fReturn;
+}
+
+static int init_jk(char *serverName)
+{
+    int rc = JK_TRUE;
+    /* XXX this need review, works well because the initializations are done at the first request 
+       but in case inits should be splited another time using directly globalEnv here could lead 
+       to subtle problems.. 
+     */
+    jk_env_t *env = workerEnv->globalEnv;
+    workerEnv->initData->add(env, workerEnv->initData, "serverRoot",
+                             workerEnv->pool->pstrdup(env, workerEnv->pool,
+                                                      server_root));
+    /* Logging the initialization type: registry or properties file in virtual dir
+     */
+    if (strlen(worker_file)) {
+        rc = (JK_OK ==
+              workerEnv->config->setPropertyString(env, workerEnv->config,
+                                                   "config.file",
+                                                   worker_file));
+    }
+    workerEnv->init(env, workerEnv);
+
+    env->l->jkLog(env, env->l, JK_LOG_INFO, "Set serverRoot %s\n",
+                  server_root);
+    if (using_ini_file) {
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG, "Using ini file %s.\n",
+                      ini_file_name);
+    }
+    else {
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG, "Using registry.\n");
+    }
+    env->l->jkLog(env, env->l, JK_LOG_DEBUG, "Using extension uri %s.\n",
+                  extension_uri);
+    env->l->jkLog(env, env->l, JK_LOG_DEBUG, "Using server root %s.\n",
+                  server_root);
+    env->l->jkLog(env, env->l, JK_LOG_DEBUG, "Using worker file %s.\n",
+                  worker_file);
+    return rc;
+}
+
+static int initialize_extension()
+{
+    jk_env_t *env = jk2_create_config();
+    if (read_registry_init_data(env)) {
+        jk2_iis_init_pool(env);
+        is_inited = JK_TRUE;
+    }
+    return is_inited;
+}
+
+static int read_registry_init_data(jk_env_t *env)
+{
+    char tmpbuf[INTERNET_MAX_URL_LENGTH];
+    HKEY hkey;
+    long rc;
+    int ok = JK_TRUE;
+    char *tmp;
+    jk_map_t *map;
+
+    if (JK_OK == jk2_map_default_create(env, &map, workerEnv->pool)) {
+        if (JK_OK == jk2_config_file_read(env, map, ini_file_name)) {
+            tmp = map->get(env, map, EXTENSION_URI_TAG);
+            if (tmp) {
+                strcpy(extension_uri, tmp);
+            }
+            else {
+                ok = JK_FALSE;
+            }
+            tmp = map->get(env, map, SERVER_ROOT_TAG);
+            if (tmp) {
+                strcpy(server_root, tmp);
+            }
+            else {
+                ok = JK_FALSE;
+            }
+            tmp = map->get(env, map, WORKERS_FILE_TAG);
+            if (tmp) {
+                strcpy(worker_file, tmp);
+            }
+            tmp = map->get(env, map, THREAD_POOL_TAG);
+            if (tmp) {
+                use_thread_pool = atoi(tmp);
+                if (use_thread_pool < 10)
+                    use_thread_pool = 0;
+            }
+            tmp = map->get(env, map, USE_AUTH_COMP_TAG);
+            if (tmp) {
+                use_auth_notification_flags = atoi(tmp);
+            }
+            tmp = map->get(env, map, SEND_GROUPS_TAG);
+            if (tmp) {
+                send_groups = atoi(tmp);
+            }
+            using_ini_file = JK_TRUE;
+            return ok;
+        }
+    }
+    else {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "read_registry_init_data, Failed to create map \n");
+    }
+    rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                      REGISTRY_LOCATION, (DWORD) 0, KEY_READ, &hkey);
+    if (ERROR_SUCCESS != rc) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "read_registry_init_data, Failed Registry OpenKey %s\n",
+                      REGISTRY_LOCATION);
+        return JK_FALSE;
+    }
+
+    if (get_registry_config_parameter(hkey,
+                                      EXTENSION_URI_TAG,
+                                      tmpbuf, sizeof(extension_uri))) {
+        strcpy(extension_uri, tmpbuf);
+    }
+    else {
+        ok = JK_FALSE;
+    }
+
+    if (get_registry_config_parameter(hkey,
+                                      SERVER_ROOT_TAG,
+                                      tmpbuf, sizeof(server_root))) {
+        strcpy(server_root, tmpbuf);
+    }
+    else {
+        ok = JK_FALSE;
+    }
+    if (get_registry_config_parameter(hkey,
+                                      WORKERS_FILE_TAG,
+                                      tmpbuf, sizeof(server_root))) {
+        strcpy(worker_file, tmpbuf);
+    }
+    else {
+        ok = JK_FALSE;
+    }
+    if (get_registry_config_parameter(hkey, THREAD_POOL_TAG, tmpbuf, 8)) {
+        use_thread_pool = atoi(tmpbuf);
+        if (use_thread_pool < 10) {
+            use_thread_pool = 0;
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "read_registry_init_data, ThreadPool must be set to the value 10 or higher\n");
+        }
+    }
+    if (get_registry_config_parameter(hkey, USE_AUTH_COMP_TAG, tmpbuf, 8)) {
+        use_auth_notification_flags = atoi(tmpbuf);
+    }
+
+    if (get_registry_config_parameter(hkey, SEND_GROUPS_TAG, tmpbuf, 8)) {
+        send_groups = atoi(tmpbuf);
+    }
+
+    RegCloseKey(hkey);
+    return ok;
+}
+
+static int get_registry_config_parameter(HKEY hkey,
+                                         const char *tag, char *b, DWORD sz)
+{
+    DWORD type = 0;
+    LONG lrc;
+
+    lrc = RegQueryValueEx(hkey, tag, (LPDWORD) 0, &type, (LPBYTE) b, &sz);
+    if ((ERROR_SUCCESS != lrc) || (type != REG_SZ)) {
+        return JK_FALSE;
+    }
+
+    b[sz] = '\0';
+
+    return JK_TRUE;
+}
+
+
+/** Basic initialization for jk2.
+ */
+static jk_env_t *jk2_create_workerEnv(void)
+{
+
+    jk_logger_t *l;
+    jk_pool_t *globalPool;
+    jk_bean_t *jkb;
+    jk_env_t *env;
+
+    jk2_pool_apr_create(NULL, &globalPool, NULL, jk_globalPool);
+
+    /** Create the global environment. This will register the default
+        factories
+    */
+    env = jk2_env_getEnv(NULL, globalPool);
+
+    /* Optional. Register more factories ( or replace existing ones ) */
+    /* Init the environment. */
+
+    /* Create the logger */
+    jkb = env->createBean2(env, env->globalPool, "logger.win32", "");
+    env->alias(env, "logger.win32:", "logger");
+    l = jkb->object;
+
+    env->l = l;
+    env->soName = env->globalPool->pstrdup(env, env->globalPool, file_name);
+
+    if (env->soName == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "Error creating env->soName\n");
+        return env;
+    }
+    env->l->init(env, env->l);
+
+    /* We should make it relative to JK_HOME or absolute path.
+       ap_server_root_relative(cmd->pool,opt); */
+
+    /* Create the workerEnv */
+    jkb = env->createBean2(env, env->globalPool, "workerEnv", "");
+    workerEnv = jkb->object;
+    env->alias(env, "workerEnv:", "workerEnv");
+
+    if (workerEnv == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "Error creating workerEnv\n");
+        return env;
+    }
+
+    workerEnv->childId = 0;
+/* XXX 
+    
+    Detect install dir, be means of service configs, */
+
+
+    return env;
+}
+
+static jk_env_t *jk2_create_config()
+{
+    jk_env_t *env;
+    if (workerEnv == NULL) {
+        env = jk2_create_workerEnv();
+        env->l->jkLog(env, env->l, JK_LOG_INFO, "JK2 Config Created");
+    }
+    else {
+        env = workerEnv->globalEnv->getEnv(workerEnv->globalEnv);
+        env->l->jkLog(env, env->l, JK_LOG_INFO, "JK2 Config Reused");
+    }
+
+
+
+    return env;
+}
+
+static int get_auth_flags()
+{
+    HKEY hkey;
+    long rc;
+    int maj, sz;
+    int rv = SF_NOTIFY_PREPROC_HEADERS;
+    int use_auth = JK_FALSE;
+    /* Retreive the IIS version Major */
+    rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                      W3SVC_REGISTRY_KEY, (DWORD) 0, KEY_READ, &hkey);
+    if (ERROR_SUCCESS != rc) {
+        return rv;
+    }
+    sz = sizeof(int);
+    rc = RegQueryValueEx(hkey,
+                         "MajorVersion", NULL, NULL, (LPBYTE) & maj, &sz);
+    if (ERROR_SUCCESS != rc) {
+        CloseHandle(hkey);
+        return rv;
+    }
+    CloseHandle(hkey);
+    if (use_auth_notification_flags && maj > 4)
+        rv = SF_NOTIFY_AUTH_COMPLETE;
+
+    return rv;
+}
diff --git a/connectors/jk/native2/server/isapi/jk_service_iis.c b/connectors/jk/native2/server/isapi/jk_service_iis.c
new file mode 100644
index 0000000..fa837de
--- /dev/null
+++ b/connectors/jk/native2/server/isapi/jk_service_iis.c
@@ -0,0 +1,658 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * Description: IIS Jk2 Service 
+ * Author:      Gal Shachor <shachor@il.ibm.com>                           
+ *              Henri Gomez <hgomez@apache.org> 
+ *              Ignacio J. Ortega <nacho@apache.org>
+ */
+
+// This define is needed to include wincrypt,h, needed to get client certificates
+#define _WIN32_WINNT 0x0400
+
+#include <httpext.h>
+#include <httpfilt.h>
+#include <wininet.h>
+
+#include "jk_global.h"
+#include "jk_requtil.h"
+#include "jk_map.h"
+#include "jk_pool.h"
+#include "jk_env.h"
+#include "jk_service.h"
+#include "jk_worker.h"
+
+#include "jk_iis.h"
+
+#define ROLES_ATTRIBUTE_NAME  "org.apache.tomcat.jk.roles"
+
+extern int send_groups;
+
+static int JK_METHOD jk2_service_iis_head(jk_env_t *env, jk_ws_service_t *s)
+{
+    static char crlf[3] = { '\r', '\n', '\0' };
+    const char *reason;
+    LPEXTENSION_CONTROL_BLOCK lpEcb =
+        (LPEXTENSION_CONTROL_BLOCK) s->ws_private;
+    DWORD len_of_status;
+    char *status_str;
+    char *headers_str;
+    int headerCount;
+
+    env->l->jkLog(env, env->l, JK_LOG_DEBUG, "Into jk_ws_service_t::head\n");
+
+    if (s->status < 100 || s->status > 1000) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "jk_ws_service_t::jk2_service_iis_head, invalid status %d\n",
+                      s->status);
+        return JK_ERR;
+    }
+
+    if (lpEcb == NULL) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "jk_ws_service_t::head, no lpEcp\n");
+        return JK_ERR;
+    }
+
+    s->response_started = JK_TRUE;
+
+    /*
+     * Create the status line
+     */
+    if (s->msg == NULL) {
+        reason = "";
+    }
+    else {
+        reason = s->msg;
+    }
+
+    status_str = (char *)_alloca((6 + strlen(reason)) * sizeof(char));
+    sprintf(status_str, "%d %s", s->status, reason);
+    len_of_status = strlen(status_str);
+
+    headerCount = s->headers_out->size(env, s->headers_out);
+    /*
+     * Create response headers string
+     */
+    if (headerCount > 0) {
+        int i;
+        unsigned len_of_headers;
+
+        for (i = 0, len_of_headers = 0; i < headerCount; i++) {
+            len_of_headers +=
+                strlen(s->headers_out->nameAt(env, s->headers_out, i));
+            len_of_headers +=
+                strlen(s->headers_out->valueAt(env, s->headers_out, i));
+            len_of_headers += 4;        /* extra for colon, space and crlf */
+        }
+
+        len_of_headers += 3;    /* crlf and terminating null char */
+        headers_str = (char *)_alloca(len_of_headers * sizeof(char));
+        headers_str[0] = '\0';
+
+        for (i = 0; i < headerCount; i++) {
+            strcat(headers_str,
+                   s->headers_out->nameAt(env, s->headers_out, i));
+            strcat(headers_str, ": ");
+            strcat(headers_str,
+                   s->headers_out->valueAt(env, s->headers_out, i));
+            strcat(headers_str, crlf);
+        }
+        strcat(headers_str, crlf);
+    }
+    else {
+        headers_str = crlf;
+    }
+
+    if (!lpEcb->ServerSupportFunction(lpEcb->ConnID,
+                                      HSE_REQ_SEND_RESPONSE_HEADER,
+                                      status_str,
+                                      (LPDWORD) & len_of_status,
+                                      (LPDWORD) headers_str)) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "jk_ws_service_t::head, ServerSupportFunction failed\n");
+        return JK_ERR;
+    }
+
+    return JK_OK;
+}
+
+static int JK_METHOD jk2_service_iis_read(jk_env_t *env, jk_ws_service_t *s,
+                                          void *b, unsigned int len,
+                                          unsigned int *actually_read)
+{
+
+    env->l->jkLog(env, env->l, JK_LOG_DEBUG, "Into jk_ws_service_t::read\n");
+
+    *actually_read = 0;
+    if (!len) {
+        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                      "jk_ws_service_t::read, requested read length is zero\n");
+        return JK_OK;
+    }
+    if (s && s->ws_private && b) {
+        LPEXTENSION_CONTROL_BLOCK lpEcb =
+            (LPEXTENSION_CONTROL_BLOCK) s->ws_private;
+
+        if (s->end_of_stream) {
+            env->l->jkLog(env, env->l, JK_LOG_INFO,
+                          "jk_ws_service_t::read, end of stram allready reached\n");
+            return JK_OK;
+        }
+
+        if ((DWORD) s->content_read < lpEcb->cbTotalBytes) {
+            DWORD rdlen, toread = len;
+            DWORD cblen = 0;
+            LPBYTE buff = (LPBYTE) b;
+
+            /* 
+             * Fix the read length in case the requested
+             * is larger then what's available
+             */
+            if (s->content_read + toread > lpEcb->cbTotalBytes)
+                toread = lpEcb->cbTotalBytes - s->content_read;
+            rdlen = toread;
+
+            /* 
+             * First read the already sent data from the client
+             * No need to call the ReadClient fuction for the
+             * data held in the control buffer
+             */
+            if ((DWORD) s->content_read < lpEcb->cbAvailable) {
+                /* Read the avail buffer */
+                if (s->content_read + toread > lpEcb->cbAvailable)
+                    toread = lpEcb->cbAvailable - s->content_read;
+                memcpy(buff, lpEcb->lpbData + s->content_read, toread);
+                *actually_read = toread;
+
+                /* if that's all what that server wants to read, return... */
+                if (toread == rdlen) {
+                    env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                  "jk_ws_service_t::read buffer readed %d from already %d of initial %d bytes from %d\n",
+                                  toread, s->content_read, lpEcb->cbAvailable,
+                                  lpEcb->cbTotalBytes);
+                    return JK_OK;
+                }
+                else {
+                    /* Adjust the read buffer and length */
+                    rdlen -= toread;
+                    buff += toread;
+                    env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                  "jk_ws_service_t::read initial readed %d going to read %d\n",
+                                  toread, rdlen);
+                }
+            }
+
+            /*
+             * Now try to read from the client ...
+             */
+            
+            while (cblen < rdlen) {
+                toread = rdlen - cblen;
+                if (lpEcb->ReadClient(lpEcb->ConnID, buff + cblen, &toread)) {
+                    if (toread == 0)
+                        break;
+                    cblen += toread;
+                    env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                  "jk_ws_service_t::read ReadClient readed %d (actually %d) bytes\n",
+                                  toread, *actually_read + cblen);
+
+                }
+                else {
+                    env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                                  "jk_ws_service_t::read, ReadClient failed\n");
+                    /* XXX: We should return here HSE_STATUS_ERROR */
+                    break;
+                }
+            }
+            *actually_read += cblen;
+        }
+        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                      "jk_ws_service_t::read actually readed %d from already %d of total %d bytes\n",
+                      *actually_read, s->content_read, lpEcb->cbTotalBytes);
+        if ((s->content_read + *actually_read) == lpEcb->cbTotalBytes) {
+            s->end_of_stream = JK_TRUE;
+        }
+        return JK_OK;
+    }
+    else {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "jk_ws_service_t::read, NULL parameters\n");
+        return JK_ERR;
+    }
+}
+
+static int JK_METHOD jk2_service_iis_write(jk_env_t *env, jk_ws_service_t *s,
+                                           const void *b, unsigned int len)
+{
+    env->l->jkLog(env, env->l, JK_LOG_DEBUG, "Into jk_ws_service_t::write\n");
+
+    if (s && s->ws_private && b) {
+        LPEXTENSION_CONTROL_BLOCK lpEcb =
+            (LPEXTENSION_CONTROL_BLOCK) s->ws_private;
+
+        if (len) {
+            unsigned int written = 0;
+            char *buf = (char *)b;
+
+            if (!s->response_started) {
+                s->head(env, s);
+            }
+
+            while (written < len) {
+                DWORD try_to_write = len - written;
+                if (!lpEcb->WriteClient(lpEcb->ConnID,
+                                        buf + written, &try_to_write, 0)) {
+                    env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                                  "jk_ws_service_t::write, WriteClient failed\n");
+                    return JK_ERR;
+                }
+                written += try_to_write;
+            }
+        }
+
+        return JK_OK;
+
+    }
+
+    env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                  "jk_ws_service_t::write, NULL parameters\n");
+
+    return JK_ERR;
+}
+
+int get_server_value(LPEXTENSION_CONTROL_BLOCK lpEcb,
+                     char *name, char *buf, DWORD bufsz, char *def_val)
+{
+    if (!lpEcb->GetServerVariable(lpEcb->ConnID,
+                                  name, buf, (LPDWORD) & bufsz)) {
+        strcpy(buf, def_val);
+        return JK_FALSE;
+    }
+
+    if (bufsz > 0) {
+        buf[bufsz - 1] = '\0';
+    }
+
+    return JK_TRUE;
+}
+
+#define MAX_NAME 256
+
+char *jk2_service_iis_get_roles(jk_env_t *env, jk_ws_service_t *s)
+{
+    LPEXTENSION_CONTROL_BLOCK lpEcb =
+        (LPEXTENSION_CONTROL_BLOCK) s->ws_private;
+    HANDLE h;
+    DWORD len = 0;
+    PTOKEN_GROUPS g = NULL;
+    unsigned i;
+    char *roles = NULL;
+    if (lpEcb->ServerSupportFunction(lpEcb->ConnID,
+                                     HSE_REQ_GET_IMPERSONATION_TOKEN,
+                                     (LPVOID) & h, NULL, NULL) != FALSE) {
+        // First get the length of the user's groups array and gets the memory 
+        if (!GetTokenInformation(h, TokenGroups, NULL, len, &len)) {
+            if (ERROR_INSUFFICIENT_BUFFER == GetLastError()) {
+                g = (PTOKEN_GROUPS) s->pool->calloc(env, s->pool, len);
+            }
+        }
+        if (g != NULL) {
+            if (GetTokenInformation(h, TokenGroups, g, len, &len)) {
+                roles =
+                    s->pool->calloc(env, s->pool, (g->GroupCount) * MAX_NAME);
+                for (i = 0; i < g->GroupCount; i++) {
+                    char name[MAX_NAME], domain[MAX_NAME];
+                    DWORD nLen = MAX_NAME, dLen = MAX_NAME;
+                    SID_NAME_USE eUse;
+                    // Get  the user name and the domain from the SID.
+                    env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                  "jk2_service_iis_get_roles requesting name for member:%d attributes:%#lx SID:%#lx \n",
+                                  i, g->Groups[i].Attributes,
+                                  g->Groups[i].Sid);
+                    if (!LookupAccountSid
+                        (NULL, g->Groups[i].Sid, name, &nLen, domain, &dLen,
+                         &eUse)) {
+                        env->l->jkLog(env, env->l, JK_LOG_INFO,
+                                      "jk2_service_iis_get_roles problems requesting name for member:%d attributes:%#lx SID:%#lx \n",
+                                      i, g->Groups[i].Attributes,
+                                      g->Groups[i].Sid);
+
+                    }
+                    else {
+                        strcpy(roles + strlen(roles), name);
+                        roles[strlen(roles)] = ',';
+                        roles[strlen(roles) + 1] = '\0';
+                        env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                      "jk2_service_iis_get_roles member:%d attributes:%#lx SID:%#lx name:%s\n",
+                                      i, g->Groups[i].Attributes,
+                                      g->Groups[i].Sid, name);
+                    }
+                }
+                roles[strlen(roles) - 1] = '\0';
+                env->l->jkLog(env, env->l, JK_LOG_INFO,
+                              "jk_ws_service_t::jk2_service_iis_get_roles roles:%s \n",
+                              roles);
+            }
+            return roles;
+        }
+        else {
+            return NULL;
+        }
+    }
+    return NULL;
+}
+
+static int JK_METHOD jk2_service_iis_initService(struct jk_env *env,
+                                                 jk_ws_service_t *s,
+                                                 struct jk_worker *w,
+                                                 void *serverObj)
+/* */
+{
+    LPEXTENSION_CONTROL_BLOCK lpEcb = (LPEXTENSION_CONTROL_BLOCK) serverObj;
+    char huge_buf[16 * 1024];   /* should be enough for all */
+
+    DWORD huge_buf_sz;
+
+    s->jvm_route = NULL;
+
+    GET_SERVER_VARIABLE_VALUE(s->pool, HTTP_URI_HEADER_NAME, s->req_uri);
+    GET_SERVER_VARIABLE_VALUE(s->pool, HTTP_QUERY_HEADER_NAME,
+                              s->query_string);
+
+    if (s->req_uri == NULL) {
+        s->query_string = lpEcb->lpszQueryString;
+        /* *worker_name    = DEFAULT_WORKER_NAME; */
+        GET_SERVER_VARIABLE_VALUE(s->pool, "URL", s->req_uri);
+        if (jk_requtil_unescapeUrl(s->req_uri) < 0)
+            return JK_ERR;
+        jk_requtil_getParents(s->req_uri);
+    }
+
+    GET_SERVER_VARIABLE_VALUE(s->pool, "AUTH_TYPE", s->auth_type);
+    GET_SERVER_VARIABLE_VALUE(s->pool, "REMOTE_USER", s->remote_user);
+    GET_SERVER_VARIABLE_VALUE(s->pool, "SERVER_PROTOCOL", s->protocol);
+    GET_SERVER_VARIABLE_VALUE(s->pool, "REMOTE_HOST", s->remote_host);
+    GET_SERVER_VARIABLE_VALUE(s->pool, "REMOTE_ADDR", s->remote_addr);
+    GET_SERVER_VARIABLE_VALUE(s->pool, SERVER_NAME, s->server_name);
+    GET_SERVER_VARIABLE_VALUE_INT("SERVER_PORT", s->server_port, 80);
+    GET_SERVER_VARIABLE_VALUE(s->pool, SERVER_SOFTWARE, s->server_software);
+    GET_SERVER_VARIABLE_VALUE_INT("SERVER_PORT_SECURE", s->is_ssl, 0);
+
+    s->method = lpEcb->lpszMethod;
+    s->content_length = lpEcb->cbTotalBytes;
+    s->end_of_stream = JK_FALSE;
+
+    s->ssl_cert = NULL;
+    s->ssl_cert_len = 0;
+    s->ssl_cipher = NULL;
+    s->ssl_session = NULL;
+    s->ssl_key_size = -1;
+
+    if (JK_OK != jk2_map_default_create(env, &s->headers_out, s->pool)) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "jk_ws_service_t::init, Failed to create headers_out map \n");
+        return JK_ERR;
+
+    }
+    if (JK_OK != jk2_map_default_create(env, &s->attributes, s->pool)) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "jk_ws_service_t::init, Failed to create attributes map \n");
+        return JK_ERR;
+
+    }
+    if (JK_OK != jk2_map_default_create(env, &s->headers_in, s->pool)) {
+        env->l->jkLog(env, env->l, JK_LOG_ERROR,
+                      "jk_ws_service_t::init, Failed to create headers_in map \n");
+        return JK_ERR;
+    }
+//    s->headers_values   = NULL;
+//  s->num_headers      = 0;
+
+    /*
+     * Add SSL IIS environment
+     */
+
+    if (send_groups && *s->remote_user) {
+        char *groups = jk2_service_iis_get_roles(env, s);
+        if (groups != NULL) {
+            s->attributes->put(env, s->attributes, ROLES_ATTRIBUTE_NAME,
+                               groups, NULL);
+        }
+    }
+
+    if (s->is_ssl) {
+        char *ssl_env_names[9] = {
+            "CERT_ISSUER",
+            "CERT_SUBJECT",
+            "CERT_COOKIE",
+            "HTTPS_SERVER_SUBJECT",
+            "CERT_FLAGS",
+            "HTTPS_SECRETKEYSIZE",
+            "CERT_SERIALNUMBER",
+            "HTTPS_SERVER_ISSUER",
+            "HTTPS_KEYSIZE"
+        };
+        char *ssl_env_values[9] = {
+            NULL,
+            NULL,
+            NULL,
+            NULL,
+            NULL,
+            NULL,
+            NULL,
+            NULL,
+            NULL
+        };
+        unsigned i;
+        unsigned num_of_vars = 0;
+
+        for (i = 0; i < 9; i++) {
+            GET_SERVER_VARIABLE_VALUE(s->pool, ssl_env_names[i],
+                                      ssl_env_values[i]);
+            if (ssl_env_values[i]) {
+                num_of_vars++;
+            }
+        }
+        if (num_of_vars) {
+            unsigned j = 0;
+            for (i = 0; i < 9; i++) {
+                if (ssl_env_values[i]) {
+                    s->attributes->put(env, s->attributes,
+                                       ssl_env_names[i], ssl_env_values[i],
+                                       NULL);
+                    j++;
+                }
+            }
+            if (ssl_env_values[4] && ssl_env_values[4][0] == '1') {
+                CERT_CONTEXT_EX cc;
+                DWORD cc_sz = sizeof(cc);
+                cc.cbAllocated = sizeof(huge_buf);
+                cc.CertContext.pbCertEncoded = (BYTE *) huge_buf;
+                cc.CertContext.cbCertEncoded = 0;
+
+                if (lpEcb->ServerSupportFunction(lpEcb->ConnID,
+                                                 (DWORD)
+                                                 HSE_REQ_GET_CERT_INFO_EX,
+                                                 (LPVOID) & cc, NULL,
+                                                 NULL) != FALSE) {
+                    env->l->jkLog(env, env->l, JK_LOG_DEBUG,
+                                  "Client Certificate encoding:%d sz:%d flags:%ld\n",
+                                  cc.CertContext.
+                                  dwCertEncodingType & X509_ASN_ENCODING,
+                                  cc.CertContext.cbCertEncoded,
+                                  cc.dwCertificateFlags);
+                    s->ssl_cert =
+                        s->pool->alloc(env, s->pool,
+                                       jk_requtil_base64CertLen(cc.
+                                                                CertContext.
+                                                                cbCertEncoded));
+
+                    s->ssl_cert_len = jk_requtil_base64EncodeCert(s->ssl_cert,
+                                                                  huge_buf,
+                                                                  cc.
+                                                                  CertContext.
+                                                                  cbCertEncoded)
+                        - 1;
+                }
+            }
+        }
+    }
+
+
+    huge_buf_sz = sizeof(huge_buf);
+    if (get_server_value(lpEcb, "ALL_HTTP", huge_buf, huge_buf_sz, "")) {
+        unsigned cnt = 0;
+        char *tmp;
+
+        for (tmp = huge_buf; *tmp; tmp++) {
+            if (*tmp == '\n') {
+                cnt++;
+            }
+        }
+
+        if (cnt) {
+            char *headers_buf = s->pool->pstrdup(env, s->pool, huge_buf);
+            unsigned i;
+            unsigned len_of_http_prefix = strlen("HTTP_");
+            BOOL need_content_length_header = (s->content_length == 0);
+
+            cnt -= 2;           /* For our two special headers */
+            /* allocate an extra header slot in case we need to add a content-length header */
+            for (i = 0, tmp = headers_buf; *tmp && i < cnt;) {
+                int real_header = JK_TRUE;
+                char *headerName;
+                /* Skipp the HTTP_ prefix to the beginning of th header name */
+                tmp += len_of_http_prefix;
+
+                if (!strnicmp(tmp, URI_HEADER_NAME, strlen(URI_HEADER_NAME))
+                    || !strnicmp(tmp, WORKER_HEADER_NAME,
+                                 strlen(WORKER_HEADER_NAME))) {
+                    real_header = JK_FALSE;
+                }
+                else if (need_content_length_header &&
+                         !strnicmp(tmp, CONTENT_LENGTH,
+                                   strlen(CONTENT_LENGTH))) {
+                    need_content_length_header = FALSE;
+                    headerName = tmp;
+                }
+                else if (!strnicmp(tmp, TOMCAT_TRANSLATE_HEADER_NAME,
+                                   strlen(TOMCAT_TRANSLATE_HEADER_NAME))) {
+                    tmp += 6;   /* TOMCAT */
+                    headerName = tmp;
+                }
+                else {
+                    headerName = tmp;
+                }
+
+                while (':' != *tmp && *tmp) {
+                    if ('_' == *tmp) {
+                        *tmp = '-';
+                    }
+                    else {
+                        *tmp = tolower(*tmp);
+                    }
+                    tmp++;
+                }
+                *tmp = '\0';
+                tmp++;
+
+                /* Skip all the WS chars after the ':' to the beginning of th header value */
+                while (' ' == *tmp || '\t' == *tmp || '\v' == *tmp) {
+                    tmp++;
+                }
+
+                if (real_header) {
+                    s->headers_in->put(env, s->headers_in, headerName, tmp,
+                                       NULL);
+                }
+
+                while (*tmp != '\n' && *tmp != '\r') {
+                    tmp++;
+                }
+                *tmp = '\0';
+                tmp++;
+
+                /* skipp CR LF */
+                while (*tmp == '\n' || *tmp == '\r') {
+                    tmp++;
+                }
+
+                if (real_header) {
+                    i++;
+                }
+            }
+            /* Add a content-length = 0 header if needed.
+             * Ajp13 assumes an absent content-length header means an unknown,
+             * but non-zero length body.
+             */
+            if (need_content_length_header) {
+                s->headers_in->put(env, s->headers_in,
+                                   "content-length", "0", NULL);
+                cnt++;
+            }
+        }
+        else {
+            /* We must have our two headers */
+            return JK_ERR;
+        }
+    }
+    else {
+        return JK_ERR;
+    }
+
+    return JK_OK;
+}
+
+static void JK_METHOD jk2_service_iis_afterRequest(jk_env_t *env,
+                                                   jk_ws_service_t *s)
+{
+
+    if (s->content_read < s->content_length ||
+        (s->is_chunked && !s->no_more_chunks)) {
+
+        LPEXTENSION_CONTROL_BLOCK lpEcb =
+            (LPEXTENSION_CONTROL_BLOCK) s->ws_private;
+
+#if 0
+        char *buff = s->pool->calloc(env, s->pool, 2048);
+        if (buff != NULL) {
+            /* int rd; */
+            /* FIXME Is there a IIS equivalent ? */
+            /*             while ((rd = ap_get_client_block(r, buff, 2048)) > 0) { */
+            /*                 s->content_read += rd; */
+            /*             } */
+        }
+#endif
+    }
+}
+
+
+int jk2_service_iis_init(jk_env_t *env, jk_ws_service_t *s)
+{
+    if (s == NULL) {
+        return JK_ERR;
+    }
+    jk2_requtil_initRequest(env, s);
+    s->head = jk2_service_iis_head;
+    s->read = jk2_service_iis_read;
+    s->write = jk2_service_iis_write;
+    s->init = jk2_service_iis_initService;
+    s->afterRequest = jk2_service_iis_afterRequest;
+
+    return JK_OK;
+}
diff --git a/connectors/jk/support/jk_apache_static.m4 b/connectors/jk/support/jk_apache_static.m4
new file mode 100644
index 0000000..5d375ea
--- /dev/null
+++ b/connectors/jk/support/jk_apache_static.m4
@@ -0,0 +1,132 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl Author Henri Gomez <hgomez@apache.org>
+dnl
+dnl Inspired by Pier works on webapp m4 macros :)
+dnl 
+dnl Version $Id$
+dnl --------------------------------------------------------------------------
+
+dnl Apache-2.0 needs the os subdirectory to include os.h
+dnl this include is copy from os/config.m4
+sinclude(../../common/build/os_apache.m4)
+
+dnl --------------------------------------------------------------------------
+dnl JK_APACHE_STATIC
+dnl   Set the APACHE 1.3/2.0 source dir.
+dnl   $1 => apache source dir to detect ("", 2)
+dnl   $2 => apache 1.3 build dir 
+dnl   $3 => apache 2.0 build dir
+dnl
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [JK_APACHE_STATIC],
+  [
+    tempval=""
+
+    AC_ARG_WITH(
+      [apache$1],
+      [  --with-apache$1=DIR  Location of Apache$2 source dir],
+      [
+        if ${TEST} ${use_apxs$1} ; then
+          AC_MSG_ERROR([Sorry cannot use --with-apxs= and --with-apache= together, please choose one])
+        fi
+
+        AC_MSG_CHECKING([for Apache source directory (assume static build)])
+        
+        if ${TEST} -n "${withval}" && ${TEST} -d "${withval}" ; then
+
+          if ${TEST} -d "${withval}/src" ; then
+            # handle the case where people use relative paths to 
+            # the apache source directory by pre-pending the current
+            # build directory to the path. there are probably 
+            # errors with this if configure is run while in a 
+            # different directory than what you are in at the time
+            if ${TEST} -n "`${ECHO} ${withval}|${GREP} \"^\.\.\"`" ; then
+              withval=`pwd`/${withval}
+            fi
+
+            APACHE$1_DIR=${withval}
+            use_static="true"
+            AC_MSG_RESULT(${APACHE$1_DIR})
+        
+            AC_MSG_CHECKING(for Apache include directory)
+
+            if ${TEST} -d "${withval}/src/include" ; then
+              # read osdir from the existing apache.
+              osdir=`${GREP} '^OSDIR=' ${withval}/src/Makefile.config | ${SED} -e 's:^OSDIR=.*/os:os:'`
+
+              if ${TEST} -z "${osdir}" ; then
+                osdir=os/unix
+              fi
+
+              APACHE$1_DIR=${withval}
+              APACHE$1_HOME=${withval}
+              APACHE$1_INCL="-I${withval}/src/include -I${withval}/src/${osdir}"
+              EXTRA_CFLAGS=""
+              EXTRA_CPPFLAGS=""
+              REPORTED_SERVER="apache-1.3"
+              SERVER_DIR="$3"
+              use_static="true"
+              use_apache13="true"
+              AC_MSG_RESULT([${APACHE$1_INCL}, version 1.3])
+            else
+              AC_MSG_ERROR([Sorry Apache 1.2.x is no longer supported.])
+            fi
+
+          else
+
+            if ${TEST} -d "${withval}/include" ; then
+              # osdir for Apache20.
+              APACHE$1_DIR=${withval}
+              APACHE$1_HOME=${withval}
+              APACHE$1_INCL="-I${withval}/include -I${withval}/srclib/apr/include -I${withval}/os/${OS_APACHE_DIR} -I${withval}/srclib/apr-util/include"
+              EXTRA_CFLAGS=""
+              EXTRA_CPPFLAGS=""
+              REPORTED_SERVER="apache-2.0"
+              SERVER_DIR="$3"
+              use_static="true"
+              use_apache2="true"
+              APACHE$1_INCL="-I${withval}/include -I${withval}/srclib/apr/include -I${withval}/os/${OS_APACHE_DIR} -I${withval}/srclib/apr-util/include"
+              AC_MSG_RESULT(${APACHE$1_DIR})
+
+              
+              JK_CHANNEL_APR_SOCKET="\${JK}jk_channel_apr_socket\${OEXT}"
+              JK_POOL_APR="\${JK}jk_pool_apr\${OEXT}"
+              HAS_APR="-DHAS_APR"
+           fi
+        fi
+    fi
+
+    dnl Make sure we have a result.
+    if ${TEST} -z "$WEBSERVER" ; then
+        AC_MSG_ERROR([Directory $apache_dir is not a valid Apache source distribution])
+    fi
+
+# VT: Now, which one I'm supposed to use? Let's figure it out later
+
+    configure_apache=true
+    configure_src=true
+    
+    AC_MSG_RESULT([building connector for \"$WEBSERVER\"])
+],
+[
+	AC_MSG_RESULT(no apache$1 dir given)
+])
+
+dnl vi:set sts=2 sw=2 autoindent:
diff --git a/connectors/jk/support/jk_apr.m4 b/connectors/jk/support/jk_apr.m4
new file mode 100644
index 0000000..2ccb1b7
--- /dev/null
+++ b/connectors/jk/support/jk_apr.m4
@@ -0,0 +1,319 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl Author Henri Gomez <hgomez@apache.org>
+dnl
+dnl Inspired by Pier works on webapp m4 macros :)
+dnl 
+dnl Version $Id$
+dnl --------------------------------------------------------------------------
+
+dnl --------------------------------------------------------------------------
+dnl JK_APR_THREADS
+dnl   Configure APR threading for use with --with-apr.
+dnl   Result goes into APR_CONFIGURE_ARGS
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [JK_APR_THREADS],
+  [
+    AC_ARG_ENABLE(
+      [apr-threads],
+      [  --enable-apr-threads        Configure APR threading for use with --with-apr ],
+      [
+        case "${enableval}" in
+          ""|"yes"|"YES"|"true"|"TRUE")
+            APR_CONFIGURE_ARGS="--enable-threads ${APR_CONFIGURE_ARGS}"
+          ;;
+          "no"|"NO"|"false"|"FALSE")
+            APR_CONFIGURE_ARGS="--disable-threads ${APR_CONFIGURE_ARGS}"
+          ;;
+        *)
+          APR_CONFIGURE_ARGS="--enable-threads=${enableval} ${APR_CONFIGURE_ARGS}"
+         esac
+      ])
+  ])
+
+dnl --------------------------------------------------------------------------
+dnl JK_APR
+dnl   Set the APR source dir.
+dnl   $1 => File which should be present
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [JK_APR],
+  [
+    tempval=""
+    AC_ARG_WITH(
+      [apr],
+      [  --with-apr=DIR           Location of APR source dir ],
+      [
+        case "${withval}" in
+          ""|"yes"|"YES"|"true"|"TRUE")
+            AC_MSG_ERROR(valid apr source dir location required)
+          ;;
+          "no"|"NO"|"false"|"FALSE")
+            AC_MSG_ERROR(valid apr source dir location required)
+          ;;
+        *)
+          tempval="${withval}"
+
+          if ${TEST} ! -d ${tempval} ; then
+            AC_MSG_ERROR(Not a directory: ${tempval})
+          fi
+
+          if ${TEST} ! -f ${tempval}/$1; then
+            AC_MSG_ERROR(can't locate ${tempval}/$1)
+          fi
+
+          if ${TEST} ! -z "$tempval" ; then
+            APR_BUILD="apr-build"
+            APR_CFLAGS="-I ${tempval}/include"
+            APR_CLEAN="apr-clean"
+            APR_DIR=${tempval}
+            APR_INCDIR="${tempval}/include"
+            AC_MSG_RESULT(configuring apr...)
+            tempret="0"
+            JK_EXEC(
+              [tempret],
+              [${SHELL} ./configure --prefix=${APR_DIR} --with-installbuilddir=${APR_DIR}/instbuild --disable-shared ${APR_CONFIGURE_ARGS}],
+              [apr],
+              [${APR_DIR}])
+            if ${TEST} "${tempret}" = "0"; then
+              AC_MSG_RESULT(apr configure ok)
+            else
+              AC_MSG_ERROR(apr configure failed with ${tempret})
+            fi
+            JK_APR_LIBNAME(apr_libname,${APR_DIR})
+            APR_LDFLAGS="${APR_DIR}/lib/${apr_libname}"
+            APR_LIBDIR=""
+			use_apr=true
+            COMMON_APR_OBJECTS="\${COMMON_APR_OBJECTS}"
+          fi
+          ;;
+        esac
+      ])
+
+      unset tempret
+      unset tempval
+      unset apr_libname
+  ])
+
+dnl --------------------------------------------------------------------------
+dnl JK_APR_UTIL
+dnl   Set the APR-UTIL source dir.
+dnl   $1 => File which should be present
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [JK_APR_UTIL],
+  [
+    tempval=""
+    AC_ARG_WITH(
+      [apr-util],
+      [  --with-apr-util=DIR      Location of APR-UTIL source dir ],
+      [
+        case "${withval}" in
+          ""|"yes"|"YES"|"true"|"TRUE")
+            AC_MSG_ERROR(valid apr-util source dir location required)
+          ;;
+          "no"|"NO"|"false"|"FALSE")
+            AC_MSG_ERROR(valid apr-util source dir location required)
+          ;;
+        *)
+          tempval="${withval}"
+
+          if ${TEST} ! -d ${tempval} ; then
+            AC_MSG_ERROR(Not a directory: ${tempval})
+          fi
+
+          if ${TEST} ! -f ${tempval}/$1; then
+            AC_MSG_ERROR(can't locate ${tempval}/$1)
+          fi
+
+          if ${TEST} -z "${APR_BUILD}"; then
+            AC_MSG_ERROR([--with-apr and --with-apr-util must be used together])
+          fi
+
+          if ${TEST} ! -z "$tempval" ; then
+            APR_UTIL_DIR=${tempval}
+            APR_CFLAGS="${APR_CFLAGS} -I ${APR_UTIL_DIR}/include"
+            APR_UTIL_INCDIR="${APR_UTIL_DIR}/include"
+            AC_MSG_RESULT(configuring apr-util...)
+            tempret="0"
+            JK_EXEC(
+              [tempret],
+              [${SHELL} ./configure --prefix=${APR_UTIL_DIR} --with-apr=${APR_DIR}],
+              [apr-util],
+              [${APR_UTIL_DIR}])
+            if ${TEST} "${tempret}" = "0"; then
+              AC_MSG_RESULT(apr-util configure ok)
+            else
+              AC_MSG_ERROR(apr-util configure failed with ${tempret})
+            fi
+            JK_APR_UTIL_LIBNAME(apr_util_libname,${APR_UTIL_DIR})
+            APR_LDFLAGS="${APR_LDFLAGS} ${APR_UTIL_DIR}/lib/${apr_util_libname}"
+            APR_UTIL_LIBDIR=""
+			use_apr=true
+            COMMON_APR_OBJECTS="\${COMMON_APR_OBJECTS}"
+          fi
+          ;;
+        esac
+      ])
+
+      unset tempret
+      unset tempval
+      unset apr_util_libname
+  ])
+
+
+dnl --------------------------------------------------------------------------
+dnl JK_APR_INCDIR
+dnl   Set the APR include dir.
+dnl   $1 => File which should be present
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [JK_APR_INCDIR],
+  [
+    tempval=""
+    AC_ARG_WITH(
+      [apr-include],
+      [  --with-apr-include=DIR   Location of APR include dir ],
+      [  
+        case "${withval}" in
+          ""|"yes"|"YES"|"true"|"TRUE")
+          ;;
+          "no"|"NO"|"false"|"FALSE")
+            AC_MSG_ERROR(valid apr include dir location required)
+          ;;
+        *)
+          tempval="${withval}"
+          if ${TEST} ! -d ${tempval} ; then
+            AC_MSG_ERROR(Not a directory: ${tempval})
+          fi
+
+          if ${TEST} ! -f ${tempval}/$1; then
+            AC_MSG_ERROR(can't locate ${tempval}/$1)
+          fi
+
+          if ${TEST} ! -z "$tempval" ; then
+            APR_BUILD=""
+            APR_CFLAGS="-I${tempval}"
+            APR_CLEAN=""
+            APR_DIR=""
+            APR_INCDIR=${tempval}
+            COMMON_APR_OBJECTS="\${COMMON_APR_OBJECTS}"
+			use_apr=true
+          fi
+          ;;
+
+        esac
+      ])
+
+      unset tempval
+  ])
+
+
+dnl --------------------------------------------------------------------------
+dnl JK_APR_LIBDIR
+dnl   Set the APR library dir.
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [JK_APR_LIBDIR],
+  [
+    tempval=""
+    AC_ARG_WITH(
+      [apr-lib],
+      [  --with-apr-lib=DIR       Location of APR lib dir ],
+      [
+        case "${withval}" in
+          ""|"yes"|"YES"|"true"|"TRUE")
+          ;;
+          "no"|"NO"|"false"|"FALSE")
+            AC_MSG_ERROR(valid apr lib dir location required)
+          ;;
+          *)
+            tempval="${withval}"
+
+            if ${TEST} ! -d ${tempval} ; then
+              AC_MSG_ERROR(Not a directory: ${tempval})
+            fi
+
+            if ${TEST} ! -z "$tempval" ; then
+              APR_BUILD=""
+              APR_CLEAN=""
+              APR_DIR=""
+              APR_LIBDIR=${tempval}
+              APR_LDFLAGS="`apr-config --link-ld` -L${tempval}"
+              COMMON_APR_OBJECTS="\${COMMON_APR_OBJECTS}"
+			  use_apr=true
+            fi
+
+            ;;
+            esac
+      ])
+
+      unset tempval
+  ])
+
+
+dnl --------------------------------------------------------------------------
+dnl JK_APR_LIBNAME
+dnl   Retrieve the complete name of the library.
+dnl   $1 => Environment variable name for the returned value
+dnl   $2 => APR sources directory
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [JK_APR_LIBNAME],
+  [
+    AC_MSG_CHECKING([for apr APR_LIBNAME])
+    if ${TEST} ! -f "$2/apr-config" ; then
+      AC_MSG_ERROR([cannot find apr-config file in $2])
+    fi
+    jk_apr_get_tempval=`$2/apr-config --link-libtool 2> /dev/null`
+    if ${TEST} -z "${jk_apr_get_tempval}" ; then
+      AC_MSG_ERROR([$2/apr-config --link-libtool failed])
+    fi
+    jk_apr_get_tempval=`basename ${jk_apr_get_tempval}`
+    $1="${jk_apr_get_tempval}"
+    AC_MSG_RESULT([${jk_apr_get_tempval}])
+    unset jk_apr_get_tempval
+  ])
+
+
+dnl --------------------------------------------------------------------------
+dnl JK_APR_UTIL_LIBNAME
+dnl   Retrieve the complete name of the library.
+dnl   $1 => Environment variable name for the returned value
+dnl   $2 => APR_UTIL sources directory
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [JK_APR_UTIL_LIBNAME],
+  [
+    AC_MSG_CHECKING([for apr-util APR_UTIL_LIBNAME])
+    if ${TEST} ! -f "$2/apu-config" ; then
+      AC_MSG_ERROR([cannot find apu-config file in $2])
+    fi
+    jk_apu_get_tempval=`$2/apu-config --link-libtool 2> /dev/null`
+    if ${TEST} -z "${jk_apu_get_tempval}" ; then
+      AC_MSG_ERROR([$2/apu-config --link-libtool failed])
+    fi
+    jk_apu_get_tempval=`basename ${jk_apu_get_tempval}`
+    $1="${jk_apu_get_tempval}"
+    AC_MSG_RESULT([${jk_apu_get_tempval}])
+    unset jk_apu_get_tempval
+  ])
+
+dnl vi:set sts=2 sw=2 autoindent:
+
diff --git a/connectors/jk/support/jk_apxs.m4 b/connectors/jk/support/jk_apxs.m4
new file mode 100644
index 0000000..21441dc
--- /dev/null
+++ b/connectors/jk/support/jk_apxs.m4
@@ -0,0 +1,143 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl Author Henri Gomez <hgomez@apache.org>
+dnl
+dnl Inspired by Pier works on webapp m4 macros :)
+dnl 
+dnl Version $Id$
+dnl --------------------------------------------------------------------------
+
+dnl --------------------------------------------------------------------------
+dnl JK_APXS
+dnl
+dnl Get APXS to be used, determine if Apache 1.3 or 2.0 are target
+dnl $1 => blank/2 if you want to detect Apache 1.3 & 2.0 
+dnl $2 => comment for --with-apxs
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [JK_APXS],
+  [
+    tempval=""
+    AC_ARG_WITH(apxs$1,
+    [  --with-apxs$1[=FILE]      $2],
+    [
+      case "${withval}" in 
+        y | yes | true) find_apxs=true ;;
+        n | no | false) find_apxs=false ;;
+        *) find_apxs=false ;;
+      esac
+
+      if ${TEST} ${find_apxs} ; then    
+        AC_MSG_RESULT([need to check for Perl first, apxs depends on it...])
+        AC_PATH_PROG(PERL,perl,$PATH)dnl
+    
+        if ${TEST} ${find_apxs} ; then
+            APXS$1=${withval}
+        else
+            AC_PATH_PROG(APXS$1,apxs$1,$PATH)dnl
+        fi
+    
+		use_apxs$1=true;
+		
+        if ${TEST} -n "${APXS$1}" ; then
+            dnl Seems that we have it, but have to check if it is OK first        
+            if ${TEST} ! -x "${APXS$1}" ; then
+                AC_MSG_ERROR(Invalid location for apxs: '${APXS$1}')
+            fi
+            
+            ${APXS$1} -q PREFIX >/dev/null 2>/dev/null || apxs_support=false
+    
+            if ${TEST} "${apxs_support}" = "false" ; then
+                AC_MSG_RESULT(could not find ${APXS$1})
+                AC_MSG_ERROR(You must specify a valid --with-apxs$1 path)
+            fi
+
+            dnl apache_dir and apache_include are also needed.
+            APACHE$1_HOME=`${APXS$1} -q PREFIX`
+            APACHE$1_INCL="-I`${APXS$1} -q INCLUDEDIR`"
+            APACHE$1_INCDIR="`${APXS$1} -q INCLUDEDIR`"
+            APACHE$1_LIBEXEC="`${APXS$1} -q LIBEXECDIR`"
+            APACHE$1_CC="`${APXS$1} -q CC`"
+
+            dnl test apache version
+            APA=`${GREP} STANDARD20 ${APXS$1}`
+
+            dnl check if we have an apxs for Apache 1.3 or 2.0
+            if ${TEST} -z "$APA" ; then
+	      if ${TEST} ! -z "$1" ; then
+                AC_MSG_ERROR(Do not use --with-apxs$1 but --with-apxs)
+	      fi
+              WEBSERVERS="${WEBSERVERS} server/apache13"
+              RWEBSERVER="apache-1.3"
+              APXS$1_CFLAGS="`${APXS$1} -q CFLAGS`"
+              APXS$1_CPPFLAGS=""
+            else
+	      if ${TEST} -z "$1" ; then
+                AC_MSG_ERROR(Do not use --with-apxs but --with-apxs2)
+	      fi
+              WEBSERVERS="${WEBSERVERS} server/apache2"
+              RWEBSERVER="apache-2.0"
+              APACHE2_CONFIG_VARS=${apache_dir}/build/config_vars.mk
+              JK_CHANNEL_APR_SOCKET="\${JK}jk_channel_apr_socket\${OEXT}"
+              JK_POOL_APR="\${JK}jk_pool_apr\${OEXT}"
+              APXS$1_CFLAGS="`${APXS$1} -q CFLAGS` `${APXS$1} -q EXTRA_CFLAGS`"
+              APXS$1_CPPFLAGS="`${APXS$1} -q EXTRA_CPPFLAGS`"
+              APR_INCDIR="-I`${APXS$1} -q APR_INCLUDEDIR`"
+			  APR_UTIL_INCDIR="-I`${APXS$1} -q APU_INCLUDEDIR`"
+              APACHE2_LIBDIR="`${APXS$1} -q LIBDIR`"
+              LIBTOOL=`${APXS$1} -q LIBTOOL`
+              if ${TEST} -f ${APACHE2_LIBDIR}/libapr-1.so -o -f ${APACHE2_LIBDIR}/libapr-1.dylib; then
+                APR_LIBS="-L${APACHE2_LIBDIR} -lapr-1"
+              elif ${TEST} -f ${APACHE2_LIBDIR}/libapr-0.so -o -f ${APACHE2_LIBDIR}/libapr-0.dylib; then
+                APR_LIBS="-L${APACHE2_LIBDIR} -lapr-0"
+              elif ${TEST} -f ${APACHE2_LIBDIR}/libapr.so -o -f ${APACHE2_LIBDIR}/libapr.dylib; then
+                APR_LIBS="-L${APACHE2_LIBDIR} -lapr"
+              else
+                AC_MSG_ERROR(can't locate libapr)
+              fi
+            fi
+            
+            AC_MSG_RESULT([building connector for \"$RWEBSERVER\"])
+        fi
+
+      fi
+  ],
+  [
+	  AC_MSG_RESULT(no apxs$1 given)
+  ])
+
+  unset tempval
+
+  AC_SUBST(APXS$1)
+  AC_SUBST(APXS$1_CFLAGS)
+  AC_SUBST(APACHE$1_CONFIG_VARS)
+  AC_SUBST(APXS$1_CPPFLAGS)
+  AC_SUBST(APACHE$1_DIR)
+  AC_SUBST(APACHE$1_HOME)
+  AC_SUBST(APACHE$1_INCDIR)
+  AC_SUBST(APACHE$1_INCL)
+  AC_SUBST(APACHE$1_LIBEXEC)
+  AC_SUBST(APACHE$1_LIBDIR)
+  AC_SUBST(APACHE$1_CC)
+  AC_SUBST(APXS$1_LDFLAGS)
+  AC_SUBST(APR_LIBS)
+
+])
+
+dnl vi:set sts=2 sw=2 autoindent:
+
diff --git a/connectors/jk/support/jk_dominohome.m4 b/connectors/jk/support/jk_dominohome.m4
new file mode 100644
index 0000000..5c8f8b6
--- /dev/null
+++ b/connectors/jk/support/jk_dominohome.m4
@@ -0,0 +1,73 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl Author Andy Armstrong <andy@tagish.com>
+dnl Shamelessly cribbed from  Henri Gomez <hgomez@apache.org>
+dnl
+dnl He was inspired by Pier works on webapp m4 macros :)
+dnl 
+dnl Version $Id$
+dnl --------------------------------------------------------------------------
+
+dnl --------------------------------------------------------------------------
+dnl JK_DOMHOME
+dnl   Set the Domino Home directory.
+dnl   $1 => Domino Name
+dnl   $2 => Domino VarName
+dnl   $3 => File which should be present
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [JK_DOMHOME],
+  [
+    tempval=""
+
+    AC_MSG_CHECKING([for $1 location])
+    AC_ARG_WITH(
+      [$1],
+      [  --with-$1=DIR      Location of $1 ],
+      [ 
+        case "${withval}" in
+        ""|"yes"|"YES"|"true"|"TRUE")
+          ;;
+        "no"|"NO"|"false"|"FALSE")
+          AC_MSG_ERROR(valid $1 location required)
+          ;;
+        *)
+          tempval="${withval}"
+
+          if ${TEST} ! -d ${tempval} ; then
+            AC_MSG_ERROR(Not a directory: ${tempval})
+          fi
+
+          if ${TEST} ! -f ${tempval}/$3; then
+            AC_MSG_ERROR(can't locate ${tempval}/$3)
+          fi
+          ;;
+        esac
+      ])  
+
+      if ${TEST} -z "$tempval" ; then
+        AC_MSG_RESULT(not provided)
+      else
+        [$2]=${tempval}
+        AC_MSG_RESULT(${[$2]})
+      fi
+
+      unset tempval
+  ])
+
+dnl vi:set sts=2 sw=2 autoindent:
diff --git a/connectors/jk/support/jk_exec.m4 b/connectors/jk/support/jk_exec.m4
new file mode 100644
index 0000000..627fbb8
--- /dev/null
+++ b/connectors/jk/support/jk_exec.m4
@@ -0,0 +1,90 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl
+dnl Inspired by Pier works on webapp m4 macros :)
+dnl
+dnl Version $Id:$
+dnl --------------------------------------------------------------------------
+
+dnl --------------------------------------------------------------------------
+dnl JK_EXEC
+dnl   Execute a program filtering its output (pretty printing).
+dnl
+dnl   Parameters:
+dnl     $1 => name of the variable containing the return value (error code).
+dnl     $2 => name of the binary/script to invoke
+dnl     $3 => message used for pretty printing output
+dnl     $4 => the directory where the command must be executed
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [JK_EXEC],
+  [
+    jk_exec_curdir="`pwd`"
+    if test -d "$4" ; then
+      cd "$4"
+    else
+      AC_MSG_ERROR([can't switch to directory $4])
+    fi
+
+    echo "  invoking \"$2\""
+    echo "  in directory \"$4\""
+    echo "-1" > retvalue.tmp
+
+    set $2
+    jk_exec_file=[$]1
+    if test ! -x "${jk_exec_file}" ; then
+      cd "${jk_exec_curdir}"
+      AC_MSG_ERROR([cannot find or execute \"${jk_exec_file}\" in \"$4\"])
+      exit 1
+    fi
+    unset jk_exec_file
+
+    {
+      $2
+      echo
+      echo "jk_exec_retvalue $?"
+    } | {
+      jk_exec_ret=0
+      while true ; do
+        read jk_exec_first jk_exec_line
+        if test ! "$?" -eq "0" ; then
+          break
+        else
+          if test "${jk_exec_first}" = "jk_exec_retvalue" ; then
+            jk_exec_ret="${jk_exec_line}"
+          else
+            if test -n "${jk_exec_line}" ; then
+             echo "    $3: ${jk_exec_first} ${jk_exec_line}"
+            fi
+          fi
+        fi
+      done
+      echo "${jk_exec_ret}" > retvalue.tmp
+      unset jk_exec_first
+      unset jk_exec_line
+      unset jk_exec_ret
+    }
+
+    $1="`cat retvalue.tmp`"
+    rm -f retvalue.tmp
+    echo "  execution of \"$2\""
+    echo "  returned with value \"${$1}\""
+
+    cd "${jk_exec_curdir}"
+    unset jk_exec_curdir
+  ])
diff --git a/connectors/jk/support/jk_java.m4 b/connectors/jk/support/jk_java.m4
new file mode 100644
index 0000000..015b139
--- /dev/null
+++ b/connectors/jk/support/jk_java.m4
@@ -0,0 +1,223 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl Author Henri Gomez <hgomez@apache.org>
+dnl
+dnl Inspired by Pier works on webapp m4 macros :)
+dnl 
+dnl Version $Id$
+dnl --------------------------------------------------------------------------
+
+dnl --------------------------------------------------------------------------
+dnl JK_JDK
+dnl
+dnl Detection of JDK location and Java Platform (1.1, 1.2, 1.3, 1.4)
+dnl result goes in JAVA_HOME / JAVA_PLATFORM (1 -> 1.1, 2 -> 1.2 and higher)
+dnl 
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [JK_JNI],
+  [
+    AC_ARG_WITH(jni,
+      [  --with-jni               Build jni support],
+      [
+		case "${withval}" in
+		  y | yes | true) use_jni=true ;;
+		  n | no | false) use_jni=false ;;
+	    *) use_jni=true ;;
+	      esac
+
+		if ${TEST} "${use_jni}" = "true"; then
+		  HAVE_JNI="-DHAVE_JNI"
+		  JNI_BUILD="jni-build"
+		fi
+      ])
+  ])
+
+AC_DEFUN(
+  [JK_JDK],
+  [
+    if ${TEST} "${use_jni}" = "true"; then
+      tempval=""
+      AC_MSG_CHECKING([for JDK location (please wait)])
+      if ${TEST} -n "${JAVA_HOME}" ; then
+        JAVA_HOME_ENV="${JAVA_HOME}"
+      else
+        JAVA_HOME_ENV=""
+      fi
+
+      JAVA_HOME=""
+      JAVA_PLATFORM=""
+
+      AC_ARG_WITH(
+        [java-home],
+        [  --with-java-home=DIR     Location of JDK directory.],
+        [
+
+        # This stuff works if the command line parameter --with-java-home was
+        # specified, so it takes priority rightfully.
+  
+        tempval=${withval}
+
+        if ${TEST} ! -d "${tempval}" ; then
+            AC_MSG_ERROR(Not a directory: ${tempval})
+        fi
+  
+        JAVA_HOME=${tempval}
+        AC_MSG_RESULT(${JAVA_HOME})
+      ],
+      [
+        # This works if the parameter was NOT specified, so it's a good time
+        # to see what the enviroment says.
+        # Since Sun uses JAVA_HOME a lot, we check it first and ignore the
+        # JAVA_HOME, otherwise just use whatever JAVA_HOME was specified.
+
+        if ${TEST} -n "${JAVA_HOME_ENV}" ; then
+          JAVA_HOME=${JAVA_HOME_ENV}
+          AC_MSG_RESULT(${JAVA_HOME_ENV} from environment)
+        fi
+      ])
+
+      if ${TEST} -z "${JAVA_HOME}" ; then
+
+        # Oh well, nobody set neither JAVA_HOME nor JAVA_HOME, have to guess
+        # The following code is based on the code submitted by Henner Zeller
+        # for ${srcdir}/src/scripts/package/rpm/ApacheJServ.spec
+        # Two variables will be set as a result:
+        #
+        # JAVA_HOME
+        # JAVA_PLATFORM
+        AC_MSG_CHECKING([Try to guess JDK location])
+
+        for JAVA_PREFIX in /usr/local /usr/local/lib /usr /usr/lib /opt /usr/java ; do
+
+          for JAVA_PLATFORM in 4 3 2 1 ; do
+
+            for subversion in .9 .8 .7 .6 .5 .4 .3 .2 .1 "" ; do
+
+              for VARIANT in IBMJava2- java java- jdk jdk-; do
+                GUESS="${JAVA_PREFIX}/${VARIANT}1.${JAVA_PLATFORM}${subversion}"
+dnl             AC_MSG_CHECKING([${GUESS}])
+                if ${TEST} -d "${GUESS}/bin" & ${TEST} -d "${GUESS}/include" ; then
+                  JAVA_HOME="${GUESS}"
+                  AC_MSG_RESULT([${GUESS}])
+                  break
+                fi
+              done
+
+              if ${TEST} -n "${JAVA_HOME}" ; then
+                break;
+              fi
+
+            done
+
+            if ${TEST} -n "${JAVA_HOME}" ; then
+              break;
+            fi
+
+          done
+
+          if ${TEST} -n "${JAVA_HOME}" ; then
+            break;
+          fi
+
+        done
+
+        if ${TEST} ! -n "${JAVA_HOME}" ; then
+          AC_MSG_ERROR(can't locate a valid JDK location)
+        fi
+
+      fi
+
+      if ${TEST} -n "${JAVA_PLATFORM}"; then
+        AC_MSG_RESULT(Java Platform detected - 1.${JAVA_PLATFORM})
+      else
+        AC_MSG_CHECKING(Java platform)
+      fi
+
+      AC_ARG_WITH(java-platform,
+       [  --with-java-platform[=2] Force the Java platorm
+                                   (value is 1 for 1.1.x or 2 for 1.2.x or greater)],
+       [
+          case "${withval}" in
+            "1"|"2")
+              JAVA_PLATFORM=${withval}
+              ;;
+            *)
+              AC_MSG_ERROR(invalid java platform provided)
+              ;;
+          esac
+       ],
+       [
+          if ${TEST} -n "${JAVA_PLATFORM}"; then
+            AC_MSG_RESULT(Java Platform detected - 1.${JAVA_PLATFORM})
+          else
+            AC_MSG_CHECKING(Java platform)
+          fi
+       ])
+
+       AC_MSG_RESULT(${JAVA_PLATFORM})
+
+      unset tempval
+    else
+      # no jni, then make sure JAVA_HOME is not picked up from env
+      JAVA_HOME=""
+      JAVA_PLATFORM=""
+    fi
+  ])
+
+
+AC_DEFUN(
+  [JK_JDK_OS],
+  [
+    if ${TEST} "${use_jni}" = "true"; then
+      tempval=""
+      OS=""
+      AC_ARG_WITH(os-type,
+        [  --with-os-type[=SUBDIR]  Location of JDK os-type subdirectory.],
+        [
+          tempval=${withval}
+
+          if ${TEST} ! -d "${JAVA_HOME}/${tempval}" ; then
+            AC_MSG_ERROR(Not a directory: ${JAVA_HOME}/${tempval})
+          fi
+
+          OS = ${tempval}
+        ],
+        [   
+          AC_MSG_CHECKING(os_type directory)
+          if ${TEST} -f ${JAVA_HOME}/include/jni_md.h; then
+            OS=""
+          else
+            for f in ${JAVA_HOME}/include/*/jni_md.h; do
+              if ${TEST} -f $f; then
+                OS=`dirname ${f}`
+                OS=`basename ${OS}`
+                echo " ${OS}"
+              fi
+            done
+            if ${TEST} -z "${OS}"; then
+              AC_MSG_RESULT(Cannot find jni_md.h in ${JAVA_HOME}/${OS})
+              AC_MSG_ERROR(You should retry --with-os-type=SUBDIR)
+            fi
+          fi
+        ])
+    fi
+  ])
+
+dnl vi:set sts=2 sw=2 autoindent:
+
diff --git a/connectors/jk/support/jk_pcre.m4 b/connectors/jk/support/jk_pcre.m4
new file mode 100644
index 0000000..122bc6e
--- /dev/null
+++ b/connectors/jk/support/jk_pcre.m4
@@ -0,0 +1,39 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+
+
+AC_DEFUN(
+  [JK_PCRE],
+  [
+    AC_ARG_WITH(pcre,
+      [  --with-pcre              Build pcre support],
+      [
+		case "${withval}" in
+		  y | yes | true) use_pcre=true ;;
+		  n | no | false) use_pcre=false ;;
+	    *) use_pcre=true ;;
+	      esac
+
+		if ${TEST} ${use_pcre} ; then
+		  HAS_PCRE="-I${includedir} -DHAS_PCRE"
+		  PCRE_LIBS="-L${libdir} -lpcre -lpcreposix"
+		fi
+      ])
+  ])
+
+dnl vi:set sts=2 sw=2 autoindent:
+
diff --git a/connectors/jk/support/jk_tchome.m4 b/connectors/jk/support/jk_tchome.m4
new file mode 100644
index 0000000..c9b38dc
--- /dev/null
+++ b/connectors/jk/support/jk_tchome.m4
@@ -0,0 +1,72 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl Author Henri Gomez <hgomez@apache.org>
+dnl
+dnl Inspired by Pier works on webapp m4 macros :)
+dnl 
+dnl Version $Id$
+dnl --------------------------------------------------------------------------
+
+dnl --------------------------------------------------------------------------
+dnl JK_TCHOME
+dnl   Set the Tomcat Home directory.
+dnl   $1 => Tomcat Name
+dnl   $2 => Tomcat VarName
+dnl   $3 => File which should be present
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [JK_TCHOME],
+  [
+    tempval=""
+
+    AC_MSG_CHECKING([for $1 location])
+    AC_ARG_WITH(
+      [$1],
+      [  --with-$1=DIR      Location of $1 ],
+      [ 
+        case "${withval}" in
+        ""|"yes"|"YES"|"true"|"TRUE")
+          ;;
+        "no"|"NO"|"false"|"FALSE")
+          AC_MSG_ERROR(valid $1 location required)
+          ;;
+        *)
+          tempval="${withval}"
+
+          if ${TEST} ! -d ${tempval} ; then
+            AC_MSG_ERROR(Not a directory: ${tempval})
+          fi
+
+          if ${TEST} ! -f ${tempval}/$3; then
+            AC_MSG_ERROR(can't locate ${tempval}/$3)
+          fi
+          ;;
+        esac
+      ])  
+
+      if ${TEST} -z "$tempval" ; then
+        AC_MSG_RESULT(not provided)
+      else
+        [$2]=${tempval}
+        AC_MSG_RESULT(${[$2]})
+      fi
+
+      unset tempval
+  ])
+
+dnl vi:set sts=2 sw=2 autoindent:
diff --git a/connectors/jk/support/jk_ws.m4 b/connectors/jk/support/jk_ws.m4
new file mode 100644
index 0000000..52c5c7a
--- /dev/null
+++ b/connectors/jk/support/jk_ws.m4
@@ -0,0 +1,228 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl Author Henri Gomez <hgomez@apache.org>
+dnl
+dnl Inspired by Pier works on webapp m4 macros :)
+dnl 
+dnl Version $Id$
+dnl --------------------------------------------------------------------------
+
+dnl --------------------------------------------------------------------------
+dnl JK_WS_DIR
+dnl   Set the WebServer source dir.
+dnl   $1 => Webserver name
+dnl   $2 => Webserver vars prefix name
+dnl   $3 => File which should be present
+dnl   $4 => Server specific source directory 
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [JK_WS_DIR],
+  [
+    tempval=""
+    AC_ARG_WITH(
+      [$1],
+      [  --with-$1=DIR           Location of $1 source dir ],
+      [
+        case "${withval}" in
+          ""|"yes"|"YES"|"true"|"TRUE")
+          AC_MSG_ERROR(valid $1 source dir location required)
+          ;;
+          "no"|"NO"|"false"|"FALSE")
+          AC_MSG_ERROR(Don't use with/without $1 if you don't have $1)
+          ;;
+          *)
+          tempval="${withval}"
+
+          if ${TEST} ! -d ${tempval} ; then
+            AC_MSG_ERROR(Not a directory: ${tempval})
+          fi
+
+          if ${TEST} ! -f ${tempval}/$3; then
+            AC_MSG_ERROR(can't locate ${tempval}/$3)
+          fi
+
+          if ${TEST} ! -z "$tempval" ; then
+            $2_BUILD="true"
+            $2_CFLAGS="-I ${tempval}/include"
+            $2_DIR=${tempval}
+            $2_HOME="${tempval}"
+            $2_LIBDIR=""
+	    if ${TEST} -d ${withval}/include ; then
+	      $2_INCL="-I${tempval}/include"
+              $2_INCDIR="${tempval}/include"
+	    fi
+	    if ${TEST} -d ${withval}/src/include ; then
+	      # read osdir from the existing apache.
+	      osdir=`${GREP} '^OSDIR=' ${withval}/src/Makefile.config | ${SED} -e 's:^OSDIR=.*/os:os:'`
+	      if ${TEST} -z "${osdir}" ; then
+	        osdir=os/unix
+	      fi
+	      $2_INCL="-I${tempval}/src/include -I${withval}/src/${osdir}"
+              $2_INCDIR="${tempval}/src/include"
+	    fi
+	    if ${TEST} -d ${tempval}/srclib/apr ; then
+	      # Apache 2 contains apr.
+	      if ${TEST} ! -f ${tempval}/srclib/apr/config.status ; then
+                AC_MSG_ERROR(configure Apache2 before mod_jk2)
+	      fi
+	      osdir=`${GREP} @OSDIR@ ${tempval}/srclib/apr/config.status | sed 's:s,@OSDIR@,::' | sed 's:,;t t::'`
+	      $2_INCL="-I${tempval}/include -I${withval}/os/${osdir}"
+	      $2_LIBEXEC=`${GREP} "^exp_libexecdir =" ${tempval}/build/config_vars.mk | sed 's:exp_libexecdir = ::'`
+	      LIBTOOL=${tempval}/srclib/apr/libtool
+	      APR_INCDIR=-I${tempval}/srclib/apr/include
+	      APR_CFLAGS=`${tempval}/srclib/apr/apr-config --cflags`
+	      APR_UTIL_INCDIR=-I${tempval}/srclib/apr-util/include
+	      APR_LIBDIR_LA=`${tempval}/srclib/apr/apr-config --apr-la-file`
+              $2_LIBDIR=${tempval}/lib
+
+	      AC_SUBST(APR_INCDIR)
+	      AC_SUBST(APR_CFLAGS)
+	      AC_SUBST(APR_INCDIR)
+	      AC_SUBST(APR_UTIL_INCDIR)
+	    fi
+            $2_LDFLAGS=""
+	    WEBSERVERS="${WEBSERVERS} $4"
+
+	    AC_SUBST($2_BUILD)
+	    AC_SUBST($2_CFLAGS)
+	    AC_SUBST($2_DIR)
+	    AC_SUBST($2_HOME)
+	    AC_SUBST($2_INCL)
+	    AC_SUBST($2_INCDIR)
+	    AC_SUBST($2_LDFLAGS)
+	    AC_SUBST($2_LIBDIR)
+
+          fi
+          ;;
+        esac
+      ])
+
+      if ${TEST} -z "$tempval" ; then
+        AC_MSG_RESULT(not provided)
+      else	
+        AC_MSG_RESULT(${tempval})
+      fi
+
+      unset tempval
+  ])
+
+
+dnl --------------------------------------------------------------------------
+dnl JK_WS_INCDIR
+dnl   Set the WebServer include dir.
+dnl   $1 => Webserver name
+dnl   $2 => Webserver vars prefix name
+dnl   $3 => File which should be present
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [JK_WS_INCDIR],
+  [
+    tempval=""
+    AC_ARG_WITH(
+      [$1-include],
+      [  --with-$1-include=DIR   Location of $1 include dir ],
+      [  
+        case "${withval}" in
+          ""|"yes"|"YES"|"true"|"TRUE")
+          ;;
+          "no"|"NO"|"false"|"FALSE")
+          AC_MSG_ERROR(valid $1 include dir location required)
+          ;;
+          *)
+          tempval="${withval}"
+          if ${TEST} ! -d ${tempval} ; then
+            AC_MSG_ERROR(Not a directory: ${tempval})
+          fi
+
+          if ${TEST} ! -f ${tempval}/$3; then
+            AC_MSG_ERROR(can't locate ${tempval}/$3)
+          fi
+
+          if ${TEST} ! -z "$tempval" ; then
+            $2_BUILD=""
+            $2_CFLAGS="-I${tempval}"
+            $2_CLEAN=""
+            $2_DIR=""
+            $2_INCDIR=${tempval}
+            AC_MSG_RESULT($2_INCDIR)
+	    
+	    AC_SUBST($2_BUILD)
+	    AC_SUBST($2_CFLAGS)
+	    AC_SUBST($2_CLEAN)
+	    AC_SUBST($2_DIR)
+	    AC_SUBST($2_INCDIR)
+          fi
+          ;;
+        esac
+      ])
+
+      unset tempval
+  ])
+
+
+dnl --------------------------------------------------------------------------
+dnl JK_WS_LIBDIR
+dnl   Set the WebServer library dir.
+dnl   $1 => Webserver name
+dnl   $2 => Webserver vars prefix name
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [JK_WS_LIBDIR],
+  [
+    tempval=""
+    AC_ARG_WITH(
+      [$1-lib],
+      [  --with-$1-lib=DIR       Location of $1 lib dir ],
+      [
+        case "${withval}" in
+          ""|"yes"|"YES"|"true"|"TRUE")
+          ;;
+          "no"|"NO"|"false"|"FALSE")
+          AC_MSG_ERROR(valid $1 lib directory location required)
+          ;;
+        *)
+          tempval="${withval}"
+
+          if ${TEST} ! -d ${tempval} ; then
+            AC_MSG_ERROR(Not a directory: ${tempval})
+          fi
+
+          if ${TEST} ! -z "$tempval" ; then
+            $2_BUILD=""
+            $2_CLEAN=""
+            $2_DIR=""
+            $2_LIBDIR=${tempval}
+            $2_LDFLAGS=""
+            AC_MSG_RESULT($2_LIBDIR)
+
+	    AC_SUBST($2_BUILD)
+	    AC_SUBST($2_CLEAN)
+	    AC_SUBST($2_DIR)
+	    AC_SUBST($2_LIBDIR)
+	    AC_SUBST($2_LDFLAGS)
+          fi
+
+          ;;
+          esac
+      ])
+
+      unset tempval
+  ])
+
+dnl vi:set sts=2 sw=2 autoindent:
+
diff --git a/connectors/jk/test/org/apache/ajp/test/TestAjp13.java b/connectors/jk/test/org/apache/ajp/test/TestAjp13.java
new file mode 100644
index 0000000..d9b4af5
--- /dev/null
+++ b/connectors/jk/test/org/apache/ajp/test/TestAjp13.java
@@ -0,0 +1,276 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.apache.ajp.Ajp13;
+import org.apache.ajp.Ajp13Packet;
+import org.apache.ajp.RequestHandler;
+import org.apache.tomcat.util.http.BaseRequest;
+import org.apache.tomcat.util.http.MimeHeaders;
+
+public class TestAjp13 extends TestCase {
+
+    Ajp13Server server = null;
+
+    public TestAjp13(String name) {
+        super(name);
+    }
+
+    public static Test suite() {
+        return new TestSuite(TestAjp13.class);
+    }
+
+    protected void setUp() {
+        println("setup...");
+
+        server = new Ajp13Server();
+        server.start();
+    }
+
+    protected void tearDown() {
+        println("tear down...");
+
+        server.shutdown();
+    }
+
+    public void test1() throws Exception {
+        println("running test1");
+
+        Socket s = new Socket("localhost", 8009);
+
+        Ajp13Packet p = new Ajp13Packet(Ajp13.MAX_PACKET_SIZE);
+        p.appendInt(0x1234);
+        p.appendInt(0);
+        p.setByteOff(4);
+        p.appendByte(RequestHandler.JK_AJP13_FORWARD_REQUEST);
+        p.appendByte((byte)2);
+        p.appendString("http");
+        p.appendString("/test_uri");
+        p.appendString("remote_addr");
+        p.appendString("remote_host");
+        p.appendString("server_name");
+        p.appendInt(80);
+        p.appendBool(false);
+        p.appendInt(3);
+        p.appendString("my header");
+        p.appendString("my header value");
+        p.appendInt((0xA0 << 8) + RequestHandler.SC_REQ_AUTHORIZATION);
+        p.appendString("some auth string");
+        p.appendInt((0xA0 << 8) + RequestHandler.SC_REQ_USER_AGENT);
+        p.appendString("TestAjp13 User Agent");
+        p.appendByte(RequestHandler.SC_A_ARE_DONE);
+
+        int len = p.getByteOff() - 4;
+        p.setByteOff(2);
+        p.appendInt(len);
+
+        OutputStream os = s.getOutputStream();
+        os.write(p.getBuff(), 0, len + 4);
+
+        InputStream is = s.getInputStream();
+
+        println("decoding response...");
+        
+        boolean done = false;
+        while (!done) {
+            int b1, b2;
+            // read a packet
+
+            // first 2 bytes should be AB
+            b1 = is.read();
+            assertTrue("byte 1 was " + (char)b1, b1 == (int)'A');
+            b2 = is.read();
+            assertTrue("byte 2 was " + (char)b2, b2 == (int)'B');
+
+            println("b1 = " + (char)b1 + "; b2 = " + (char)b2);
+            
+            // next is length
+            b1 = is.read();
+            b1 &= 0xFF;
+            b2 = is.read();
+            b2 &= 0xFF;
+            
+            int l = (b1 << 8) + b2;
+
+            println("length = " + l);
+
+            // now get data
+            byte[] buf = new byte[l];
+            int n = 0;
+            int off = 0;
+            int total = 0;
+            while ((n = is.read(buf, off, l - off)) != -1 && (l - off != 0)) {
+                total += n;
+                off += n;
+            }
+
+            println("read " + total);
+
+            assertTrue("total read was " + total +
+                       ", should have been " + l,
+                       total == l);
+
+            
+
+            int code = (int)buf[0];
+
+            switch (code) {
+            case 3:
+                println("AJP13_SEND_BODY_CHUNK ");
+                break;
+            case 4:
+                println("AJP13_SEND_HEADERS ");
+                break;
+            case 5:
+                println("AJP13_END_RESPONSE ");
+                done = true;
+                break;
+            case 6:
+                println("AJP13_GET_BODY_CHUNK ");
+                break;
+            default:
+                assertTrue("invalid prefix code:  " + code, false);
+                break;
+            }
+        }
+
+        println("shutting down socket...");
+        s.shutdownOutput();
+        s.shutdownInput();
+        s.close();
+
+        println("done test1...");
+    }
+
+    protected static void println(String msg) {
+        System.out.println("[TestAjp13] " + msg);
+    }
+
+    public static void main(String[] args) throws Exception {
+    }
+}
+
+
+class Ajp13Server extends Thread {
+
+    boolean shutdown = false;
+
+    void shutdown() {
+        this.shutdown = true;
+        this.interrupt();
+    }
+
+    public void run() {
+        try {
+            ServerSocket server = new ServerSocket(8009);
+            TestAjp13.println("Ajp13Server running...");
+            Socket socket = server.accept();
+            Ajp13 ajp13 = new Ajp13();
+            MimeHeaders headers = new MimeHeaders();
+            BaseRequest request = new BaseRequest();
+            ajp13.setSocket(socket);
+
+            boolean moreRequests = true;
+            while (moreRequests && !shutdown) {
+            
+                int status = 0;
+                try {
+                    status = ajp13.receiveNextRequest(request);
+                } catch (IOException e) {
+                    if (shutdown) {
+                        TestAjp13.println("Ajp13Server told to shutdown");
+                        break;
+                    }
+                    TestAjp13.println("process: ajp13.receiveNextRequest -> " + e);
+                }
+            
+                if( status==-2) {
+                    // special case - shutdown
+                    // XXX need better communication, refactor it
+                    //                  if( !doShutdown(socket.getLocalAddress(),
+                    //                                  socket.getInetAddress())) {
+                    //                      moreRequests = false;
+                    //                      continue;
+                    //                  }
+                    break;
+                }
+            
+            	// Special low level request allready handled (ie: PING/PONG)
+            	if( status == 999 )
+            	{
+					request.recycle();
+            		continue;
+            	}
+            	
+                if( status != 200 )
+                    break;
+
+                TestAjp13.println(request.toString());
+
+                String message =
+                    "<html><body><pre>" +
+                    "hello from ajp13:  " +
+                    System.getProperty("line.separator") +
+                    request.toString() +
+                    "</pre></body></html>";
+
+                headers.addValue("content-type").setString( "text/html");
+                headers.addValue("content-length").setInt(message.length());
+                headers.addValue("my-header").setString( "my value");
+                ajp13.sendHeaders(200, headers);
+
+                byte[] b = message.getBytes();
+                ajp13.doWrite(b, 0, b.length);
+
+                ajp13.finish();
+
+                request.recycle();
+                headers.recycle();
+            }
+
+            try {
+                ajp13.close();
+            } catch (IOException e) {
+                TestAjp13.println("process: ajp13.close ->" + e);
+            }
+
+            try {
+                socket.close();
+            } catch (IOException e) {
+                TestAjp13.println("process: socket.close ->" + e);
+            }
+            socket = null;
+
+            TestAjp13.println("process:  done");
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e.toString());
+        }
+    }
+}
+
diff --git a/connectors/jk/test/org/apache/ajp/test/TestAll.java b/connectors/jk/test/org/apache/ajp/test/TestAll.java
new file mode 100644
index 0000000..6b8ade1
--- /dev/null
+++ b/connectors/jk/test/org/apache/ajp/test/TestAll.java
@@ -0,0 +1,40 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ajp.test;
+
+// junit
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class TestAll extends TestCase {
+
+    public TestAll(String testName) {
+        super(testName);
+    }
+
+    public static Test suite() {
+        TestSuite suite = new TestSuite();
+        suite.addTest(TestAjp13.suite());
+        return suite;
+    }
+
+    public static void main(String args[]) {
+        String[] testCaseName = { TestAll.class.getName() };
+        junit.textui.TestRunner.main(testCaseName);
+    }
+}
diff --git a/connectors/jk/tools/reports/README.txt b/connectors/jk/tools/reports/README.txt
new file mode 100644
index 0000000..c53d39f
--- /dev/null
+++ b/connectors/jk/tools/reports/README.txt
@@ -0,0 +1,18 @@
+This directory contains perl scripts which can be used to generate
+statistics for tomcat requests and errors logged by mod_jk.
+
+See the comments in the scripts for more details.
+
+A great deal of statistical data is generated but at this time
+only long term trend graphs are being created and no reports.
+This is only a start.  Many more graphs and reports could be
+generated from the data. Please consider contributing back any
+new reports or graphs you create.  Thanks.
+
+Requires the following perl modules and libraries:
+
+GD 1.8.x graphics library http://www.boutell.com/gd/.
+GD 1.4.x perl module.
+GD Graph perl module.
+GD TextUtil perl module.
+StatisticsDescriptive perl module.
diff --git a/connectors/jk/tools/reports/tomcat_reports.pl b/connectors/jk/tools/reports/tomcat_reports.pl
new file mode 100755
index 0000000..517d9af
--- /dev/null
+++ b/connectors/jk/tools/reports/tomcat_reports.pl
@@ -0,0 +1,432 @@
+#!/usr/local/bin/perl
+
+#
+# Copyright 1999-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# $Header$
+# $Revision$
+# $Date$
+
+# Author: Glenn Nielsen
+
+# Script for generating reports and graphs using statistical data generated
+# by the tomcat_trend.pl script.
+#
+# The following graphs are created:
+#
+#  tomcat_request.png
+#    Long term trend graph of total number of tomcat requests handled
+#
+#  tomcat_median.png
+#    Long term overall trend graph of tomcat request latency median
+#
+#  tomcat_deviation.png
+#    Long term overall trend graph of tomcat request mean and standard deviation
+#
+#  tomcat_error.png
+#    Long term trend graph of requests rejected by tomcat. Shows requests rejected
+#    when tomcat has no request processors available.  Can be an indicator that tomcat
+#    is overloaded or having other scaling problems.
+#
+#  tomcat_client.png
+#    Long term trend graph of requests forward to tomcat which were aborted by the remote
+#    client (browser).  You will normally see some aborted requests.  High numbers of these
+#    can be an indicator that tomcat is overloaded or there are requests which have very high
+#    latency.
+#
+# tomcat_reports.pl <directory where statistics are archived> <directory to place graphs/reports in>
+
+use GD;
+use GD::Graph;
+use GD::Graph::Data;
+use GD::Graph::lines;
+use GD::Graph::linespoints;
+use Statistics::Descriptive;
+use Time::Local;
+
+# Constants
+
+%MON = ('JAN' => 0, 'Jan' => 0,
+        'FEB' => 1, 'Feb' => 1,
+        'MAR' => 2, 'Mar' => 2,
+        'APR' => 3, 'Apr' => 3,
+        'MAY' => 4, 'May' => 4,
+        'JUN' => 5, 'Jun' => 5,
+        'JUL' => 6, 'Jul' => 6,
+        'AUG' => 7, 'Aug' => 7,
+        'SEP' => 8, 'Sep' => 8,
+        'OCT' => 9, 'Oct' => 9,
+        'NOV' => 10, 'Nov' => 10,
+        'DEC' => 11, 'Dec' => 11,);
+
+@Months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
+
+# Check the args
+
+$archivedir = $ARGV[0];
+$reportdir = $ARGV[1];
+
+die "Usage: $0 archivedir reportdir"
+   unless( length($archivedir) && ($reportdir) );
+
+die "Archive Directory $archivedir doesn't exist"
+   unless( -d $archivedir);
+
+die "Report Directory $reportdir doesn't exist"
+   unless( -d $reportdir);
+
+# Read in data from file
+die "Archive Directory $archivedir has no global.data file"
+   unless( -e "$archivedir/global.data" );
+
+@Data = `tail -365 $archivedir/global.data`;
+$numdays = $#Data;
+$daycounter = $numdays;
+
+foreach( @Data ) {
+   $line = $_;
+   chomp($line);
+   ($date,$count,$median,$mean,$stddev,$min,$max,$client_gone,$tomcat_full) = split /\s+/,$line;
+   # print "$daycounter $date $count $median $mean $stdev $min $max $client_gone $tomcat_full\n";
+   $start_time = $date unless $start_time>0;
+   $end_time = $date;
+   push @days,int($daycounter/7);
+   push @count,$count;
+   push @median,$median;
+   push @mean,$mean;
+   push @stddev,$mean+$stddev;
+   push @min,$min;
+   push @max,$max;
+   push @client_gone,$client_gone;
+   push @tomcat_full,$tomcat_full;
+   $daycounter--;
+}
+
+($day,$mon,$year) = (localtime($start_time))[3..5];
+$year += 1900;
+$startdate = "$Months[$mon] $day, $year";
+($day,$mon,$year) = (localtime($end_time))[3..5];
+$year += 1900;
+$enddate = "$Months[$mon] $day, $year";
+
+# Output request trend graph
+$outfile = "$reportdir/tomcat_request.png";
+unlink $outfile;
+
+$stats = Statistics::Descriptive::Sparse->new();
+$stats->add_data(@count);
+$max = $stats->max();
+$min = $stats->min();
+
+&RequestGraph();
+
+# Output median latency trend graph
+$outfile = "$reportdir/tomcat_median.png";
+unlink $outfile;
+
+$stats = Statistics::Descriptive::Sparse->new();
+$stats->add_data(@median);
+$max = $stats->max();
+$min = $stats->min();
+
+&MedianGraph();
+
+# Output latency deviation trend graph
+$outfile = "$reportdir/tomcat_deviation.png";
+unlink $outfile;
+
+$stats = Statistics::Descriptive::Sparse->new();
+$stats->add_data(@stddev);
+$stats->add_data(@mean);
+$max = $stats->max();
+$min = $stats->min();
+
+&DeviationGraph();
+
+# Output request error trend graph
+$outfile = "$reportdir/tomcat_error.png";
+unlink $outfile;
+
+$stats = Statistics::Descriptive::Sparse->new();
+$stats->add_data(@tomcat_full);
+$max = $stats->max();
+$min = $stats->min();
+
+&ErrorGraph();
+
+# Output request error trend graph
+$outfile = "$reportdir/tomcat_client.png";
+unlink $outfile;
+
+$stats = Statistics::Descriptive::Sparse->new();
+$stats->add_data(@client_gone);
+$max = $stats->max();
+$min = $stats->min();
+
+&ClientGraph();
+
+exit;
+
+sub RequestGraph {
+
+  $graph = GD::Graph::lines->new(800,600);
+  @data = (\@days,\@count);
+
+  $div = 100;
+  $div = 500 if $max >= 2000;
+  $div = 1000 if $max >= 5000;
+  $div = 5000 if $max >= 20000;
+  $div = 10000 if $max >= 50000;
+  $div = 50000 if $max >= 200000;
+  $div = 100000 if $max >= 500000;
+  $div = 500000 if $max >= 2000000;
+  $div = 1000000 if $max >= 5000000;
+  $ymax = (int($max/$div) + 1)*$div;
+  $ymin = int($min/$div)*$div;
+  $ytick = ($ymax - $ymin)/$div;
+
+  $graph->set(
+    y_label             => 'Requests',
+    title               => "Tomcat Requests by Day from $startdate to $enddate",
+    y_min_value         => $ymin,
+    y_max_value         => $ymax,
+    y_tick_number       => $ytick,
+    y_number_format     => \&val_format,
+    x_label		=> 'Weeks Ago',
+    x_label_skip	=> 7,
+    x_tick_offset	=> $numdays%7,
+    dclrs               => [ qw(green) ],
+    legend_placement    => 'BC'
+  ) or warn $graph->error;
+
+  $graph->set_legend( 'Requests' );
+  $graph->set_title_font(GD::gdGiantFont);
+  $graph->set_x_axis_font(GD::gdSmallFont);
+  $graph->set_y_axis_font(GD::gdSmallFont);
+  $graph->set_legend_font(GD::gdSmallFont);
+  $gd = $graph->plot(\@data);
+  die "\$gd not defined" unless defined $gd;
+
+  open IMG, ">$outfile" or die $!;
+  print IMG $gd->png or die $gd->error;
+  close IMG;
+}
+
+sub MedianGraph {
+
+  $graph = GD::Graph::lines->new(800,600);
+  @data = (\@days,\@median);
+
+  $div = .05;
+  $div = .1 if $max >= .5;
+  $div = .5 if $max >= 2;
+  $div = 1 if $max >= 5;
+  $div = 5 if $max >= 20;
+  $div = 10 if $max >= 50;
+  $div = 50 if $max >= 200;
+  $div = 100 if $max >= 500;
+  $ymax = (int($max/$div) + 1)*$div;
+  $ytick = $ymax/$div;
+
+  $graph->set(
+    y_label             => 'Latency (Seconds)',
+    title               => "Tomcat Request Median Latency by Day from $startdate to $enddate",
+    y_min_value		=> 0,
+    y_max_value         => $ymax,
+    y_tick_number       => $ytick,
+    y_number_format     => \&val_format,
+    x_label             => 'Weeks Ago',
+    x_label_skip        => 7,
+    x_tick_offset       => $numdays%7,
+    dclrs               => [ qw(green) ],
+    legend_placement    => 'BC'
+  ) or warn $graph->error;
+
+  $graph->set_legend( 'Median' );
+  $graph->set_title_font(GD::gdGiantFont);
+  $graph->set_x_axis_font(GD::gdSmallFont);
+  $graph->set_y_axis_font(GD::gdSmallFont);
+  $graph->set_legend_font(GD::gdSmallFont);
+  $gd = $graph->plot(\@data);
+  die "\$gd not defined" unless defined $gd;
+
+  open IMG, ">$outfile" or die $!;
+  print IMG $gd->png or die $gd->error;
+  close IMG;
+}
+
+sub DeviationGraph {
+
+  $graph = GD::Graph::lines->new(800,600);
+  @data = (\@days,\@mean,\@stddev);
+
+  $div = .1;
+  $div = .5 if $max >= 2;
+  $div = 1 if $max >= 5;
+  $div = 5 if $max >= 20;
+  $div = 10 if $max >= 50;
+  $div = 50 if $max >= 200;
+  $div = 100 if $max >= 500;
+  $ymax = (int($max/$div) + 1)*$div;
+  $ytick = $ymax/$div;
+
+  $graph->set(
+    y_label             => 'Latency (Seconds)',
+    title               => "Tomcat Request Latency Mean and Deviation by Day from $startdate to $enddate",
+    y_max_value         => $ymax,
+    y_tick_number       => $ytick,
+    x_label             => 'Weeks Ago',
+    x_label_skip        => 7,
+    x_tick_offset       => $numdays%7,
+    dclrs               => [ qw(green yellow) ],
+    legend_placement    => 'BC'
+  ) or warn $graph->error;
+
+  $graph->set_legend( 'Mean', 'Mean plus Standard Deviation' );
+  $graph->set_title_font(GD::gdGiantFont);
+  $graph->set_x_axis_font(GD::gdSmallFont);
+  $graph->set_y_axis_font(GD::gdSmallFont);
+  $graph->set_legend_font(GD::gdSmallFont);
+  $gd = $graph->plot(\@data);
+  die "\$gd not defined" unless defined $gd;
+
+  open IMG, ">$outfile" or die $!;
+  print IMG $gd->png or die $gd->error;
+  close IMG;
+}
+
+sub ErrorGraph {
+
+  $graph = GD::Graph::lines->new(800,600);
+  @data = (\@days,\@tomcat_full);
+
+  $div = 5;
+  $div = 10 if $max >=100;
+  $div = 50 if $max >= 200;
+  $div = 100 if $max >= 1000;
+  $div = 500 if $max >= 2000;
+  $div = 1000 if $max >= 5000;
+  $div = 5000 if $max >= 20000;
+  $div = 10000 if $max >= 50000;
+  $div = 50000 if $max >= 200000;
+  $div = 100000 if $max >= 500000;
+  $div = 500000 if $max >= 2000000;
+  $div = 1000000 if $max >= 5000000;
+  $ymax = (int($max/$div) + 1)*$div;
+  $ymin = int($min/$div)*$div;
+  $ytick = ($ymax - $ymin)/$div;
+
+  $graph->set(
+    y_label             => 'Requests',
+    title               => "Tomcat Rejected Request by Day from $startdate to $enddate",
+    y_min_value         => $ymin,
+    y_max_value         => $ymax,
+    y_tick_number       => $ytick,
+    y_number_format     => \&val_format,
+    x_label             => 'Weeks Ago',
+    x_label_skip        => 7,
+    x_tick_offset       => $numdays%7,
+    dclrs               => [ qw(green) ],
+    legend_placement    => 'BC'
+  ) or warn $graph->error;
+
+  $graph->set_legend( 'Tomcat Rejected Requests' );
+  $graph->set_title_font(GD::gdGiantFont);
+  $graph->set_x_axis_font(GD::gdSmallFont);
+  $graph->set_y_axis_font(GD::gdSmallFont);
+  $graph->set_legend_font(GD::gdSmallFont);
+  $gd = $graph->plot(\@data);
+  die "\$gd not defined" unless defined $gd;
+
+  open IMG, ">$outfile" or die $!;
+  print IMG $gd->png or die $gd->error;
+  close IMG;
+}
+
+sub ClientGraph {
+
+  $graph = GD::Graph::lines->new(800,600);
+  @data = (\@days,\@client_gone);
+
+  $div = 5;
+  $div = 10 if $max >=100;
+  $div = 50 if $max >= 200;
+  $div = 100 if $max >= 1000;
+  $div = 500 if $max >= 2000;
+  $div = 1000 if $max >= 5000;
+  $div = 5000 if $max >= 20000;
+  $div = 10000 if $max >= 50000;
+  $div = 50000 if $max >= 200000;
+  $div = 100000 if $max >= 500000;
+  $div = 500000 if $max >= 2000000;
+  $div = 1000000 if $max >= 5000000;
+  $ymax = (int($max/$div) + 1)*$div;
+  $ymin = int($min/$div)*$div;
+  $ytick = ($ymax - $ymin)/$div;
+
+  $graph->set(
+    y_label             => 'Requests',
+    title               => "Tomcat Client Aborted Requests by Day from $startdate to $enddate",
+    y_min_value         => $ymin,
+    y_max_value         => $ymax,
+    y_tick_number       => $ytick,
+    y_number_format     => \&val_format,
+    x_label             => 'Weeks Ago',
+    x_label_skip        => 7,
+    x_tick_offset       => $numdays%7,
+    dclrs               => [ qw(green) ],
+    legend_placement    => 'BC'
+  ) or warn $graph->error;
+
+  $graph->set_legend( 'Tomcat Client Aborted Requests' );
+  $graph->set_title_font(GD::gdGiantFont);
+  $graph->set_x_axis_font(GD::gdSmallFont);
+  $graph->set_y_axis_font(GD::gdSmallFont);
+  $graph->set_legend_font(GD::gdSmallFont);
+  $gd = $graph->plot(\@data);
+  die "\$gd not defined" unless defined $gd;
+
+  open IMG, ">$outfile" or die $!;
+  print IMG $gd->png or die $gd->error;
+  close IMG;
+}
+
+sub val_format {
+  my $value = shift;
+  my $ret;
+
+  $ret = $value;
+  if( $ret =~ /\./ ) {
+    $ret =~ s/\.(\d\d\d).*/\.$1/;
+  } else {
+    $ret =~ s/(\d+)(\d\d\d)$/$1,$2/;
+    $ret =~ s/(\d+)(\d\d\d),(\d\d\d)$/$1,$2,$3/;
+  }
+  return $ret;
+}
+
+sub size_format {
+  my $value = shift;
+  my $ret;
+
+  if( $max >= 5000 ) {
+     $value = int(($value/1024)+.5);
+  }
+  $ret = $value;
+  $ret =~ s/(\d+)(\d\d\d)$/$1,$2/;
+  $ret =~ s/(\d+)(\d\d\d),(\d\d\d)$/$1,$2,$3/;
+  return $ret;
+}
diff --git a/connectors/jk/tools/reports/tomcat_trend.pl b/connectors/jk/tools/reports/tomcat_trend.pl
new file mode 100755
index 0000000..48bc41e
--- /dev/null
+++ b/connectors/jk/tools/reports/tomcat_trend.pl
@@ -0,0 +1,401 @@
+#!/usr/local/bin/perl
+
+#
+# Copyright 1999-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# $Header$
+# $Revision$
+# $Date$
+
+# Author:  Glenn Nielsen
+
+# Script for analyzing mod_jk.log data when logging tomcat request data using
+# the JkRequestLogFormat Apache mod_jk configuration.
+#
+# Generates statistics for request latency and errors.  Archives the generated
+# data to files for later use in long term trend graphs and reports.
+#
+# tomcat_trend.pl <directory containing mod_jk.log> <directory for archiving statistics>
+
+use FileHandle;
+use Statistics::Descriptive;
+use Time::Local;
+
+# Constants
+
+%MON = ('JAN' => 0, 'Jan' => 0,
+        'FEB' => 1, 'Feb' => 1,
+        'MAR' => 2, 'Mar' => 2,
+        'APR' => 3, 'Apr' => 3,
+        'MAY' => 4, 'May' => 4,
+        'JUN' => 5, 'Jun' => 5,
+        'JUL' => 6, 'Jul' => 6,
+        'AUG' => 7, 'Aug' => 7,
+        'SEP' => 8, 'Sep' => 8,
+        'OCT' => 9, 'Oct' => 9,
+        'NOV' => 10, 'Nov' => 10,
+        'DEC' => 11, 'Dec' => 11,);
+
+@Months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
+
+# Check the args
+
+$logdir= $ARGV[0];
+$archivedir = $ARGV[1];
+
+die "Usage: $0 logdir archivedir"
+   unless( length($logdir) && length($archivedir) );
+
+die "Log Directory $logdir doesn't exist"
+   unless( -d $logdir);
+
+die "Archive Directory $archivedir doesn't exist"
+   unless( -d $archivedir);
+
+# Get start date from global.data if it exists
+
+if( -e "$archivedir/global.data" ) {
+   # Get the start date from the last entry in global.data
+   @tail = `tail -1 $archivedir/global.data`;
+   $startdate = (split /\s+/,$tail[0])[0];
+   ($day, $mon, $year) = (localtime($startdate))[3..5];
+   $startdate = timelocal(0,0,0,$day+1,$mon,$year);
+
+}
+
+($day, $mon, $year) = (localtime(time))[3..5];
+$curdate = timelocal(0,0,0,$day,$mon,$year);
+print "Today: " . scalar(localtime($curdate)) . "\n";
+
+# Get the log files names and date they start
+@logs = `ls -1 $logdir/mod_jk.log*`;
+foreach( @logs ) {
+   $logfile = $_;
+   chomp($logfile);
+   next if ( $logfile =~ /\.(bz2|gz|zip)$/ );
+   @head = `head -1 $logfile`;
+   ($mon, $day, $time, $year) = (split /\s+/,$head[0])[1..4];
+   ($hour, $min, $sec) = split /:/,$time;
+   $year =~ s/\]$//;
+   $logtime = timelocal($sec,$min,$hour,$day,$MON{$mon},$year-1900);
+   $modjklog{$logtime} = $logfile;
+}
+
+# Set the startdate if this is the first time processing the logs
+# If we have a startdate, remove log files we con't need to process
+foreach $logtime ( sort {$a <=> $b} keys %modjklog ) {
+   # If logs haven't been processed before, set startdate to time of 
+   # first log entry
+   if( $startdate !~ /^\d+$/ ) {
+      $startdate = $logtime;
+      ($day, $mon, $year) = (localtime($startdate))[3..5];
+      $startdate = timelocal(0,0,0,$day,$mon,$year);
+      last;
+   }
+   if( $logtime > $startdate ) {
+      last;
+   }
+   # Save the previous log file since start date may start here
+   $prevlogfile = $modjklog{$logtime};
+   $prevlogtime = $logtime;
+   # Remove log files we don't need to process
+   delete $modjklog{$logtime};
+}
+
+# Add back in the previous log file where we need to start processing
+if( defined $prevlogtime ) {
+   $modjklog{$prevlogtime} = $prevlogfile;
+}
+
+print "StartDate: " . scalar(localtime($startdate)) . "\n";
+$processdate = $startdate;
+
+foreach $key ( sort {$a <=> $b} keys %modjklog ) {
+   $logtime = $processdate;
+   $logfile = $modjklog{$key};
+   print "Processing log: $logfile\n";
+   last if( $key >= $curdate );
+   $fh = new FileHandle "<$logfile";  
+   die "Open of logfile $logfile failed: $!"
+      unless defined $fh;
+   while( $line = $fh->getline) {
+      chomp($line);
+      ($mon, $day, $time, $year) = (split /\s+/,$line)[1..4];
+      ($hour, $min, $sec) = split /:/,$time;
+      $year =~ s/\]$//;
+      if( $day !~ /^\d+/ || $hour !~ /^\d+/ || $min!~ /^\d+/ || $sec !~ /^\d+/ ) {
+         print "Unknown log entry: $origline\n" unless $origline =~ /\.c /;
+         next;
+      }
+      $logtime = timelocal($sec,$min,$hour,$day,$MON{$mon},$year-1900);
+
+      if( $logtime > $processdate ) {
+         $origline = $line;
+         # Strip off the leading date and time
+         $line =~ s/^\[.*\] //;
+
+         # See if this is a new 5 minute period
+         $interval = int($logtime/300);
+         if( $interval != $previnterval ) {
+            if( defined $previnterval ) {
+               &IntervalStats(\%Global,\%Interval,$previnterval*300);
+            }
+            undef %Interval;
+            undef @IntervalLatency;
+            undef %IntervalWorkers;
+            $Interval{tomcat_full} = 0;
+            $Interval{client_gone} = 0;
+            $Interval{latency} = \@IntervalLatency;
+            $Interval{workers} = \%IntervalWorkers;
+            $previnterval = $interval;
+         }
+
+         # See if this is a new day
+         if( $day != $prevday ) {
+            if( defined $prevday ) {
+               &DailyStats($processdate,\%Global);
+            }
+            undef %Global;
+            undef %GlobalWorkers;
+            undef @GlobalLatency;
+            $Global{tomcat_full} = 0;
+            $Global{client_gone} = 0;
+            $Global{interval} = "";
+            $Global{latency} = \@GlobalLatency;
+            $Global{workers} = \%GlobalWorkers;
+            $Global{errors} = "";
+            $prevday = $day;
+            $processdate = $logtime;
+         }
+
+         # Stop processing if logtime is today
+         last if( $logtime >= $curdate );
+
+         if( $line =~ /\d\)\]: / ) {
+            # Handle a mod_jk error
+            if( $line =~ /(jk_tcp_socket_recvfull failed|ERROR: Receiving from tomcat failed)/ ) {
+               $Global{tomcat_full}++;
+               $Interval{tomcat_full}++;
+            } elsif( $line =~ /(ajp_process_callback - write failed|ERROR sending data to client. Connection aborted or network problems|Client connection aborted or network problems)/ ) {
+               $Global{client_gone}++;
+               $Interval{client_gone}++;
+            }
+            next;
+         } else {
+            # Handle a mod_jk request log entry
+            $line =~ s/^\[.*\] //;
+            $line =~ s/\"(GET|POST|OPTIONS|HEAD)[^\"]*\" //;
+            $line =~ s/[\?\;].*\"//;
+            $line =~ s/\"//g;
+            ($work, $host, $page, $status, $latency) = split /\s+/,$line;
+            $page =~ s/\/\//\//g;
+            $page =~ s/\.\//\//g;
+            if( length($work) <= 0 || length($host) <= 0 ||
+                length($page) <= 0 || $status !~ /^\d+$/ || $latency !~ /^\d+\.\d+$/ ) {
+               print "Unknown log entry: $origline\n" unless $origline =~ /\.c /;
+               next;
+            }
+
+            # Throw out abnormally long requests and log them as an error
+            if( $latency >= 1800 ) {
+               $Global{errors} .= "Error: $page has an HTTP status of $status and an ";
+               $Global{errors} .= "abnormally long request latency of $latency seconds\n";
+               next;
+            }
+
+            # Save the data by day for Global, Worker, and Host
+            push @{$Global{latency}},$latency;
+            $workers = $Global{workers};
+            if( !defined $$workers{$work} ) {
+               undef @{"$work"};
+               undef %{"$work"};
+               undef %{"$work-hosts"};
+               ${"$work"}{latency} = \@{"$work"};
+               ${"$work"}{hosts} = \%{"$work-hosts"};
+               ${"$work"}{interval} = "";
+               $$workers{$work} = \%{"$work"};
+            }
+            $worker = $$workers{$work};
+            push @{$$worker{latency}},$latency;
+
+            if( !defined $$worker{hosts}{$host} ) {
+               undef @{"$work-$host"};
+               undef %{"$work-$host"};
+               undef %{"$work-$host-pages"};
+               ${"$work-$host"}{latency} = \@{"$work-$host"};
+               ${"$work-$host"}{pages} = \%{"$work-$host-pages"};
+               ${"$work-$host"}{interval} = "";
+               $$worker{hosts}{$host} = \%{"$work-$host"};
+            }
+            $hoster = $$worker{hosts}{$host};
+            push @{$$hoster{latency}},$latency;
+
+            if( !defined $$hoster{pages}{$page} ) {
+                undef @{"$work-$host-$page"};
+                $$hoster{pages}{$page} = \@{"$work-$host-$page"};
+            }
+            push @{$$hoster{pages}{$page}},$latency;
+
+            # Save the data by 5 minute interval for Global, Worker, and Host
+            push @{$Interval{latency}},$latency;
+            $workers = $Interval{workers};
+            if( !defined $$workers{"$work"} ) {
+               undef @{"int-$work"};
+               undef %{"int-$work"};
+               undef %{"int-$work-hosts"};
+               ${"int-$work"}{latency} = \@{"int-$work"};
+               ${"int-$work"}{hosts} = \%{"int-$work-hosts"};
+               $$workers{$work} = \%{"int-$work"};
+            }
+            $worker = $$workers{$work};
+            push @{$$worker{latency}},$latency;
+
+            if( !defined $$worker{hosts}{$host} ) {
+               undef @{"int-$work-$host"};
+               undef %{"int-$work-$host"};
+               ${"int-$work-$host"}{latency} = \@{"int-$work-$host"};
+               $$worker{hosts}{$host} = \%{"int-$work-$host"};
+            }
+            $hoster = $$worker{hosts}{$host};
+            push @{$$hoster{latency}},$latency;
+         }
+      }
+   }
+   undef $fh;
+}
+
+# If the last log file ends before switch to the current day,
+# output the last days data
+if( $logtime < $curdate ) {
+   &IntervalStats(\%Global,\%Interval,$previnterval*300);
+   &DailyStats($processdate,\%Global);
+}
+
+exit;
+
+sub IntervalStats($$$) {
+   my $global = $_[0];
+   my $data = $_[1];
+   my $interval = $_[2];
+
+   ($count,$median,$mean,$stddev,$min,$max) = &CalcStats($$data{latency});
+   $$global{interval} .= "$interval $count $median $mean $stddev $min $max $$data{client_gone} $$data{tomcat_full}\n";
+
+   foreach $work ( keys %{$$data{workers}} ) {
+      $worker = $$data{workers}{$work};
+      $gworker = $$global{workers}{$work};
+      ($count,$median,$mean,$stddev,$min,$max) = &CalcStats($$worker{latency});
+      $$gworker{interval} .= "$interval $count $median $mean $stddev $min $max\n";
+      foreach $host ( keys %{$$worker{hosts}} ) {
+         $hoster = $$worker{hosts}{$host};
+         $ghoster = $$gworker{hosts}{$host};
+         ($count,$median,$mean,$stddev,$min,$max) = &CalcStats($$hoster{latency});
+         $$ghoster{interval} .= "$interval $count $median $mean $stddev $min $max\n";
+      }
+   }
+}
+
+sub DailyStats($$) {
+   my $date = $_[0];
+   my $data = $_[1];
+
+   &SaveStats($data,$date,"","global");
+   &SaveFile($$data{interval},$date,"","daily");
+   foreach $work ( keys %{$$data{workers}} ) {
+      $worker = $$data{workers}{$work};
+      &SaveStats($worker,$date,$work,"global");
+      &SaveFile($$worker{interval},$date,$work,"daily");
+      foreach $host ( keys %{$$worker{hosts}} ) {
+         $hoster = $$worker{hosts}{$host};
+         &SaveStats($hoster,$date,"$work/$host","global");
+         &SaveFile($$hoster{interval},$date,"$work/$host","daily");
+         $pagedata = "";
+         foreach $page ( sort keys %{$$hoster{pages}} ) {
+            $pager = $$hoster{pages}{$page};
+            ($count,$median,$mean,$stddev,$min,$max) = &CalcStats($pager);
+            $pagedata .= "$page $count $median $mean $stddev $min $max\n";
+         }
+         $pagedata .= $$data{errors};
+         &SaveFile($pagedata,$date,"$work/$host","request");
+      }
+   }
+}
+
+sub CalcStats($) {
+   my $data = $_[0];
+
+   $stats = Statistics::Descriptive::Full->new();
+   $stats->add_data(@{$data});
+   $median = $stats->median();
+   $mean = $stats->mean();
+   $stddev = $stats->standard_deviation();
+   $max = $stats->max();
+   $min = $stats->min();
+   $count = $stats->count();
+   return ($count,$median,$mean,$stddev,$min,$max);
+}
+
+sub SaveStats($$$$) {
+   my $data = $_[0];
+   my $date = $_[1];
+   my $dir = $_[2];
+   my $file = $_[3];
+
+   if( length($dir) > 0 ) {
+      $dir = "$archivedir/$dir";
+   } else {
+      $dir = $archivedir;
+   }
+   mkdir "$dir",0755;
+
+   $outfile = "$dir/${file}.data";
+
+   ($count,$median,$mean,$stddev,$min,$max) = &CalcStats($$data{latency});
+
+   open DATA, ">>$outfile" or die $!;
+   print DATA "$date $count $median $mean $stddev $min $max";
+   print DATA " $$data{client_gone} $$data{tomcat_full}" if defined $$data{tomcat_full};
+   print DATA "\n";
+   close DATA;
+}
+
+sub SaveFile($$$$) {
+   my $data = $_[0];
+   my $date = $_[1];
+   my $dir = $_[2];
+   my $file = $_[3];
+   my ($day, $mon, $year);
+
+   ($day, $mon, $year) = (localtime($date))[3..5];
+   $year += 1900;
+   $mon++;
+   $mon = "0$mon" if $mon < 10;
+   $day = "0$day" if $day < 10;
+   $file = "$year-$mon-$day-$file";
+
+   if( length($dir) > 0 ) {
+      $dir = "$archivedir/$dir";
+   } else {
+      $dir = $archivedir;
+   }
+   mkdir "$dir",0755;
+
+   $outfile = "$dir/${file}.data";
+
+   open DATA, ">>$outfile" or die $!;
+   print DATA $data;
+   close DATA;
+}
diff --git a/connectors/jk/xdocs/.cvsignore b/connectors/jk/xdocs/.cvsignore
new file mode 100644
index 0000000..429ffae
--- /dev/null
+++ b/connectors/jk/xdocs/.cvsignore
@@ -0,0 +1,3 @@
+style.css
+style.xsl
+menu.idx
diff --git a/connectors/jk/xdocs/build.properties.samples b/connectors/jk/xdocs/build.properties.samples
new file mode 100644
index 0000000..a08a255
--- /dev/null
+++ b/connectors/jk/xdocs/build.properties.samples
@@ -0,0 +1,13 @@
+#
+# Define the color of html pages.
+#
+body-bg=#ffffff
+body-fg=#000000
+body-link=#525D76
+banner-bg=#525D76
+banner-fg=#ffffff
+sub-banner-bg=#828DA6
+sub-banner-fg=#ffffff
+table-th-bg=#039acc
+table-td-bg=#a0ddf0
+source-color=#023264
diff --git a/connectors/jk/xdocs/common/AJPv13-extensions-proposal.xml b/connectors/jk/xdocs/common/AJPv13-extensions-proposal.xml
new file mode 100644
index 0000000..63dfb46
--- /dev/null
+++ b/connectors/jk/xdocs/common/AJPv13-extensions-proposal.xml
@@ -0,0 +1,680 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>AJPv13 extensions Proposal</title>
+<author email="hgomez@apache.org">Henri Gomez</author>
+<date>$Date: 2002/09/20 21:35:31 $</date>
+</properties>
+
+<section name="Introduction">
+<p>
+This document is a proposal of evolution of the current
+Apache JServ Protocol version 1.3, also known as ajp13.  
+I'll not cover here the full protocol but only the add-on from ajp13.
+
+This nth pass include comments from the tomcat-dev list and
+misses discovered during developpment.
+</p>
+<subsection name="Missing features in AJP13">
+<p>
+ajp13 is a good protocol to link a servlet engine like tomcat to a web server like Apache: 
+
+<ul>
+<li>
+use persistants connections to avoid reconnect time at each request
+</li>
+<li>
+encode many http commands to reduce stream size
+</li>
+<li>
+send to servlet engine many info from web server (like SSL certs)
+</li>
+</ul>
+<p>
+But ajp13 lacks support for : 
+</p>
+<ul>
+<li>
+  security between web server and servlet engine.
+  Anybody can connect to an ajp13 port (no login mecanism used)
+  You could connect, for example with telnet, and keep the remote thread
+  up by not sending any data (no timeout in connection)
+</li>
+<li>
+  context information passed from servlet engine to web server.
+  Part of the configuration of JK, the web server connector, is to
+  indicate to the web server which URI to handle. 
+  The mod_jk JkMount directive, told to web server which URI must be 
+  forwarded to servlet engine.
+  A servlet engine allready knows which URI it handle and TC 3.3 is
+  allready capable to generate a config file for JK from the list
+  of available contexts.
+</li>
+<li>
+  state update of contexts from servlet engine to web server.
+  Big site with farm of Tomcat, like ISP and virtuals hosters,
+  may need to stop a context for admin purposes. In that case the front
+  web server must know that the context is currently down, to eventually
+  relay the request to another Tomcat
+</li>
+<li>
+  verify state of connection before sending request.
+  Actually JK send the request to the servlet engine and next wait 
+  for the answer. But one of the beauty of the socket API, is you that 
+  you could write() to a closed connection without any error reporting, 
+  but a read() to a closed connection return you the error code. 
+</li>
+</ul>
+
+</p>
+</subsection>
+
+<subsection name="Proposed add-ons to AJP13">
+<p>
+Let's descrive here the features and add-on that could be added to AJP13.
+Since this document is a proposal, a reasonable level of chaos must be expected at first.
+Be sure that discussion on tomcat list will help clarify points, add 
+features but the current list seems to be a 'minimun vital'
+
+<ul>
+
+<li>
+Advanced login features at connect time
+</li>
+
+<li>
+Basic authorisation system, where a shared secret key is
+present in web server and servlet engine.
+</li>
+
+<li>
+Basic protocol negociation, just to be sure that if functionnalities are added
+to AJP13 in the future, current implementations will still works.
+</li>
+
+<li>
+Clean handling of 'Unknown packets'
+</li>
+
+<li>
+Extended env vars passed from web-server to servlet engine.
+</li>
+
+<li>
+Add extra SSL informations needed by Servlet 2.3 API (like SSL_KEY_SIZE)
+</li>
+
+</ul>
+
+</p>
+</subsection>
+
+<subsection name="Advanced login">
+<p>
+
+<ol>
+<li>
+WEB-SERVER send LOGIN INIT CMD + NEGOCIATION DATA + WEB SERVER INFO
+</li>
+<li>
+  TOMCAT respond with LOGIN SEED CMD + RANDOM DATA
+</li>
+<li>
+  WEB-SERVER calculted the MD5 of RANDOM DATA+SECRET DATA
+</li>
+<li>
+  WEB-SERVER send LOGIN COMP CMD + MD5 (SECRET DATA + RANDOM DATA)
+</li>
+<li>
+  TOMCAT respond with LOGIN STATUS CMD + NEGOCIED DATA + SERVLET ENGINE INFO
+</li>
+</ol>
+
+To prevent DOS attack, the servlet engine will wait
+the LOGIN CMD only 15/30 seconds and reports the
+timeout exception for admins investigation.
+
+The login command will contains basic protocol
+negociation information like compressing ability, 
+crypto, context info (at start up), context update at 
+run-time (up/down), level of SSL env vars, AJP protocol
+level supported (level1/level2/level3...)
+
+The Web server info will contain web server info and
+connector name (ie Apache 1.3.26 + mod_ssl 2.8.8 + mod_jk 1.2.1 + mod_perl 1.25).
+
+The servlet engine will mask the negociation mask with it's own
+mask (what it can do) and return it when loggin is accepted.
+
+This will help having a basic AJP13 implementation (level 1)
+on a web-server working with a more advanced protocol handler on
+the servlet engine side or vice-versa.
+
+AJP13 was designed to be small and fast and so many
+SSL informations present in the web-server are not
+forwarded to the servlet engine. 
+
+We add here four negociations flags to provide more
+informations on client SSL data (certs), server SSL datas, 
+crypto used, and misc datas (timeout...). 
+</p>
+</subsection>
+
+<subsection name="Messages Stream">
+<p>
+<source>
++----------------+------------------+-----------------+
+| LOGIN INIT CMD | NEGOCIATION DATA | WEB SERVER INFO |
++----------------+------------------+-----------------+
+
++----------------+----------------+
+| LOGIN SEED CMD | MD5 of entropy |
++----------------+----------------+
+
++----------------+----------------------------+
+| LOGIN COMP CMD | MD5 of RANDOM + SECRET KEY |
++----------------+----------------------------+
+
++-----------+---------------+---------------------+
+| LOGOK CMD | NEGOCIED DATA | SERVLET ENGINE INFO |
++-----------+---------------+---------------------+
+
++------------+--------------+
+| LOGNOK CMD | FAILURE CODE |
++------------+--------------+
+</source>
+
+<ul>
+<li>
+LOGIN INIT CMD, LOGIN SEED CMD, LOGIN COMP CMD, LOGOK CMD, LOGNOK CMD are 1 byte long.
+</li>
+<li>
+MD5, MD5 of RANDOM + SECRET KEY are 32 chars long.
+</li>
+<li>
+NEGOCIATION DATA, NEGOCIED DATA, FAILURE CODE are 32 bits long.
+</li>
+<li>
+WEB SERVER INFO, SERVLET ENGINE INFO are CString.
+</li>
+</ul>
+
+The secret key will be set by a new propertie in
+workers.properties : secretkey
+<source>
+worker.ajp13.port=8009
+worker.ajp13.host=localhost
+worker.ajp13.type=ajp13
+worker.ajp13.secretkey=myverysecretkey
+</source>
+</p>
+</subsection>
+
+<subsection name="Shutdown feature">
+<p>
+AJP13 miss a functionnality of AJP12, which is shutdown command.
+A logout will tell servlet engine to shutdown itself.
+<source>
++--------------+----------------------------+
+| SHUTDOWN CMD | MD5 of RANDOM + SECRET KEY |
++--------------+----------------------------+
+
++------------+
+| SHUTOK CMD |
++------------+
+
++-------------+--------------+
+| SHUTNOK CMD | FAILURE CODE |
++-------------+--------------+
+</source>
+
+<ul>
+<li>
+SHUTDOWN CMD, SHUTOK CMD, SHUTNOK CMD are 1 byte long.
+</li>
+<li>
+MD5 of RANDOM + SECRET KEY are 32 chars long.
+</li>
+<li>
+FAILURE CODE is 32 bits long.
+</li>
+</ul>
+
+</p>
+</subsection>
+
+<subsection name="Extended Env Vars feature">
+<p>
+NOTA:
+
+While working on AJP13 in JK, I really discovered "JkEnvVar". 
+The following "Extended Env Vars feature" description may not
+be implemented in extended AJP13 since allready available in original
+implementation.
+
+DESC:
+
+Many users will want to see some of their web-server env vars 
+passed to their servlet engine.
+
+To reduce the network traffic, the web-servlet will send a 
+table to describing the external vars in a shorter fashion.
+
+We'll use there a functionnality allready present in AJP13,
+attributes list :
+
+In the AJP13, we've got :
+
+<source>
+AJP13_FORWARD_REQUEST :=
+    prefix_code      2
+    method           (byte)
+    protocol         (string)
+    req_uri          (string)
+    remote_addr      (string)
+    remote_host      (string)
+    server_name      (string)
+    server_port      (integer)
+    is_ssl           (boolean)
+    num_headers      (integer)
+    request_headers *(req_header_name req_header_value)
+
+    ?context       (byte string)
+    ?servlet_path  (byte string)
+    ?remote_user   (byte string)
+    ?auth_type     (byte string)
+    ?query_string  (byte string)
+    ?jvm_route     (byte string)
+    ?ssl_cert      (byte string)
+    ?ssl_cipher    (byte string)
+    ?ssl_session   (byte string)
+
+    ?attributes   *(attribute_name attribute_value)
+    request_terminator (byte)
+</source>
+
+Using short 'web server attribute name' will reduce the 
+network traffic.
+ 
+<source>
++-------------------+---------------------------+-------------------------------+----+
+| EXTENDED VARS CMD | WEB SERVER ATTRIBUTE NAME | SERVLET ENGINE ATTRIBUTE NAME | ES |
++-------------------+---------------------------+-------------------------------+----+
+</source>
+
+ie :
+
+<source>
+JkExtVars S1 SSL_CLIENT_V_START javax.servlet.request.ssl_start_cert_date
+JkExtVars S2 SSL_CLIENT_V_END   javax.servlet.request.ssl_end_cert_date
+JkExtVars S3 SSL_SESSION_ID     javax.servlet.request.ssl_session_id
+
+
++-------------------+----+-------------------------------------------+
+| EXTENDED VARS CMD | S1 | javax.servlet.request.ssl_start_cert_date |
++-------------------+----+-------------------------------------------+
++----+-----------------------------------------+
+| S2 | javax.servlet.request.ssl_end_cert_date |
++----+-----------------------------------------+
++----+-----------------------------------------+
+| S3 | javax.servlet.request.ssl_end_cert_date |
++----+-----------------------------------------+
+</source>
+
+During transmission in extended AJP13 we'll see attributes name
+containing S1, S2, S3 and attributes values of 
+2001/01/03, 2002/01/03, 0123AFE56.
+ 
+This example showed the use of extended SSL vars but 
+any 'personnal' web-server vars like custom authentification
+vars could be reused in the servlet engine.
+The cost will be only some more bytes in the AJP traffic.
+
+<ul>
+<li>
+EXTENDED VARS CMD is 1 byte long.
+</li>
+<li>
+WEB SERVER ATTRIBUTE NAME, SERVLET ENGINE ATTRIBUTE NAME are CString.
+</li>
+<li>
+ES is an empty CString.
+</li>
+</ul>
+
+</p>
+</subsection>
+
+<subsection name="Context informations forwarding for Servlet engine to Web Server">
+<p>
+Just after the LOGON PHASE, the web server will ask for the list of contexts
+and URLs/URIs handled by the servlet engine.
+It will ease installation in many sites, reduce questions about configuration 
+on tomcat-user list, and be ready for servlet API 2.3.
+
+This mode will be activated by a new directive JkAutoMount 
+
+ie: JkAutoMount examples myworker1 /examples/
+
+If we want to get ALL the contexts handled by the servlet engine, willcard
+could be used :
+
+ie: JkAutoMount * myworker1 *
+
+A servlet engine could have many contexts, /examples, /admin, /test.
+We may want to use only some contexts for a given worker. It was
+done previously, in apache HTTP server for example, by setting by 
+hand the JkMount accordingly in each [virtual] area of Apache.
+
+If you web-server support virtual hosting, we'll forward also that
+information to servlet engine which will only return contexts for
+that virtual host. 
+In that case the servlet engine will only return the URL/URI matching
+these particular virtual server (defined in server.xml). 
+This feature will help ISP and big sites which mutualize large farm
+of Tomcat in load-balancing configuration.
+
+<source>
++-----------------+-------------------+----------+----------+----+
+| CONTEXT QRY CMD | VIRTUAL HOST NAME | CONTEXTA | CONTEXTB | ES |
++-----------------+-------------------+----------+----------+----+
+
++------------------+-------------------+----------+-------------------+----------+---------------+----+
+| CONTEXT INFO CMD | VIRTUAL HOST NAME | CONTEXTA | URL1 URL2 URL3 ES | CONTEXTB | URL1 URL2 ... | ES |
++------------------+-------------------+----------+-------------------+----------+---------------+----+
+</source>
+
+We'll discover via context-query, the list of URL/MIMES handled by the remove servlet engine
+for a list of contextes.
+In wildcard mode, CONTEXTA will contains just '*'.
+
+<ul>
+<li>
+CONTEXT QRY CMD and CONTEXT INFO CMD are 1 byte long.
+</li>
+<li>
+VIRTUAL HOST NAME is a CString, ie an array of chars terminated by a null byte (/0).
+</li>
+<li>
+An empty string is just a null byte (/0).
+</li>
+<li>
+ES is an empty CString. Indicate end of URI/URLs or end of CONTEXTs.
+</li>
+</ul>
+
+NB:<br/>
+When VirtualMode is not to be used, the VIRTUAL HOST NAME is '*'.
+In that case the servlet engine will send all contexts handled.
+</p>
+</subsection>
+
+<subsection name="Context informations updates from Servlet engine to Web Server">
+<p>
+Context update are messages caming from the servlet engine each time a context 
+is desactivated/reactivated. The update will be in use when the directive JkUpdateMount.
+This directive will set the AJP13_CONTEXT_UPDATE_NEG flag.
+
+ie: JkUpdateMount myworker1
+
+<source>
++--------------------+-------------------+----------+--------+----------+--------+----+
+| CONTEXT UPDATE CMD | VIRTUAL HOST NAME | CONTEXTA | STATUS | CONTEXTB | STATUS | ES |
++--------------------+-------------------+----------+--------+----------+--------+----+
+</source>
+
+<ul>
+<li>
+CONTEXT UPDATE CMD, STATUS are 1 byte long.
+</li>
+<li>
+VIRTUAL HOST NAME, CONTEXTS are CString.
+</li>
+<li>
+ES is an empty CString. Indicate end of CONTEXTs.
+</li>
+</ul>
+
+NB:<br/>
+When VirtualMode is not in use, the VIRTUAL HOST NAME is '*'.
+STATUS is one byte indicating if context is UP/DOWN/INVALID
+</p>
+</subsection>
+
+<subsection name="Context status query to Servlet engine">
+<p>
+This query will be used by the web-server to determine if a given
+contexts are UP, DOWN or INVALID (and should be removed).
+
+<source>
++-------------------+--------------------+----------+----------+----+
+| CONTEXT STATE CMD |  VIRTUAL HOST NAME | CONTEXTA | CONTEXTB | ES |
++-------------------+--------------------+----------+----------+----+
+
++-------------------------+-------------------+----------+--------+----------+--------+----+
+| CONTEXT STATE REPLY CMD | VIRTUAL HOST NAME | CONTEXTA | STATUS | CONTEXTB | STATUS | ES |
++-------------------------+-------------------+----------+-------------------+--------+----+
+</source>
+
+<ul>
+<li>
+CONTEXT STATE CMD, CONTEXT STATE REPLY CMD, STATUS are 1 byte long.
+</li>
+<li>
+VIRTUAL HOST NAME, CONTEXTs are CString
+</li>
+<li>
+ES is an empty CString
+</li>
+</ul>
+
+NB:<br/>
+When VirtualMode is not in use, the VIRTUAL HOST NAME is an empty string. 
+</p>
+</subsection>
+
+<subsection name="Handling of unknown packets">
+<p>
+Sometimes even with a well negocied protocol, we may be in a situation 
+where one end (web server or servlet engine), will receive a message it
+couldn't understand. In that case the receiver will send an 
+'UNKNOW PACKET CMD' with attached the unhandled message.
+
+<source>
++--------------------+------------------------+-------------------+
+| UNKNOWN PACKET CMD | UNHANDLED MESSAGE SIZE | UNHANDLED MESSAGE |
++--------------------+------------------------+-------------------+
+</source>
+
+Depending on the message, the sender will report an error and if 
+possible will try to forward the message to another endpoint.
+
+<ul>
+<li>
+UNKNOWN PACKET CMD is 1 byte long.
+</li>
+<li>
+UNHANDLED MESSAGE SIZE is 16bits long.
+</li>
+<li>
+UNHANDLED MESSAGE is an array of byte (length is contained in UNHANDLED MESSAGE SIZE)
+</li>
+</ul>
+
+NB:<br/>
+added UNHANDLED MESSAGE SIZE (development)
+</p>
+</subsection>
+
+<subsection name="Verification of connection before sending request">
+<p>
+NOTA: This fonctionality may never be used, since it may slow up the normal process
+since requiring on the web-server side an extra IO (read) before forwarding
+the request.....
+
+One of the beauty of socket APIs, is that you could write on a half closed socket.
+When servlet engine close the socket, the web server will discover it only at the
+next read() to the socket. 
+Basically, in the AJP13 protocol, the web server send the HTTP HEADER and HTTP BODY 
+(POST by chunk of 8K) to the servlet engine and then try to receive the reply. 
+If the connection was broken the web server will learn it only at receive time.
+
+We could use a buffering scheme but what happen when you use the servlet engine
+for upload operations with more than 8ko of datas ?
+
+The hack in the AJP13 protocol is to add some bytes to read after the end of the
+service :
+
+<source>
+EXAMPLE OF DISCUSSION BETWEEN WEB SERVER AND SERVLET ENGINE
+
+AJP HTTP-HEADER (+ HTTP-POST)   (WEB->SERVLET)
+
+AJP HTTP-REPLY					(SERVLET->WEB)
+
+AJP END OF DISCUSSION			(SERVLET->WEB)
+						
+---> AJP STATUS 				(SERVLET->WEB AJP13)
+</source>
+
+The AJP STATUS will not be read by the servlet engine at the end of 
+the request/response #N but at the begining of the next session.
+
+More at that time the web server could also use OS dependants functions
+(or better APR functions) to determine if there is also more data 
+to read. And that datas could be CONTEXT Updates. 
+
+This will avoid the web server sending a request to a 
+desactivated context. In that case, if the load-balancing is used,
+it will search for another servlet engine to handle the request.
+
+And that feature will help ISP and big sites with farm of tomcat, 
+to updates their servlet engine without any service interruption.
+
+<source>
++------------+-------------+
+| STATUS CMD | STATUS DATA |
++------------+-------------+
+</source>
+
+<ul>
+<li>
+STATUS CMD and STATUS DATA are one byte long.
+</li>
+</ul>
+</p>
+</subsection>
+
+</section>
+
+<section name="Conclusion">
+<p>
+The goal of the extended AJP13 protocol is to overcome some of the original AJP13 limitation.
+An easier configuration, a better support for large site and farm of Tomcat, 
+a simple authentification system and provision for protocol updates.
+
+Using the stable ajp13 implementation in JK (native) and in servlet 
+engine (java), it's a reasonable evolution of the well known ajp13.
+</p>
+</section>
+
+<section name="Commands and IDs in extended AJP13 Index">
+<p>
+Index of Commands and ID to be added in AJP13 Protocol
+</p>
+
+<subsection name="Commands IDs">
+<p>
+<table>
+  <tr><th>Command Name</th><th>Command Number</th></tr>
+  <tr><td>AJP13_LOGINIT_CMD</td><td>0x10</td></tr>
+  <tr><td>AJP13_LOGSEED_CMD</td><td>0x11</td></tr>
+  <tr><td>AJP13_LOGCOMP_CMD</td><td>0x12</td></tr>
+  <tr><td>AJP13_LOGOK_CMD</td><td>0x13</td></tr>
+  <tr><td>AJP13_LOGNOK_CMD</td><td>0x14</td></tr>
+  <tr><td>AJP13_CONTEXT_QRY_CMD</td><td>0x15</td></tr>
+  <tr><td>AJP13_CONTEXT_INFO_CMD</td><td>0x16</td></tr>
+  <tr><td>AJP13_CONTEXT_UPDATE_CMD</td><td>0x17</td></tr>
+  <tr><td>AJP13_STATUS_CMD</td><td>0x18</td></tr>
+  <tr><td>AJP13_SHUTDOWN_CMD</td><td>0x19</td></tr>
+  <tr><td>AJP13_SHUTOK_CMD</td><td>0x1A</td></tr>
+  <tr><td>AJP13_SHUTNOK_CMD</td><td>0x1B</td></tr>
+  <tr><td>AJP13_CONTEXT_STATE_CMD</td><td>0x1C</td></tr>
+  <tr><td>AJP13_CONTEXT_STATE_REP_CMD</td><td>0x1D</td></tr>
+  <tr><td>AJP13_UNKNOW_PACKET_CMD</td><td>0x1E</td></tr>
+</table>
+
+</p>
+</subsection>
+
+<subsection name="Negociations Flags">
+<p>
+<table>
+  <tr><th>Command Name</th><th>Number</th><th>Description</th></tr>
+  <tr><td>AJP13_CONTEXT_INFO_NEG</td><td>0x80000000</td><td>web-server want context info after login</td></tr>
+  <tr><td>AJP13_CONTEXT_UPDATE_NEG</td><td>0x40000000</td><td>web-server want context updates</td></tr>
+  <tr><td>AJP13_GZIP_STREAM_NEG</td><td>0x20000000</td><td>web-server want compressed stream</td></tr>
+  <tr><td>AJP13_DES56_STREAM_NEG</td><td>0x10000000</td><td>web-server want crypted DES56 stream with secret key</td></tr>
+  <tr><td>AJP13_SSL_VSERVER_NEG</td><td>0x08000000</td><td>Extended info on server SSL vars</td></tr>
+  <tr><td>AJP13_SSL_VCLIENT_NEG</td><td>0x04000000</td><td>Extended info on client SSL vars</td></tr>
+  <tr><td>AJP13_SSL_VCRYPTO_NEG</td><td>0x02000000</td><td>Extended info on crypto SSL vars</td></tr>
+  <tr><td>AJP13_SSL_VMISC_NEG</td><td>0x01000000</td><td>Extended info on misc SSL vars</td></tr>
+</table>
+
+<br/>
+
+<table>
+  <tr><th>Negociation ID</th><th>Number</th><th>Description</th></tr>
+  <tr><td>AJP13_PROTO_SUPPORT_AJPXX_NEG</td><td>0x00FF0000</td><td>mask of protocol supported</td></tr>
+  <tr><td>AJP13_PROTO_SUPPORT_AJP13L1_NEG</td><td>0x00010000</td><td>communication could use AJP13 Level 1</td></tr>
+  <tr><td>AJP13_PROTO_SUPPORT_AJP13L2_NEG</td><td>0x00020000</td><td>communication could use AJP13 Level 2</td></tr>
+  <tr><td>AJP13_PROTO_SUPPORT_AJP13L3_NEG</td><td>0x00040000</td><td>communication could use AJP13 Level 3</td></tr>
+</table>
+
+<br/>
+All others flags must be set to 0 since they are reserved for future use.
+
+</p>
+</subsection>
+
+<subsection name="Failure IDs">
+<p>
+<table>
+  <tr><th>Failure Id</th><th>Number</th></tr>
+  <tr><td>AJP13_BAD_KEY_ERR</td><td>0xFFFFFFFF</td></tr>
+  <tr><td>AJP13_ENGINE_DOWN_ERR</td><td>0xFFFFFFFE</td></tr>
+  <tr><td>AJP13_RETRY_LATER_ERR</td><td>0xFFFFFFFD</td></tr>
+  <tr><td>AJP13_SHUT_AUTHOR_FAILED_ERR</td><td>0xFFFFFFFC</td></tr>
+</table>
+</p>
+</subsection>
+
+<subsection name="Status">
+<p>
+<table>
+  <tr><th>Failure Id</th><th>Number</th></tr>
+  <tr><td>AJP13_CONTEXT_DOWN</td><td>0x01</td></tr>
+  <tr><td>AJP13_CONTEXT_UP</td><td>0x02</td></tr>
+  <tr><td>AJP13_CONTEXT_OK</td><td>0x03</td></tr>
+</table>
+</p>
+</subsection>
+
+</section>
+
+</document>
diff --git a/connectors/jk/xdocs/common/AJPv13.xml b/connectors/jk/xdocs/common/AJPv13.xml
new file mode 100644
index 0000000..5908e65
--- /dev/null
+++ b/connectors/jk/xdocs/common/AJPv13.xml
@@ -0,0 +1,688 @@
+<?xml version="1.0"?> 
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>AJPv13</title>
+<author email="danmil@shore.net">danmil@shore.net</author>
+<author email="jfrederic.clere@fujitsu-siemens.com">Jean-Frederic Clere</author>
+<date>$Date$</date>
+</properties>
+
+<section name="Intro">
+
+<p>
+The original document was written by
+Dan Milstein, <author email="danmil@shore.net">danmil@shore.net</author>
+on December 2000. The present document is generated out of an xml file
+to allow a more easy integration in the Tomcat documentation.
+
+</p>
+
+<p>
+This describes the Apache JServ Protocol version 1.3 (hereafter
+<b>ajp13</b>).  There is, apparently, no current documentation of how the
+protocol works.  This document is an attempt to remedy that, in order to
+make life easier for maintainers of JK, and for anyone who wants to
+port the protocol somewhere (into jakarta 4.x, for example).
+</p>
+
+</section>
+
+<section name="author">
+
+<p>
+I am not one of the designers of this protocol -- I believe that Gal
+Shachor was the original designer.  Everything in this document is derived
+from the actual implementation I found in the tomcat 3.x code.  I hope it
+is useful, but I can't make any grand claims to perfect accuracy.  I also
+don't know why certain design decisions were made.  Where I was able, I've
+offered some possible justifications for certain choices, but those are
+only my guesses.  In general, the C code which Shachor wrote is very clean
+and comprehensible (if almost totally undocumented).  I've cleaned up the
+Java code, and I think it's reasonably readable.
+</p>
+</section>
+
+<section name="Design Goals">
+
+<p>
+According to email from Gal Shachor to the jakarta-dev mailing list,
+the original goals of <b>JK</b> (and thus <b>ajp13</b>) were to extend
+<b>mod_jserv</b> and <b>ajp12</b> by (I am only including the goals which
+relate to communication between the web server and the servlet container):
+
+<ul>
+  <li> Increasing performance (speed, specifically). </li>
+
+  <li> Adding support for SSL, so that <code>isSecure()</code> and
+       <code>geScheme()</code> will function correctly within the servlet
+       container.  The client certificates and cipher suite will be
+       available to servlets as request attributes. </li>
+
+</ul>
+</p>
+</section>
+
+<section name="Overview of the protocol">
+
+<p>
+The <b>ajp13</b> protocol is packet-oriented.  A binary format was
+presumably chosen over the more readable plain text for reasons of
+performance.  The web server communicates with the servlet container over
+TCP connections.  To cut down on the expensive process of socket creation,
+the web server will attempt to maintain persistent TCP connections to the
+servlet container, and to reuse a connection for multiple request/response
+cycles.
+</p><p>
+Once a connection is assigned to a particular request, it will not be
+used for any others until the request-handling cycle has terminated.  In
+other words, requests are not multiplexed over connections.  This makes
+for much simpler code at either end of the connection, although it does
+cause more connections to be open at once.
+</p><p>
+Once the web server has opened a connection to the servlet container,
+the connection can be in one of the following states:
+</p><p>
+<ul>
+  <li> Idle <br/> No request is being handled over this connection. </li>
+  <li> Assigned <br/> The connecton is handling a specific request.</li>
+</ul>
+
+</p><p>
+Once a connection is assigned to handle a particular request, the basic
+request informaton (e.g. HTTP headers, etc) is sent over the connection in
+a highly condensed form (e.g. common strings are encoded as integers).
+Details of that format are below in Request Packet Structure. If there is a
+body to the request (content-length > 0), that is sent in a separate
+packet immediately after.
+</p><p>
+At this point, the servlet container is presumably ready to start
+processing the request.  As it does so, it can send the
+following messages back to the web server:
+
+<ul>
+  <li>SEND_HEADERS <br/>Send a set of headers back to the browser.</li>
+
+  <li>SEND_BODY_CHUNK <br/>Send a chunk of body data back to the browser.</li>
+
+  <li>GET_BODY_CHUNK <br/>Get further data from the request if it hasn't all
+  been transferred yet.  This is necessary because the packets have a fixed
+  maximum size and arbitrary amounts of data can be included the body of a
+  request (for uploaded files, for example).  (Note: this is unrelated to
+  HTTP chunked tranfer).</li>
+
+  <li>END_RESPONSE <br/> Finish the request-handling cycle.</li>
+</ul>
+</p><p>
+
+Each message is accompanied by a differently formatted packet of data.  See
+Response Packet Structures below for details.
+</p>
+</section>
+
+<section name="Basic Packet Structure">
+
+<p>
+There is a bit of an XDR heritage to this protocol, but it differs in
+lots of ways (no 4 byte alignment, for example).
+</p><p>
+Byte order: I am not clear about the endian-ness of the individual
+bytes.  I'm guessing the bytes are little-endian, because that's what XDR
+specifies, and I'm guessing that sys/socket library is magically making
+that so (on the C side).  If anyone with a better knowledge of socket calls
+can step in, that would be great.
+</p><p>
+There are four data types in the protocol: bytes, booleans, integers and
+strings.
+
+<dl>
+  <dt><b>Byte</b></dt>
+  <dd>A single byte.</dd>
+
+  <dt><b>Boolean</b></dt>
+  <dd>A single byte, 1 = true, 0 = false.  Using other non-zero values as
+  true (i.e. C-style) may work in some places, but it won't in
+  others.</dd>
+  
+  <dt><b>Integer</b></dt>
+  <dd>A number in the range of 0 to 2^16 (32768).  Stored in 2 bytes with
+  the high-order byte first.</dd>
+
+  <dt><b>String</b></dt>
+  <dd>A variable-sized string (length bounded by 2^16). Encoded with the
+  length packed into two bytes first, followed by the string (including the
+  terminating '\0').  Note that the encoded length does <b>not</b> include
+  the trailing '\0' -- it is like <code>strlen</code>.  This is a touch
+  confusing on the Java side, which is littered with odd autoincrement
+  statements to skip over these terminators.  I believe the reason this was
+  done was to allow the C code to be extra efficient when reading strings
+  which the servlet container is sending back -- with the terminating \0
+  character, the C code can pass around references into a single buffer,
+  without copying.  If the \0 was missing, the C code would have to copy
+  things out in order to get its notion of a string.</dd>
+</dl>
+</p>
+
+<subsection name="Packet Size">
+<p>
+According to much of the code, the max packet
+size is 8 * 1024 bytes (8K).  The actual length of the packet is encoded in the
+header.
+</p>
+</subsection>
+
+<subsection name="Packet Headers">
+<p>
+Packets sent from the server to the container begin with
+<code>0x1234</code>.  Packets sent from the container to the server begin
+with <code>AB</code> (that's the ASCII code for A followed by the ASCII
+code for B).  After those first two bytes, there is an integer (encoded as
+above) with the length of the payload.  Although this might suggest that
+the maximum payload could be as large as 2^16, in fact, the code sets the
+maximum to be 8K.
+
+
+<table>
+  <tr>
+    <th colspan="6">Packet Format (Server->Container)</th>
+  </tr>
+
+  <tr>
+    <th>Byte</th>
+    <td>0</td>
+    <td>1</td>
+    <td>2</td>
+    <td>3</td>
+    <td>4...(n+3)</td>
+  </tr>
+
+  <tr>
+    <th>Contents</th>
+    <td>0x12</td>
+    <td>0x34</td>
+    <td colspan="2">Data Length (n)</td>
+    <td>Data</td>
+  </tr>
+</table>
+
+<table>
+  <tr>
+    <th colspan="6"><b>Packet Format (Container->Server)</b></th>
+  </tr>
+
+  <tr>
+    <th>Byte</th>
+    <td>0</td>
+    <td>1</td>
+    <td>2</td>
+    <td>3</td>
+    <td>4...(n+3)</td>
+  </tr>
+
+  <tr>
+    <th>Contents</th>
+    <td>A</td>
+    <td>B</td>
+    <td colspan="2">Data Length (n)</td>
+    <td>Data</td>
+  </tr>
+</table>
+</p>
+<p>
+<A NAME="prefix-codes"></A> For most packets, the first byte of the
+payload encodes the type of message.  The exception is for request body
+packets sent from the server to the container -- they are sent with a
+standard packet header (0x1234 and then length of the packet), but without
+any prefix code after that (this seems like a mistake to me).
+</p><p>
+The web server can send the following messages to the servlet container:
+
+<table>
+  <tr>
+    <th>Code</th>
+    <th>Type of Packet</th>
+    <th>Meaning</th>
+  </tr>
+  <tr>
+    <td>2</td>
+    <td>Forward Request</td>
+    <td>Begin the request-processing cycle with the following data</td>
+  </tr>
+  <tr>
+    <td>7</td>
+    <td>Shutdown</td>
+    <td>The web server asks the container to shut itself down.</td>
+  </tr>
+  <tr>
+    <td>8</td>
+    <td>Ping</td>
+    <td>The web server asks the container to take control (secure login phase).</td>
+  </tr>
+  <tr>
+    <td>10</td>
+    <td>CPing</td>
+    <td>The web server asks the container to respond quickly with a CPong.</td>
+  </tr>
+  <tr>
+    <td>none</td>
+    <td>Data</td>
+    <td>Size (2 bytes) and corresponding body data.</td>
+  </tr>
+</table>
+</p>
+<p>
+To ensure some
+basic security, the container will only actually do the <code>Shutdown</code> if the
+request comes from the same machine on which it's hosted.
+</p>
+<p>
+The first <code>Data</code> packet is send immediatly after the <code>Forward Request</code> by the web server.
+</p>
+
+<p>The servlet container can send the following types of messages to the web
+server:
+<table>
+  <tr>
+    <th>Code</th>
+    <th>Type of Packet</th>
+    <th>Meaning</th>
+  </tr>
+  <tr>
+    <td>3</td>
+    <td>Send Body Chunk</td>
+    <td>Send a chunk of the body from the servlet container to the web
+    server (and presumably, onto the browser). </td>
+  </tr>
+  <tr>
+    <td>4</td>
+    <td>Send Headers</td>
+    <td>Send the response headers from the servlet container to the web
+    server (and presumably, onto the browser).</td>
+  </tr>
+  <tr>
+    <td>5</td>
+    <td>End Response</td>
+    <td>Marks the end of the response (and thus the request-handling cycle).</td>
+  </tr>
+  <tr>
+    <td>6</td>
+    <td>Get Body Chunk</td>
+    <td>Get further data from the request if it hasn't all been transferred
+    yet.</td>
+  </tr>
+  <tr>
+    <td>9</td>
+    <td>CPong Reply</td>
+    <td>The reply to a CPing request</td>
+  </tr>
+</table>
+</p>
+<p>
+Each of the above messages has a different internal structure, detailed below.
+</p>
+</subsection>
+</section>
+
+<section name="Request Packet Structure">
+
+<p>
+For messages from the server to the container of type "Forward Request":
+</p><p>
+<source>
+AJP13_FORWARD_REQUEST :=
+    prefix_code      (byte) 0x02 = JK_AJP13_FORWARD_REQUEST
+    method           (byte)
+    protocol         (string)
+    req_uri          (string)
+    remote_addr      (string)
+    remote_host      (string)
+    server_name      (string)
+    server_port      (integer)
+    is_ssl           (boolean)
+    num_headers      (integer)
+    request_headers *(req_header_name req_header_value)
+    attributes      *(attribut_name attribute_value)
+    request_terminator (byte) OxFF
+</source>
+</p><p>
+The <code>request_headers</code> have the following structure:
+</p><p>
+<source>
+req_header_name := 
+    sc_req_header_name | (string)  [see below for how this is parsed]
+
+sc_req_header_name := 0xA0xx (integer)
+
+req_header_value := (string)
+</source>
+</p><p>
+
+The <code>attributes</code> are optional and have the following structure:
+</p><p>
+<source>
+attribute_name := sc_a_name | (sc_a_req_attribute string)
+
+attribute_value := (string)
+
+</source>
+</p><p>
+Not that the all-important header is "content-length', because it
+determines whether or not the container looks for another packet
+immediately.
+</p><p>
+Detailed description of the elements of Forward Request.
+</p>
+<subsection name="request_prefix">
+<p>
+For all requests, this will be 2.
+See above for details on other <A HREF="#prefix-codes">prefix codes</A>.
+</p>
+</subsection>
+
+<subsection name="method">
+<p>
+The HTTP method, encoded as a single byte:
+</p>
+
+<p>
+<table>
+  <tr><th>Command Name</th><th>Code</th></tr>
+  <tr><td>OPTIONS</td><td>1</td></tr>
+  <tr><td>GET</td><td>2</td></tr>
+  <tr><td>HEAD</td><td>3</td></tr>
+  <tr><td>POST</td><td>4</td></tr>
+  <tr><td>PUT</td><td>5</td></tr>
+  <tr><td>DELETE</td><td>6</td></tr>
+  <tr><td>TRACE</td><td>7</td></tr>
+  <tr><td>PROPFIND</td><td>8</td></tr>
+  <tr><td>PROPPATCH</td><td>9</td></tr>
+  <tr><td>MKCOL</td><td>10</td></tr>
+  <tr><td>COPY</td><td>11</td></tr>
+  <tr><td>MOVE</td><td>12</td></tr>
+  <tr><td>LOCK</td><td>13</td></tr>
+  <tr><td>UNLOCK</td><td>14</td></tr>
+  <tr><td>ACL</td><td>15</td></tr>
+  <tr><td>REPORT</td><td>16</td></tr>
+  <tr><td>VERSION-CONTROL</td><td>17</td></tr>
+  <tr><td>CHECKIN</td><td>18</td></tr>
+  <tr><td>CHECKOUT</td><td>19</td></tr>
+  <tr><td>UNCHECKOUT</td><td>20</td></tr>
+  <tr><td>SEARCH</td><td>21</td></tr>
+  <tr><td>MKWORKSPACE</td><td>22</td></tr>
+  <tr><td>UPDATE</td><td>23</td></tr>
+  <tr><td>LABEL</td><td>24</td></tr>
+  <tr><td>MERGE</td><td>25</td></tr>
+  <tr><td>BASELINE_CONTROL</td><td>26</td></tr>
+  <tr><td>MKACTIVITY</td><td>27</td></tr>
+</table>
+</p>
+
+<p>Later version of ajp13, when used with mod_jk2, will transport 
+additional methods, even if they are not in this list.
+</p>
+
+</subsection>
+
+<subsection  name="protocol, req_uri, remote_addr, remote_host, server_name, server_port, is_ssl">
+<p>
+  These are all fairly self-explanatory.  Each of these is required, and
+  will be sent for every request.
+</p>
+</subsection>
+
+<subsection name="Headers">
+<p>
+  The structure of <code>request_headers</code> is the following:
+  First, the number of headers <code>num_headers</code> is encoded.
+  Then, a series of header name <code>req_header_name</code> / value
+  <code>req_header_value</code> pairs follows.
+  Common header names are encoded as integers,
+  to save space.  If the header name is not in the list of basic headers,
+  it is encoded normally (as a string, with prefixed length).  The list of
+  common headers <code>sc_req_header_name</code>and their codes
+  is as follows (all are case-sensitive):
+</p><p>
+<table>
+  <tr><th>Name</th><th>Code value</th><th>Code name</th></tr>
+  <tr><td>accept</td><td>0xA001</td><td>SC_REQ_ACCEPT</td></tr>
+  <tr><td>accept-charset</td><td>0xA002</td><td>SC_REQ_ACCEPT_CHARSET</td></tr>
+  <tr><td>accept-encoding</td><td>0xA003</td><td>SC_REQ_ACCEPT_ENCODING</td></tr>
+  <tr><td>accept-language</td><td>0xA004</td><td>SC_REQ_ACCEPT_LANGUAGE</td></tr>
+  <tr><td>authorization</td><td>0xA005</td><td>SC_REQ_AUTHORIZATION</td></tr>
+  <tr><td>connection</td><td>0xA006</td><td>SC_REQ_CONNECTION</td></tr>
+  <tr><td>content-type</td><td>0xA007</td><td>SC_REQ_CONTENT_TYPE</td></tr>
+  <tr><td>content-length</td><td>0xA008</td><td>SC_REQ_CONTENT_LENGTH</td></tr>
+  <tr><td>cookie</td><td>0xA009</td><td>SC_REQ_COOKIE</td></tr>
+  <tr><td>cookie2</td><td>0xA00A</td><td>SC_REQ_COOKIE2</td></tr>
+  <tr><td>host</td><td>0xA00B</td><td>SC_REQ_HOST</td></tr>
+  <tr><td>pragma</td><td>0xA00C</td><td>SC_REQ_PRAGMA</td></tr>
+  <tr><td>referer</td><td>0xA00D</td><td>SC_REQ_REFERER</td></tr>
+  <tr><td>user-agent</td><td>0xA00E</td><td>SC_REQ_USER_AGENT</td></tr>
+</table>
+</p><p>
+  The Java code that reads this grabs the first two-byte integer and if
+  it sees an <code>'0xA0'</code> in the most significant
+  byte, it uses the integer in the second byte as an index into an array of
+  header names.  If the first byte is not '0xA0', it assumes that the
+  two-byte integer is the length of a string, which is then read in.
+</p><p>
+  This works on the assumption that no header names will have length
+  greater than 0x9999 (==0xA000 - 1), which is perfectly reasonable, though
+  somewhat arbitrary. (If you, like me, started to think about the cookie
+  spec here, and about how long headers can get, fear not -- this limit is
+  on header <b>names</b> not header <b>values</b>.  It seems unlikely that
+  unmanageably huge header names will be showing up in the HTTP spec any time
+  soon).
+</p><p>
+  <b>Note:</b> The <code>content-length</code> header is extremely
+  important.  If it is present and non-zero, the container assumes that
+  the request has a body (a POST request, for example), and immediately
+  reads a separate packet off the input stream to get that body.
+</p>
+</subsection>
+
+<subsection name="Attributes">
+<p>
+
+  The attributes prefixed with a <code>?</code>
+  (e.g. <code>?context</code>) are all optional.  For each, there is a
+  single byte code to indicate the type of attribute, and then a string to
+  give its value.  They can be sent in any order (thogh the C code always
+  sends them in the order listed below).  A special terminating code is
+  sent to signal the end of the list of optional attributes. The list of
+  byte codes is:
+</p><p>
+
+<table>
+  <tr><th>Information</th><th>Code Value</th><th>Note</th></tr>
+  <tr><td>?context</td><td>0x01</td><td>Not currently implemented</td></tr>
+  <tr><td>?servlet_path</td><td>0x02</td><td>Not currently implemented</td></tr>
+  <tr><td>?remote_user</td><td>0x03</td><td></td></tr>
+  <tr><td>?auth_type</td><td>0x04</td><td></td></tr>
+  <tr><td>?query_string</td><td>0x05</td><td></td></tr>
+  <tr><td>?jvm_route</td><td>0x06</td><td></td></tr>
+  <tr><td>?ssl_cert</td><td>0x07</td><td></td></tr>
+  <tr><td>?ssl_cipher</td><td>0x08</td><td></td></tr>
+  <tr><td>?ssl_session</td><td>0x09</td><td></td></tr>
+  <tr><td>?req_attribute</td><td>0x0A</td><td>Name (the name of the attribut follows)</td></tr>
+  <tr><td>?ssl_key_size</td><td>0x0B</td><td></td></tr>
+  <tr><td>are_done</td><td>0xFF</td><td>request_terminator</td></tr>
+</table>
+
+</p><p>
+
+  The <code>context</code> and <code>servlet_path</code> are not currently
+  set by the C code, and most of the Java code completely ignores whatever
+  is sent over for those fields (and some of it will actually break if a
+  string is sent along after one of those codes).  I don't know if this is
+  a bug or an unimplemented feature or just vestigial code, but it's
+  missing from both sides of the connection.
+</p><p>
+  The <code>remote_user</code> and <code>auth_type</code> presumably refer
+  to HTTP-level authentication, and communicate the remote user's username
+  and the type of authentication used to establish their identity (e.g. Basic,
+  Digest).  I'm not clear on why the password isn't also sent, but I don't
+  know HTTP authentication inside and out.
+</p><p>
+  The <code>query_string</code>, <code>ssl_cert</code>,
+  <code>ssl_cipher</code>, and <code>ssl_session</code> refer to the
+  corresponding pieces of HTTP and HTTPS.
+</p><p>
+  The <code>jvm_route</code>, as I understand it, is used to support sticky
+  sessions -- associating a user's sesson with a particular Tomcat instance
+  in the presence of multiple, load-balancing servers.  I don't know the
+  details.
+</p><p>
+  Beyond this list of basic attributes, any number of other attributes can
+  be sent via the <code>req_attribute</code> code (0x0A).  A pair of strings
+  to represent the attribute name and value are sent immediately after each
+  instance of that code.  Environment values are passed in via this method.
+</p><p>
+  Finally, after all the attributes have been sent, the attribute terminator,
+  0xFF, is sent.  This signals both the end of the list of attributes and
+  also then end of the Request Packet.
+</p>
+</subsection>
+
+</section>
+
+<section name="Response Packet Structure">
+
+<p>
+For messages which the container can send back to the server.
+
+<source>
+AJP13_SEND_BODY_CHUNK := 
+  prefix_code   3
+  chunk_length  (integer)
+  chunk        *(byte)
+
+
+AJP13_SEND_HEADERS :=
+  prefix_code       4
+  http_status_code  (integer)
+  http_status_msg   (string)
+  num_headers       (integer)
+  response_headers *(res_header_name header_value)
+
+res_header_name := 
+    sc_res_header_name | (string)   [see below for how this is parsed]
+
+sc_res_header_name := 0xA0 (byte)
+
+header_value := (string)
+
+AJP13_END_RESPONSE :=
+  prefix_code       5
+  reuse             (boolean)
+
+
+AJP13_GET_BODY_CHUNK :=
+  prefix_code       6
+  requested_length  (integer)
+</source>
+
+</p>
+<p>
+Details:
+</p>
+
+<subsection name="Send Body Chunk">
+<p>
+  The chunk is basically binary data, and is sent directly back to the browser.
+</p>
+</subsection>
+
+<subsection name="Send Headers">
+<p>
+  The status code and message are the usual HTTP things (e.g. "200" and "OK").
+  The response header names are encoded the same way the request header names are.
+  See <A HREF="#header_encoding">above</A> for details about how the the
+  codes are distinguished from the strings.  The codes for common headers are:
+</p>
+
+<p>
+<table>
+  <tr><th>Name</th><th>Code value</th></tr>
+  <tr><td>Content-Type</td><td>0xA001</td></tr>
+  <tr><td>Content-Language</td><td>0xA002</td></tr>
+  <tr><td>Content-Length</td><td>0xA003</td></tr>
+  <tr><td>Date</td><td>0xA004</td></tr>
+  <tr><td>Last-Modified</td><td>0xA005</td></tr>
+  <tr><td>Location</td><td>0xA006</td></tr>
+  <tr><td>Set-Cookie</td><td>0xA007</td></tr>
+  <tr><td>Set-Cookie2</td><td>0xA008</td></tr>
+  <tr><td>Servlet-Engine</td><td>0xA009</td></tr>
+  <tr><td>Status</td><td>0xA00A</td></tr>
+  <tr><td>WWW-Authenticate</td><td>0xA00B</td></tr>
+</table>
+
+</p>
+
+<p> 
+  After the code or the string header name, the header value is immediately
+  encoded.
+</p>
+
+</subsection>
+
+<subsection name="End Response">
+<p>
+  Signals the end of this request-handling cycle.  If the
+  <code>reuse</code> flag is true (==1), this TCP connection can now be used to
+  handle new incoming requests.  If <code>reuse</code> is false (anything
+  other than 1 in the actual C code), the connection should be closed.
+</p>
+</subsection>
+
+<subsection name="Get Body Chunk">
+<p>
+  The container asks for more data from the request (If the body was
+  too large to fit in the first packet sent over or when the request is
+  chuncked).
+  The server will send a body packet back with an amount of data which is
+  the minimum of the <code>request_length</code>,
+  the maximum send body size (8186 (8 Kbytes - 6)), and the
+  number of bytes actually left to send from the request body.
+<br/>
+  If there is no more data in the body (i.e. the servlet container is
+  trying to read past the end of the body), the server will send back an
+  "empty" packet, which is a body packet with a payload length of 0.
+  (0x12,0x34,0x00,0x00)
+</p>
+</subsection>
+</section>
+
+<section name="Questions I Have">
+
+<p> What happens if the request headers > max packet size?  There is no
+provision to send a second packet of request headers in case there are more
+than 8K (I think this is correctly handled for response headers, though I'm
+not certain).  I don't know if there is a way to get more than 8K worth of
+data into that initial set of request headers, but I'll bet there is
+(combine long cookies with long ssl information and a lot of environment
+variables, and you should hit 8K easily).  I think the connector would just
+fail before trying to send any headers in this case, but I'm not certain.</p>
+
+<p> What about authentication?  There doesn't seem to be any authentication
+of the connection between the web server and the container.  This strikes
+me as potentially dangerous.</p>
+
+</section>
+
+
+</document>
diff --git a/connectors/jk/xdocs/common/doccontrib.xml b/connectors/jk/xdocs/common/doccontrib.xml
new file mode 100644
index 0000000..1c1eed5
--- /dev/null
+++ b/connectors/jk/xdocs/common/doccontrib.xml
@@ -0,0 +1,315 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>How to Contribute to the Documentation</title>
+<author email="rsowders@usgs.gov">Robert Sowders</author>
+<date>$Date$</date>
+</properties>
+<section name="Introduction">
+<p>
+    This document describes how you can easily contribute to the 
+documentation.  I'm going to try to make it easy for everyone to help out with 
+the documentation of Tomcat, more specifically the documentation for the 
+connectors.  This is written from a windows user perspective as I believe they 
+will most benefit from it.  For people using Unix it should be easy for them to 
+apply these steps.  Just substitute Unix sytax where needed.
+</p>
+<p>
+    The documentation is produced using xml with xsl style sheets.  This 
+effectivly seperates the content of the documents from the style, so all that 
+contributers need to worry about the content.  It is much easier to use than 
+html.
+</p>
+<p>
+    It's all really quite simple.  Here is what you will need:
+<ul>
+<li>
+<b>A recent version of Ant</b>
+</li>
+<li>
+<b>The source code for the connectors from cvs</b>
+</li>
+<li>
+<b>Any ascii text editor</b>
+</li>
+</ul>
+</p>
+</section>
+<section name="Getting Started Step by Step">
+<p>
+    After you get these tools they are simple to set up.
+</p>
+    <subsection name="STEP 1. Get Ant">
+<p>
+    Install <a href="http://jakarta.apache.org/ant">Ant</a>. The only advice I 
+have is to choose a simple installation path.  Now set an environment variable 
+for ANT_HOME, and then add the location of the Ant/bin directory to your PATH 
+variable.  Consult your Operating system documentation for information on how 
+to do this.  When you are finished verify that you can run ant from the command 
+line.
+</p>
+<p>
+    Ant is used to build the documentation, among other things, and it must be 
+able to see a file called <b>build.xml</b>.  This file is located in the 
+<b>CVS_HOME\jakarta-tomcat-connectors\jk</b> directory.  In the 
+<b>build.xml</b> file there is a target named docs that will be used to build 
+the docs.
+</p>
+</subsection>
+<subsection name="STEP 2.  Get the sources">
+<p>
+    Get the sources for <a href="http://cvs.apache.org/viewcvs/jakarta-tomcat-
+connectors/">jakarta-tomcat-connectors</a> from the CVS repository.  If you'll 
+be editing from a windows platform you will need a windows cvs client.  There 
+are several available.  I like <a href="http://www.cygwin.com/">cygwin</a>.  
+During the install open the developer group and click on cvs.  Unix users 
+should install the CVS client of their choice, if they don't already have one.
+</p>
+<p>
+    You are ready to download the sources now.  Change directory to the 
+location where you want your repository to be, and run the following commands 
+to download the sources for the first time.  For simplicity we will call this 
+your <b>CVS_HOME.</b>  Mine is located in C:\build.
+</p>
+<p>
+    Login to the repository and then give the cvs password <b>anoncvs</b>.
+<screen>
+<read> </read>
+<read>C:\build></read>
+<read>C:\build\>cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic 
+login</read>
+<read>cvs password:  anoncvs</read>
+<read> </read>
+</screen>
+</p>
+<p>
+    When your prompt comes back you are logged in.  Now run the following 
+command to <b>checkout</b> the sources for the first time.  You should only 
+need to do this once.
+<screen>
+<read> </read>
+<read>C:\build\>cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic checkout 
+jakarta-tomcat-connectors</read>
+<read> </read>
+</screen>
+</p>
+<p>
+    You should now be watching all the downloads come in.  Now that you have 
+the sources on your machine the hard part if over.  From now on, to update your 
+sources all you have to do is cd into any directory in your repository and run 
+cvs update
+    <screen>
+<note>    To update your xdocs directory simply cd into the xdocs directory 
+and:</note>
+<read>C:\build\jakarta-tomcat-connectors\jk\>cd xdocs</read>
+<read>C:\build\jakarta-tomcat-connectors\jk\xdocs\>cvs update -dP</read>
+</screen>
+</p>
+</subsection>
+<subsection name="STEP 3.  Test your build environment">
+<p>
+    Open a command prompt window and cd to the directory where you downloaded 
+the source.  Now cd into the jk directory so <b>Ant</b> can see the 
+<b>build.xml</b> file and,  from a command prompt, run the following:
+<screen>
+<read> </read>
+<read>C:\build\jakarta-tomcat-connectors>cd jk</read>
+<read>C:\build\jakarta-tomcat-connectors\jk>ant docs</read>
+<read> </read>
+</screen>
+</p>.
+<p>
+    You should see the ant compiler messages scrolling by rapidly and then stop 
+with the following:
+<screen>
+<read>[style] Transforming into C:\build\jakarta-tomcat-
+connectors\jk\build\docs></read>
+<read>[style] Processing C:\build\jakarta-tomcat-connectors\jk\xdocs\faq.xml 
+to</read>
+<read>C:\build\jakarta-tomcat-connectors\jk\build\docs\faq.html</read>
+<read>[style] Loading stylesheet C:\build\jakarta-tomcat-
+connectors\jk\xdocs\style.xsl</read>
+<read>[style] Processing C:\build\jakarta-tomcat-connectors\jk\xdocs\index.xml 
+to</read>
+<read>C:\build\jakarta-tomcat-connectors\jk\build\docs\index.html</read>
+<read>[copy] Copying 8 files to C:\build\jakarta-tomcat-
+connectors\jk\build\docs</read>
+<read> </read>
+<read>BUILD SUCCESSFUL</read>
+<read>Total time: 10 seconds</read>
+<read>C:\build\jakarta-tomcat-connectors\jk</read>
+</screen>
+</p>
+<p>
+    All the xml files present in the xdocs directory structure were transformed 
+to html and copied to the <b>CVS_HOME\jk\docs</b> directory.  Open one of the 
+html files in your browser and see how it looks.
+</p>
+</subsection>
+<subsection name="STEP 4.   The editing process.">
+<p>
+    I find it easier to use two windows while doing my updates.  One I call my 
+<b>build</b> window.  I keep this one in the <b>CVS_HOME\jk</b> directory and I 
+only run two commands in this window:
+<screen>
+<read> </read>
+<note>   First I run</note>
+<read>ant clean</read>
+<note>   Then I run</note>
+<read>ant docs</read>
+<read> </read>
+</screen>
+</p>
+<p>
+    My second window I call my <b>edit</b> window and I keep that one in the 
+<b>CVS_HOME\jk\xdocs</b> directory where I'm doing my edits, diffs, and cvs 
+updates.
+</p>
+<p>
+    Before you start editing you should always update your local repository to 
+prevent conflicts.
+<screen>
+<note>    You only need to update the xdocs directory</note>
+<read>C:\build\jakarta-tomcat-connectors\jk>cd xdocs</read>
+<read>C:\build\jakarta-tomcat-connectors\jk\xdocs></read>
+<read>C:\build\jakarta-tomcat-connectors\jk\xdocs>cvs update -dP</read>
+</screen>
+</p>
+<p>
+    Now that your repository is up to date you can begin editing.  Find 
+something in the documentation to edit.  When you find somethng remember the 
+name of the file.  In your <b>edit</b> window find and edit the xml source file 
+with the same name.  After you are done return to the  <b>build</b> window, and 
+in the <b>CVS_HOME\jk</b> directory run:
+<screen>
+<read> </read>
+<read>C:\build\jakarta-tomcat-connectors\jk> ant clean</read>
+<read> </read>
+</screen>
+</p>
+<p>
+    This will delete all the previous html files and make the area ready for 
+updated material.  Now to make fresh documents that incorporate your changes 
+run:
+<screen>
+<read> </read>
+<read>C:\build\jakarta-tomcat-connectors\jk\>ant docs</read>
+<read> </read>
+</screen>
+</p>
+<p>
+    Use your browser to view the edits you just made, they will be in the 
+<b>CVS_HOME\jk\build\docs</b> sub-tree.  If it looks good and is ready to go, 
+all that is left to do is to create a patch and submit it.
+</p>
+</subsection>
+<subsection name="STEP 5.  Creating a patch and submitting it.">
+<p>
+    From your <b>edit</b> window cd into the directory that contains the xml 
+file you are working on, and run the <b>cvs update</b> command.  For example, 
+to produce a unified diff of the index.xml file and call it patch.txt, you 
+would cd into the directory containing the index.xml file and:
+<screen>
+<read>C:\build\jakarta-tomcat-connectors\jk\xdocs\>cvs diff -u index.xml >  
+patch.txt.</read>
+<read> </read>
+</screen>
+</p>
+<p>
+    Now that you have your patch you are ready to send it in.
+</p>
+<p>
+    Patches to the documentation are handled just like a bug report.  You 
+should submit your patches to <a 
+href="http://nagoya.apache.org/bugzilla/">http://nagoya.apache.org/bugzilla/</a>
+ and include a good one line subject.  If this is your first time to use the 
+bug database then you should read  <a 
+href="http://nagoya.apache.org/bugzilla/bugwritinghelp.html">http://nagoya.apach
+e.org/bugzilla/bugwritinghelp.html.</a>  You will need to create a user 
+account.  At the web site paste your patch into the web form and don't forget 
+to describe what it is your patch is for.  Sooner or later a someone with 
+commit privileges will review your suggestion.
+</p>
+</subsection>
+</section>
+<section name="CVS Basics">
+<p>
+    After you have checked out the sources the first time it is much easier to 
+use CVS.  You can cd into any directory of the repository and run <b>cvs 
+update -dP</b> to get the latest sources for that directory.  For editing 
+purposes you should always update your repository before you start editing to 
+reduce conflicts.
+</p>
+<p>
+    You will need to run <b>cvs diff</b> to generate patches for submission.  
+Again cd into the directory containing the file you are editing and run <b>cvs 
+diff -u name_of_the_file_you_edited  patch.txt</b> to generate a patch for 
+submission.  The <b>-u</b> is the flag for a unified diff which is the prefered 
+type.
+</p>
+<p>
+    Pay attention to the terminal window during the update.
+</p>
+<p>
+    Lines begining with a <b>P</b> mean the local copy was patched to update it 
+to the current version in the master repository.
+</p>
+<p>
+    Lines begining with a <b>M</b> mean your local copy is different from the 
+master copy, and the changes were successfully merged into your copy.
+</p>
+<p>
+    Lines begining with a <b>C</b> mean there was a conflict in merging the 
+changes and you need to review the file and merge the changes manually.  Search 
+for  >>>> and merge the changes.
+</p>
+<p>
+    Lines begining with a <b>?</b> indicate files that reside on your local 
+system which are not part of the repository.  You will normally see this when 
+you are creating new files for submission.
+</p>
+</section>
+<section name="Guides and Resources">
+<p>
+    A little help to get you if you need it
+</p>
+<ul>
+<li>
+<a href="http://www.xml.org/xml/resources_focus_beginnerguide.shtml">XML 
+Beginner's Guide</a>
+</li>
+<li>
+<a href="http://nagoya.apache.org/bugzilla/">Bugzilla</a>
+</li>
+<li>
+<a href="http://nagoya.apache.org/bugzilla/bugwritinghelp.html">Bugzilla Bug 
+Writing Guide</a>
+</li>
+<li>
+<a href="http://jakarta.apache.org/ant">Ant</a>
+</li>
+<li>
+<a href="http://www.cvshome.org/">CVS Home</a>
+</li>
+<li>
+<a href="http://cvs.apache.org/viewcvs/jakarta-tomcat-connectors/jk/xdocs/">JK 
+Docs CVS</a>
+</li>
+</ul>
+</section>
+</document>
\ No newline at end of file
diff --git a/connectors/jk/xdocs/common/tools.xml b/connectors/jk/xdocs/common/tools.xml
new file mode 100644
index 0000000..35eabef
--- /dev/null
+++ b/connectors/jk/xdocs/common/tools.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>Tools</title>
+<author>Glenn Nielsen</author>
+<date>$Date$</date>
+</properties>
+<section name="Introduction">
+<p>Documentation for additional mod_jk related tools.</p>
+</section>
+
+<section name="Reports">
+<p>
+The mod_jk source distribution contains two perl scripts in the
+jk/tools/reports directory which can be used to analyze the mod_jk
+logs, save statistical data, and generate report graphs.
+</p>
+
+<p><code>tomcat_trend.pl log_dir archive_dir</code></p>
+<p>
+Script for analyzing mod_jk.log data when logging tomcat request data using
+the <code>JkRequestLogFormat</code> Apache mod_jk configuration.
+Generates statistics for request latency and errors.  Archives the generated
+data to files for later use in long term trend graphs and reports.
+</p>
+
+<p><code>tomcat_reports.pl archive_dir reports_dir</code></p>
+<p>
+Script for generating reports and graphs using statistical data generated
+by the <code>tomcat_trend.pl</code> script.
+
+The following graphs are created:
+<ul>
+  <li>tomcat_request.png - Long term trend graph of total number of tomcat
+    requests handled.</li>
+  <li>tomcat_median.png - Long term overall trend graph of tomcat request
+    latency median.</li>
+  <li>tomcat_deviation.png - Long term overall trend graph of tomcat request
+    mean and standard deviation.</li>
+  <li>tomcat_error.png - Long term trend graph of requests rejected by tomcat.
+    Shows requests rejected when tomcat has no request processors available.
+    Can be an indicator that tomcat is overloaded or having other scaling
+    problems.</li>
+  <li>tomcat_client.png - Long term trend graph of requests forward to tomcat
+    which were aborted by the remote client (browser).  You will normally see
+    some aborted requests.  High numbers of these can be an indicator that
+    tomcat is overloaded or there are requests which have very high latency.</li>
+</ul>
+</p>
+
+<p>
+A great deal of statistical data is generated but at this time
+only long term trend graphs are being created and no reports.
+This is only a start.  Many more graphs and reports could be
+generated from the data. Please consider contributing back any
+new reports or graphs you create.  Thanks.
+</p>
+
+<p>
+These perl scripts depend upon the following perl modules and libraries:
+<ul>
+  <li>GD 1.8.x graphics library <a href="http://www.boutell.com/gd/">
+    http://www.boutell.com/gd/</a></li>
+  <li>GD 1.4.x perl module</li>
+  <li>GD Graph perl module</li>
+  <li>GD TextUtil perl module</li>
+  <li>StatisticsDescriptive perl module</li>
+</ul>
+</p>
+</section>
+</document>
diff --git a/connectors/jk/xdocs/faq.xml b/connectors/jk/xdocs/faq.xml
new file mode 100755
index 0000000..a362ff9
--- /dev/null
+++ b/connectors/jk/xdocs/faq.xml
@@ -0,0 +1,339 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>FAQ</title>
+<author email="hgomez@apache.org">Henri Gomez</author>
+<date>$Date$</date>
+</properties>
+
+<section name="General">
+<p>
+General Informations and FAQ about JK
+</p>
+<subsection name="Where can I get help/support for JK ?">
+<p>
+The primary mechanism for support is through the JK 
+documentation included in the doc directory.
+Documentation is also available on the Apache Jakarta web site devoted to the
+<a href="http://jakarta.apache.org/tomcat/tomcat-4.1-doc/jk2/index.html">
+Jakarta Tomcat Connectors Project</a>
+For additional help, the best resource is the Tomcat Users Discussion list.  
+You should start by searching
+<a href="http://mikal.org/interests/java/tomcat/index.html">
+the mail list archive</a>
+before you post questions to the list.  
+If you are unable to locate the answer to your question in the archive, 
+you can post questions about JK to the user list for assistance.  
+Make sure that you include the version of your Webserver, 
+that you are using as well as the platform you are running on
+and go 
+<a href="http://jakarta.apache.org/site/mail.html">
+here</a>
+to determine how to subscribe to tomcat mailing list.
+</p>
+</subsection>
+
+<subsection name="I can't find JK anywhere. Where is it?">
+<p>
+Now that JK moved to the <b>jakarta-tomcat-connectors</b> repository, 
+the source for JK can be downloaded from a mirror at the
+<a href="http://jakarta.apache.org/site/sourceindex.cgi">
+Jakarta Source Download</a> page and the binaries for JK can
+be downloaded from a mirror at the
+<a href="http://jakarta.apache.org/site/binindex.cgi">
+Jakarta Binary Download</a> page.
+</p>
+</subsection>
+
+<subsection name="What's the difference between JK and mod_jk ?">
+<p>
+<b>JK</b> is a project covering web-servers to Tomcat connectors,
+whereas <b>mod_jk</b> is the <a href="jk/aphowto.html">Apache module</a> developped in JK.
+</p>
+
+<p>
+<a href="jk/domhowto.html">Domino webserver</a> support is implemented on JK, using a redirector
+called <b>dsapi redirector</b>.
+</p>
+
+<p>
+<a href="jk/iishowto.html">IIS webserver</a>support is implemented on JK, using a redirector
+called <b>isapi redirector</b>.
+</p>
+
+<p>
+<a href="jk/neshowto.html">Netscape/iPlanet webserver</a>webserver support is implemented on JK, using a redirector
+called <b>nsapi redirector</b>.
+</p>
+
+</subsection>
+
+<subsection name="Where can I get more information ?">
+<p>
+For <b>JK 1.2.x</b>, you should read :
+</p>
+
+<ul>
+
+<li>
+<a href="jk/quickhowto.html">For the impatients</a>
+</li>
+
+<li>
+<a href="jk/aphowto.html">Apache and JK</a>
+</li>
+
+<li>
+<a href="jk/domhowto.html">Domino and JK</a>
+</li>
+
+<li>
+<a href="jk/iishowto.html">IIS and JK</a>
+</li>
+
+<li>
+<a href="jk/neshowto.html">Netscape/iPlanet and JK</a>
+</li>
+
+<li>
+<a href="jk/workershowto.html">Workers configuration</a>
+</li>
+</ul>
+
+<p>
+For <b>JK 2.0.x</b> the <a href="jk2/configtc.html">
+config tomcat</a> and <a href="jk2/configweb.html">config webserver</a>
+documents have considerably more in-depth information.
+It's worth a look. 
+You could also try searching the mailing list archives for "JK" or look at the source.
+</p>
+</subsection>
+
+<subsection name="Which protocol should I use? Ajp12 or Ajp13?">
+<p>
+<a href="common/AJPv13.html">Ajp13</a> is a newer protocol, it's faster, and it works better with SSL. 
+You almost certainly want to use it now that ajp12 is deprecated.
+</p>
+<p> 
+Also ajp13 is supported by all Apache Tomcat including 3.2.x , 3.3.x, 4.0.x, 4.1.x and the new tomcat 5. 
+</p>
+
+<p>
+Others Servlet engines like <b>jetty</b> have support for Ajp13.
+</p>
+</subsection>
+
+<subsection name="I've got a firewall between my WebServer and Tomcat who drop ajp13 connections after some times">
+<p>
+Ajp13 use persistant connections where the traffic could be null if there is no request to be sent to Tomcat. 
+Firewall used to drop inactive connections and will make your WebServer and Tomcat think the connection is valid. 
+</p>
+<p>
+Starting with JK 1.2.0, a <b>socket_keepalive</b> property as been added to ajp13 settings, and you should take a look at 
+it in <a href="jk/workershowto.html">Workers HowTo</a>.
+</p>
+</subsection>
+
+<subsection name="Under heavy load, I've got many threads in Tomcat even if my Apache Web Server handle much of the load">
+<p>
+Under heavy load, Apache WebServer create many childs to handle the load, which will in turn create many connections 
+to Tomcat to forward the requests they should handle. 
+Apache WebServer will normally kill the childs/threads when the load decrease. But if the load is still there and 
+even if only Apache handle the requests, ie static contents, the childs are kept and with them the ajp13 connections, 
+even if they are no more used. 
+</p>
+<p>
+Since JK 1.2.0, <b>cache_timeout</b> and <b>socket_timeout</b> properties as been added to close 
+connections after some time of inactivity, for more informations refer to <a href="jk/workershowto.html">Workers HowTo</a>.
+</p>
+</subsection>
+
+</section>
+
+<section name="Apache">
+<p>
+Informations and FAQ about mod_jk and Apache Web Servers. 
+</p>
+<subsection name="Whenever I restart Tomcat, Apache locks up!">
+<p>
+The Ajp13 protocol keeps an open socket between Tomcat and Apache. Release of mod_jk present in J-T-C handle the network failure. 
+But with previous release of mod_jk, you may have to restart Apache as well.
+</p>
+</subsection>
+
+<subsection name="Why did exist two files mod_jk.so (-eapi ad -noeapi) in download dir for Linux ?">
+<p>
+Many versions of Apache use of modified API, known at Extended API, developped for use with the
+<a href="http://www.modssl.org">mod_ssl module</a>.
+</p>
+
+<p>
+For example, Apache present in certains recent Linux distributions include the
+<b>mod_ssl</b> module.
+</p>
+
+<p>
+So if you got such 'Extended Apache', you need to use <b>mod_jk.so-eapi</b>.
+</p>
+
+<p> 
+You should use <b>mod_jk.so-noeapi</b> only for 'Standard Apache' (ie without mod_ssl).
+</p>
+
+<p>
+It's wise to avoid using EAPI modules on STD API Apache or to use standard API modules on EAPI Apache. 
+Allways be sure to have the <b>mod_jk.so</b> witch match your version of Apache
+</p>
+</subsection>
+
+<subsection name="What's that message about 'garbled DSO ?'">
+<p>
+It's related to Apache EAPI, the message <code>'mod_jk.so is garbled - perhaps this is not an Apache module DSO ?'</code> 
+just told you, that your're trying to install a mod_jk.so DSO module that was compiled on an Apache using EAPI, 
+like apache-mod_ssl or apache from Redhat distro 6.2/7.0 but your system use the standard apache with normal API.
+</p>
+</subsection>
+
+<subsection name="And the message about 'module might crash under EAPI!">
+<p>
+Also related to EAPI, the message <code>'[warn] Loaded DSO /usr/lib/apache/mod_jk.so uses plain Apache 1.3 API, 
+this module might crash under EAPI! (please recompile it with -DEAPI)'</code>, the mod_jk.so was compiled under normal 
+Apache with standard API and you try to install the module on an Apache using EAPI.
+</p>
+</subsection>
+
+<subsection name="APXS is getting an error during the build of mod_jk, like rc=0 or rc=255.  I tried all of the steps in the build section, what do I do now ?">
+<p>
+APXS is a Perl script that is created when you build the Apache web server from source.  
+Chances are that if you are getting these errors and you obtained Apache as a binary distribution, 
+that APXS is not configured correctly for your system.  
+Your best bet is to get the Apache source from http://httpd.apache.org and build it yourself.  
+Use the following for a basic build (read the Apache docs for other options):
+<screen>
+<type>cd /usr/local/src</type><br/>
+<type>gzip -dc apache_1.3.19.tar.gz|tar xvf -</type><br/>
+<type>cd apache_1.3.19</type><br/>
+<type>./configure --prefix=/usr/local/apache \</type><br/>
+<type>            --enable-module=most \</type><br/>
+<type>            --enable-shared=max</type><br/>
+<type>make</type><br/>
+<type>make install</type><br/>
+</screen>
+</p>
+<p>
+Note: The above steps assume that you downloaded the Apache source and placed it in your /usr/local/src directory.
+</p>
+</subsection>
+
+<subsection name="Apache 2.0 complains about incorrect module version">
+<p>
+Since Apache 2.0 API still change often, the Apache 2.0 teams decide to put in headers of compiled modules the 
+Apache 2.0 version used to compile the module. This check is called Magic Module Number bump.
+</p>
+<p>
+At start time Apache 2.0 check that version in modules headers and stop if it detect that a module was compiled 
+for another Apache 2.0 version. As such you should allways use modules compiled for the same Apache 2.0 version. 
+This check may be removed if the future.
+</p>
+</subsection>
+
+<subsection name="JNI didn't works with Apache 1.3">
+<p>
+JNI support require a multi-threaded environment which is not the general case for Apache 1.3. 
+You should verify if Apache 1.3 has been build with thread support and if not you could add the 
+the pthreads library to your <b>httpd.conf</b> file. 
+</p>
+
+<screen>
+<note># Add pthread to Apache in httpd.conf</note>
+<read>LoadModule "/usr/lib/libpthreads.so"</read>
+</screen>
+
+<p>
+Also keep in mind that JNI is suited for multi-threaded servers and you should consider upgrading 
+to Apache 2.0 to support JNI.
+</p>
+</subsection>
+
+<subsection name="JNI report that JVM couldn't be started under Linux">
+<p>
+Under Linux, you should set some environment variables BEFORE launching your Apache server :
+</p>
+
+<screen>
+<read>export LD_LIBRARY_PATH=$jre/bin:$jre/bin/classic:$LD_LIBRARY_PATH</read>
+</screen>
+
+<p>
+Also some Linux distributions have enabled a GLIBC feature called 'floating stacks' which may not works with kernel 
+less than 2.4.10 on SMP machines. You should disable floating stacks by exporting an environment variable :
+</p>
+
+<screen>
+<read>export LD_ASSUME_KERNEL=2.2.5</read>
+</screen>
+
+<p>
+You could have to update your service scripts, ie <b>/etc/rc.d/init.d/httpd</b>, to set these env vars 
+before your httpd server starts.
+</p>
+</subsection>
+
+<subsection name="Mixed errors when building via configure">
+<p>
+configure assume you have some GNU tools allready installed and configured for your system, and ad minima <b>libtool</b>.
+</p>
+<p>
+Also some systems may have mixed cc and gcc setup which may make you puzzled when trying to link an Apache built with native
+c compiler with a jk/jk2 build with gcc.
+</p>
+<p>
+In fine some native make didn't works as expect so you should use a GNU make <b>gmake</b>.
+</p>
+</subsection>
+
+<subsection name="JK2 build report error about missing FIONBIO on Solaris 8">
+<p>
+In JK2 before v2.0.2, you should add <source>#define BSD_COMP</source> in top
+of jk/native2/common/jk_channel_socket.c to have Solaris build succeed.
+</p>
+</subsection>
+
+
+</section>
+
+<section name="IIS">
+<p>
+Informations and FAQ about JK and IIS Web Servers. 
+</p>
+<todo>
+More informations to be added, Nacho ?
+</todo>
+</section>
+
+<section name="NES/iPlanet">
+<p>
+Informations and FAQ about JK and NES/iPlanet Web Servers. 
+</p>
+<todo>
+More informations to be added, Mike ?
+</todo>
+</section>
+
+
+</document>
diff --git a/connectors/jk/xdocs/images/corner.gif b/connectors/jk/xdocs/images/corner.gif
new file mode 100644
index 0000000..e904bde
--- /dev/null
+++ b/connectors/jk/xdocs/images/corner.gif
Binary files differ
diff --git a/connectors/jk/xdocs/images/jakarta.gif b/connectors/jk/xdocs/images/jakarta.gif
new file mode 100644
index 0000000..049cf82
--- /dev/null
+++ b/connectors/jk/xdocs/images/jakarta.gif
Binary files differ
diff --git a/connectors/jk/xdocs/images/jk2.gif b/connectors/jk/xdocs/images/jk2.gif
new file mode 100644
index 0000000..35bdda6
--- /dev/null
+++ b/connectors/jk/xdocs/images/jk2.gif
Binary files differ
diff --git a/connectors/jk/xdocs/images/jk2.png b/connectors/jk/xdocs/images/jk2.png
new file mode 100644
index 0000000..a30d952
--- /dev/null
+++ b/connectors/jk/xdocs/images/jk2.png
Binary files differ
diff --git a/connectors/jk/xdocs/images/mod_jk.jpg b/connectors/jk/xdocs/images/mod_jk.jpg
new file mode 100644
index 0000000..61e2974
--- /dev/null
+++ b/connectors/jk/xdocs/images/mod_jk.jpg
Binary files differ
diff --git a/connectors/jk/xdocs/images/pixel.gif b/connectors/jk/xdocs/images/pixel.gif
new file mode 100644
index 0000000..05411ad
--- /dev/null
+++ b/connectors/jk/xdocs/images/pixel.gif
Binary files differ
diff --git a/connectors/jk/xdocs/images/tomcat.ico b/connectors/jk/xdocs/images/tomcat.ico
new file mode 100644
index 0000000..20c9e73
--- /dev/null
+++ b/connectors/jk/xdocs/images/tomcat.ico
Binary files differ
diff --git a/connectors/jk/xdocs/index.xml b/connectors/jk/xdocs/index.xml
new file mode 100644
index 0000000..888866b
--- /dev/null
+++ b/connectors/jk/xdocs/index.xml
@@ -0,0 +1,151 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>Overview</title>
+<author email="jfrederic.clere@fujitsu-siemens.com">Jean-Frederic Clere</author>
+<author email="hgomez@apache.org">Henri Gomez</author>
+<author email="yoavs@apache.org">Yoav Shapira</author>
+<date>$Date$</date>
+</properties>
+
+<section name="What's JK&#63;">
+<p>
+<b>JK</b> is a replacement to the elderly mod_jserv. 
+It was a completely new Tomcat-Apache plug-in that handles the communication between Tomcat and Apache.
+</p>
+<p>
+The newest <b>JK2</b> is a refactoring of <b>JK</b>.
+The native part has been completly
+restructured and the configuration has been simplified a lot.
+</p>
+
+<p>
+JK is more than just an apache module, since it could be used with majors WebServer :
+</p>
+
+<ul>
+<li>
+<a href="jk/aphowto.html">mod_jk</a> is an Apache module to be used with <b>Apache 1.3</b> and <b>2.0</b> Webservers.
+</li>
+<li>
+<a href="jk/iishowto.html">isapi</a> is a redirector to be used with <b>IIS</b>.
+</li>
+<li>
+<a href="jk/neshowto.html">nsapi</a> is a redirector to be used with <b>Netscape/iPlanet</b>.
+</li>
+<li>
+<a href="jk/domhowto.html">dsapi</a> is a redirector to to be used with <b>Domino</b>.
+</li>
+</ul>
+
+</section>
+
+<section name="Why should I use the JK&#63;">
+<p>
+JK was develop to overcome many limitations of its ancestor, <b>mod_jserv</b>.
+</p>
+
+<p>
+<b>mod_jserv</b> was too complex and because it was ported from Apache/JServ, 
+it brought with it lots of JServ specific bits that aren't needed by Apache.
+</p>
+
+<p>
+Where <b>mod_jserv</b> supported only Apache webservers on Unix OS, 
+<b>JK</b> supports much more web servers and operating systems through 
+via a compatibility layer named the <b>JK library</b>. 
+The layered approach provided by the JK library makes it easier to 
+support many different webservers and OS.
+</p>
+
+<p>
+JK offer better support for SSL, that's was a problem with mod_jserv which couldn't 
+reliably identify whether a request was made via HTTP or HTTPS. 
+</p>
+<p>
+JK can, using the newer Ajpv13 protocol which relay many SSL informations required by servlet 2.2 and 2.3 specs.
+</p>
+
+<p>
+JK offers a lot of different and flexible communications between a Web Server 
+and the Tomcat Servlet Engine and could be used today with all of the ASF Tomcat Engines, 
+<b>3.2.x</b>, <b>3.3.x</b>, <b>4.0.x</b>, <b>4.1.x</b> and <b>5.x</b>
+</p>
+
+</section>
+
+<section name="What's the difference between JK and JK2&#63;">
+<p>
+JK2 is a refactoring of JK and is much more powerfull.
+</p>
+<p>
+Even if it works with Apache 1.3, JK2 has been developed with Apache 2.0 in mind,
+and is better suited for multi-threaded servers like IIS, NES/iPlanet. It can also
+be embeded in other applications and used from java.
+</p>
+<p>
+JK2 improves the modularity and has a better separation between protocol and physical layer.
+As such JK2 support fast unix-socket, and could be extended to support others communications
+channels. It is better suited for JNI and may use (in a future version) JDK 1.4 NIO.
+</p>
+<p>
+There is additional support for monitoring, similar with JMX in java. A module similar
+with mod_status is provided, and additional adapters can be used to interface and 
+provide status and runtime configuration. .
+</p>
+<p>
+The configuration has been changed to follow the component models. Multiple configuration
+sources can be supported ( in additon to file ) providing better integration with
+the embeding application. The config layer uses the management layer APIs and it can
+support persistence for changes done via runtime configuration.
+</p>
+<p>
+Another feature is the JNI mode. Jk2 can be used as a JNI library and provide access to
+native features to java. For example it provides access to shared memory ( used for 
+config and monitoring in a multiprocess environment ), unix domain sockets. It can
+also provide access to signals, chuid, win registry. All using the same communication
+mechansim, and supporting both in-process and out-of process modes.
+</p>
+</section>
+
+
+<section name="Are there alternative ways to connect Apache and Tomcat&#63;">
+<p>
+<b>Tomcat's internal HTTP stack and mod_proxy</b> which is not that bad, but slow,
+well, of course Tomcat didn't have 10/11 years of C routine optimization like
+the Apache Web Server, but it's quite good. The only disadvantage is that it
+looks pretty ridiculous to parse an HTTP request to generate it again
+(exactly the same), and to parse again the response to send it back unchanged...
+</p>
+<p>
+<a href="http://jakarta.apache.org/~jfclere/webapp_docs/">mod_webapp</a> which is very easy to configure, has a well defined protocol named
+<a href="http://jakarta.apache.org/~jfclere/webapp_docs/warp.html">WARP</a> but it is no longer developped.
+BTW: It would be possible to implement the <b>WARP</b> protocol in
+<b>JK2</b> ;-))
+</p>
+</section>
+
+<section name="Additional References">
+<p>
+Please note that we maintain a list of references to external documentation
+on connector usage and configuration in various environments as part of
+the Tomcat wiki <a href="http://wiki.apache.org/jakarta-tomcat/Tomcat_2fLinks">Useful Links</a> page.
+</p>
+</section>
+
+</document>
diff --git a/connectors/jk/xdocs/jk/aphowto.xml b/connectors/jk/xdocs/jk/aphowto.xml
new file mode 100644
index 0000000..1a3263e
--- /dev/null
+++ b/connectors/jk/xdocs/jk/aphowto.xml
@@ -0,0 +1,985 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>Apache HowTo</title>
+<author email="hgomez@apache.org">Henri Gomez</author>
+<author email="shachor@il.ibm.com">Gal Shachor</author>
+<date>$Date$</date>
+</properties>
+
+<section name="Introduction">
+<p>
+This document explains how to connect Tomcat to the popular open source web server, Apache. 
+There is actually two version of Apache, 1.3 and 2.0 and both can be used with mod_jk, the Tomcat redirector
+module.
+</p>
+
+<p>
+It is recommanded that you also read the <a href="jk/workershowto.html">Workers HowTo</a> document
+to learn how to setup the working entities between your WebServer and Tomcat Engines.
+</p>
+
+<p>
+This document was originally part of <b>Tomcat: A Minimalistic User's Guide</b> written by Gal Shachor, 
+but has been split off for organizational reasons. 
+</p>
+
+<subsection name="Document Conventions and Assumptions">
+<p>
+${tomcat_home} is the root directory of tomcat. 
+Your Tomcat installation should have the following subdirectories:
+
+<ul>
+<li>
+${tomcat_home}\conf - Where you can place various configuration files
+</li>
+<li>
+${tomcat_home}\webapps - Containing example applications
+</li>
+<li>
+${tomcat_home}\bin - Where you place web server plugins
+</li>
+</ul>
+</p>
+<p>
+In all the examples in this document ${tomcat_home} will be <b>/var/tomcat3</b>.
+A <a href="jk/workershowto.html">worker</a> is defined to be a tomcat process that accepts work from the Apache server.
+</p>
+</subsection>
+
+<subsection name="Supported Configuration">
+<p>
+The mod_jk module was developed and tested on:
+<ul>
+<li>
+Linux, FreeBSD, AIX, HP-UX, MacOS X, and should works on major Unixes platforms supporting Apache 1.3 and/or 2.0
+</li>
+<li>
+WinNT4.0-i386 SP4/SP5/SP6a (should be able to work with other service packs), Win2K and WinXP and Win98
+</li>
+<li>
+Cygwin (until you have an apache server and autoconf/automake support tools)
+</li>
+<li>
+Netware
+</li>
+<li>
+iSeries V5R1 and V5R2 with Apache 2.0.39. Be sure to have the latest Apache PTF installed. 
+</li>
+<li>
+Tomcat 3.2.x, Tomcat 3.3.x, Tomcat 4.0.x, Tomcat 4.1.x and Tomcat 5
+</li>
+</ul>
+</p>
+
+<p>
+The redirector uses <b>ajp12</b> and <b>ajp13</b> to send requests to the Tomcat containers. There is also an option to use Tomcat in process, 
+more about the in-process mode can be found in the in process howto.
+</p>
+</subsection>
+
+<subsection name="Who support ajp protocols ?">
+<p>
+The ajp12 protocol is only available in Tomcat 3.2.x and 3.3.x.
+</p>
+
+<p>
+The <b>ajp12</b> has been <b>deprecated</b> with Tomcat 3.3.x and you should use instead 
+<b>ajp13</b> which is the only ajp protocol known by Tomcat 4.0.x, 4.1.x and 5.
+</p>
+
+<p>
+Of course Tomcat 3.2.x and 3.3.x also support ajp13 protocol.
+</p>
+
+<p>
+Others servlet engines such as <b>jetty</b> have support for ajp13 protocol
+</p>
+
+</subsection>
+
+<subsection name="How does it work ?">
+<p>
+In a nutshell a web server is waiting for client HTTP requests. 
+When these requests arrive the server does whatever is needed to serve the 
+requests by providing the necessary content.
+</p>
+
+<p>
+Adding a servlet container may somewhat change this behavior. 
+Now the web server needs also to perform the following:
+</p>
+
+<ul>
+<li>
+Load the servlet container adapter library and initialize it (prior to serving requests).
+</li>
+<li>
+When a request arrives, it needs to check and see if a certain request belongs to a servlet, 
+if so it needs to let the adapter take the request and handle it.
+</li>
+</ul>
+
+<p>
+The adapter on the other hand needs to know what requests it is going to serve, 
+usually based on some pattern in the request URL, and to where to direct these requests.
+</p>
+
+<p>
+Things are even more complex when the user wants to set a configuration that uses virtual hosts, 
+or when they want multiple developers to work on the same web server 
+but on different servlet container JVMs. 
+We will cover these two cases in the advanced sections. 
+</p>
+
+</subsection>
+
+</section>
+
+<section name="Obtaining mod_jk">
+<p>
+mod_jk can be obtained in two formats - binary and source.  
+Depending on the platform you are running your web server on, a binary version of mod_jk may be available.  
+</p>
+
+<p>
+It is recommended to use the binary version if one is available.  
+If the binary is not available, follow the instructions for building mod_jk from source.  
+The mod_jk source can be downloaded from a mirror
+<a href="http://jakarta.apache.org/site/sourceindex.cgi/">
+here</a>
+</p>
+
+<p>
+The binaries for mod_jk are now available, for several platforms, in a separate area as the Tomcat Binary Release.  
+The binaries are located in subdirectories by platform.  
+</p>
+
+<p>
+For some platforms, such as Windows, this is the typical way of obtaining mod_jk 
+since most Windows systems do not have C compilers.
+</p>
+
+<p>
+For others, the binary distribution of mod_jk offers simpler installation.
+</p>
+
+<p>
+For example JK 1.2.5 can be downloaded from a mirror
+<a href="http://jakarta.apache.org/site/binindex.cgi/">
+here</a> and contains binary version for a variety of
+operating systems for both Apache 1.3 and Apache 2.
+</p>
+
+</section>
+
+<section name="Installation">
+<p>
+mod_jk requires two entities:
+
+<ul>
+<li>
+<b>mod_jk.xxx</b> - The Apache module, depending on your operating system, it will be mod_jk.so, mod_jk.dll, mod_jk,nlm or 
+or QZTCJK.SRVPGM (see the build section).
+</li>
+<li>
+<b>workers.properties</b> - A file that describes the host(s) and port(s) used by the workers (Tomcat processes). 
+A sample workers.properties can be found under the conf directory.
+</li>
+</ul>
+</p>
+
+<p>
+Also as with other Apache modules, mod_jk should be first installed on the modules directory of your
+Apache webserver, ie : /usr/lib/apache and you should update your <b>httpd.conf</b> file.
+</p>
+
+
+<subsection name="Disabling old mod_jserv">
+<p>
+If you've previously configured Apache to use <b>mod_jserv</b>, remove any <b>ApJServMount</b> directives 
+from your httpd.conf. 
+</p>
+
+<p>If you're including <b>tomcat-apache.conf</b> or <b>tomcat.conf</b>, you'll want to remove them as well - 
+they are specific to <b>mod_jserv</b>.  
+</p>
+
+<p>
+The mod_jserv configuration directives are not compatible with mod_jk !
+</p>
+</subsection>
+
+<subsection name="Using Tomcat auto-configure"> 
+<p>
+The simplest way to configure Apache to use mod_jk is to turn on the Apache auto-configure setting 
+in Tomcat and put the following include directive at the end of your Apache httpd.conf file 
+(make sure you replace TOMCAT_HOME with the correct path for your Tomcat installation:
+</p>
+
+<screen>
+<note>To be added at the end of your httpd.conf</note>
+<read>Include /var/tomcat3/conf/jk/mod_jk.conf-auto</read>
+</screen>
+
+<p>
+This will tell Apache to use directives in the <b>mod_jk.conf-auto</b> file in the Apache configuration.  
+This file is created by enabling the Apache auto-configuration as described in the Tomcat documentation.
+</p>
+
+</subsection>
+
+<subsection name="Custom mod_jk configuration"> 
+<p>
+You should use custom configuration when :
+</p>
+<ul>
+<li>
+You couldn't use <b>mod_jk.conf-auto</b> since Tomcat engine isn't on the same machine that your Apache WebServer, 
+ie when you have an Apache in front of a Tomcat Farm.
+</li>
+<li>
+Another case for custom configuration is when your Apache is in front of many differents Tomcat engines,
+each one having it's own configuration, a general case in ISP hosting
+</li>
+<li>
+Also all Apache webmaster will retain custom configuration to be able to tune the settings
+to their real needs.
+</li>
+</ul>
+
+</subsection>
+
+<subsection name="Simple configuration example">
+<p>
+Here is a simple configuration:
+</p>
+
+<screen>
+<note># Load mod_jk module</note>
+<read>LoadModule    jk_module  libexec/mod_jk.so</read>
+<note># Declare the module for &lt;IfModule directive&gt;</note>
+<read>AddModule     mod_jk.c</read>
+<note># Where to find workers.properties</note>
+<read>JkWorkersFile /etc/httpd/conf/workers.properties</read>
+<note># Where to put jk logs</note>
+<read>JkLogFile     /var/log/httpd/mod_jk.log</read>
+<note># Set the jk log level [debug/error/info]</note>
+<read>JkLogLevel    info</read>
+<note># Select the log format</note>
+<read>JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "</read>
+<note># JkOptions indicate to send SSL KEY SIZE, </note>
+<read>JkOptions     +ForwardKeySize +ForwardURICompat -ForwardDirectories</read>
+<note># JkRequestLogFormat set the request format </note>
+<read>JkRequestLogFormat     "%w %V %T"</read>
+<note># Send servlet for context /examples to worker named worker1</note>
+<read>JkMount  /examples/servlet/* worker1</read>
+<note># Send JSPs  for context /examples to worker named worker1</note>
+<read>JkMount  /examples/*.jsp worker1</read>
+</screen>
+
+</subsection>
+</section>
+
+<section name="mod_jk Directives">
+<p>
+We'll discuss here the mod_jk directives and details behind them
+</p>
+
+<subsection name="Define workers">
+<p>
+<b>JkWorkersFile</b> specify the location where mod_jk will find the workers definitions.
+
+<screen>
+<read>JkWorkersFile     /etc/httpd/conf/workers.properties</read>
+</screen>
+
+<br/>
+<br/>
+</p>
+</subsection>
+
+<subsection name="Logging">
+<p>
+<b>JkLogFile</b> specify the location where mod_jk is going to place its log file.
+
+<screen>
+<read>JkLogFile     /var/log/httpd/mod_jk.log</read>
+</screen>
+
+<br/>
+<br/>
+</p>
+
+<p>
+<b>JkLogLevel</b>
+set the log level between :
+</p>
+
+<ul>
+<li>
+<b>info</b> log will contains standard mod_jk activity (default).
+</li>
+<li>
+<b>error</b> log will contains also error reports.
+</li>
+<li>
+<b>debug</b> log will contains all informations on mod_jk activity
+</li>
+</ul>
+
+<screen>
+<read>JkLogLevel    info</read>
+</screen>
+
+<p>
+<code>info</code> should be your default selection for normal operations.
+<br/>
+<br/>
+</p>
+
+<p>
+<b>JkLogStampFormat</b> will configure the date/time format found on mod_jk logfile. 
+Using the strftime() format string it's set by default to <b>"[%a %b %d %H:%M:%S %Y]"</b>
+</p>
+
+<screen>
+<read>JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "</read>
+</screen>
+
+<p>
+<br/>
+<br/>
+</p>
+
+<p>
+<b>JkRequestLogFormat</b> will configure the format of mod_jk individual request logging. 
+Request logging is configured and enabled on a per virtual host basis. 
+To enable request logging for a virtual host just add a JkRequestLogFormat config. 
+The syntax of the format string is similiar to the Apache LogFormat command, 
+here is a list of the available request log format options:
+</p>
+                       
+<p>
+<table>
+  <tr><th>Options</th><th>Description</th></tr>
+  <tr><td>%b</td><td>Bytes sent, excluding HTTP headers (CLF format)</td></tr>
+  <tr><td>%B</td><td>Bytes sent, excluding HTTP headers</td></tr>
+  <tr><td>%H</td><td>The request protocol</td></tr>
+  <tr><td>%m</td><td>The request method</td></tr>
+  <tr><td>%p</td><td>The canonical Port of the server serving the request</td></tr>
+  <tr><td>%q</td><td>The query string (prepended with a ? if a query string exists, otherwise an empty string)</td></tr>
+  <tr><td>%r</td><td>First line of request</td></tr>
+  <tr><td>%s</td><td>Request HTTP status code</td></tr>
+  <tr><td>%T</td><td>Request duration, elapsed time to handle request in seconds '.' micro seconds</td></tr>
+  <tr><td>%U</td><td>The URL path requested, not including any query string.</td></tr>
+  <tr><td>%v</td><td>The canonical ServerName of the server serving the request</td></tr>
+  <tr><td>%V</td><td>The server name according to the UseCanonicalName setting</td></tr>
+  <tr><td>%w</td><td>Tomcat worker name</td></tr>
+</table>
+
+<screen>
+<read>JkRequestLogFormat     "%w %V %T"</read>
+</screen>
+
+<br/>
+<br/>
+</p>
+
+</subsection>
+
+<subsection name="Forwarding">
+<p>
+The directive JkOptions allow you to set many forwarding options which will enable (+)
+or disable (-) following option.
+<br/>
+<br/>
+</p>
+
+<p>
+JkOptions <b>ForwardKeySize</b>,  you ask mod_jk, when using ajp13, to forward also the SSL Key Size  as 
+required by Servlet API 2.3.
+This flag shouldn't be set when servlet engine is Tomcat 3.2.x (on by default).
+
+<screen>
+<read>JkOptions     +ForwardKeySize</read>
+</screen>
+
+<br/>
+<br/>
+</p>
+
+<p>
+JkOptions <b>ForwardURICompat</b>, you told mod_jk to send the URI to Tomcat normally, 
+which is less spec compliant but mod_rewrite compatible, 
+use it for compatibility with Tomcat 3.2.x engines (on by default).
+
+<screen>
+<read>JkOptions     +ForwardURICompat</read>
+</screen>
+
+<br/>
+<br/>
+</p>
+
+<p>
+JkOptions <b>ForwardURICompatUnparsed</b>, the forwarded URI 
+is unparsed, it's spec compliant but broke mod_rewrite.
+
+<screen>
+<read>JkOptions     +ForwardURICompatUnparsed</read>
+</screen>
+
+<br/>
+<br/>
+</p>
+
+<p>
+JkOptions <b>ForwardURIEscaped</b>, the forwarded URI is escaped and 
+Tomcat (since 3.3 rc2) will do the decoding part.
+
+<screen>
+<read>JkOptions     +ForwardURIEscaped</read>
+</screen>
+
+<br/>
+<br/>
+</p>
+
+<p>
+JkOptions <b>ForwardDirectories</b> is used in conjunction with <b>DirectoryIndex</b> 
+directive of Apache web server. As such mod_dir should be available to Apache,
+statically or dynamically (DSO)
+<br/>
+<br/>
+</p>
+
+<p>
+When DirectoryIndex is configured, Apache will create sub-requests for
+each of the local-url's specified in the directive, to determine if there is a
+local file that matches (this is done by stat-ing the file).
+</p>
+
+<p>
+If ForwardDirectories is set to false (default) and Apache doesn't find any
+files that match, Apache will serve the content of the directory (if directive
+Options specifies Indexes for that directory) or a <code>403 Forbidden</code> response (if
+directive Options doesn't specify Indexes for that directory).
+</p>
+
+<p>
+If ForwarDirectories is set to true and Apache doesn't find any files that
+match, the request will be forwarded to Tomcat for resolution. This is used in
+cases when Apache cannot see the index files on the file system for various
+reasons: Tomcat is running on a different machine, the JSP file has been
+precompiled etc. 
+</p>
+
+<p>Note that locally visible files will take precedence over the
+ones visible only to Tomcat (i.e. if Apache can see the file, that's the one
+that's going to get served). This is important if there is more then one type of
+file that Tomcat normally serves - for instance Velocity pages and JSP pages.
+
+<screen>
+<read>JkOptions     +ForwardDirectories</read>
+</screen>
+<br/>
+<br/>
+</p>
+
+<p>
+The directive <b>JkEnvVar</b> allow you to forward an environment vars from Apache server to Tomcat engine.
+
+<screen>
+<read>JkEnvVar     SSL_CLIENT_V_START</read>
+</screen>
+<br/>
+<br/>
+</p>
+
+</subsection>
+
+<subsection name="Assigning URLs to Tomcat">
+<p>
+If you have created a custom or local version of mod_jk.conf-local as noted above, 
+you can change settings such as the workers or URL prefix.
+</p>
+
+<p>
+<b>JkMount</b> directive assign specific URLs to Tomcat. 
+In general the structure of a JkMount directive is:
+</p>
+
+<source>JkMount [URL prefix] [Worker name]</source>
+
+<screen>
+<note># send all requests ending in .jsp to worker1</note>
+<read>JkMount /*.jsp worker1</read>
+<note># send all requests ending /servlet to worker1</note>
+<read>JkMount /*/servlet/ worker1</read>
+<note># send all requests jsp requests to files located in /otherworker will go worker2</note>
+<read>JkMount /otherworker/*.jsp worker2</read>
+</screen>
+
+<p>
+You can use the JkMount directive at the top level or inside &lt;VirtualHost&gt; sections of your httpd.conf file.
+</p>
+</subsection>
+
+<subsection name="Configuring Apache to serve static web application files">
+<p>
+If the Tomcat Host appBase (webapps) directory is accessible by the Apache web server, 
+Apache can be configured to serve web application context directory static files instead 
+of passing the request to Tomcat.
+</p>
+
+<p>
+Caution: If Apache is configured to serve static pages for a web application it bypasses 
+any security contraints you may have configured in your web application web.xml config file.
+</p>
+
+<p>Use Apache's <b>Alias</b> directive to map a single web application context directory into Apache's 
+document space for a VirtualHost:
+</p>
+
+<screen>
+<note># Static files in the examples webapp are served by apache</note>
+<read>Alias /examples /vat/tomcat3/webapps/examples</read>
+<note># The following line prohibits users from directly access WEB-INF</note>
+<read>&lt;Location "/examples/WEB-INF/"&gt;</read>
+<read>AllowOverride None</read>
+<read>deny from all</read>
+<read>&lt;Location&gt;</read>
+<note># All JSP will goes to worker1</note>
+<read>JkMount /*.jsp worker1</read>
+<note># All servlets goes to worker1</note>
+<read>JkMount /*/servlet/ worker1</read>
+</screen>
+
+<p>
+Starting with mod_jk 1.2.6, and under Apache 2.0, it's possible to exclude some URL/URI from
+jk processing by setting the env var <b>no-jk</b>, for example with the SetEnvIf Directive.
+</p>
+
+<p>
+You could use <b>no-jk</b> env var to fix problem with mod_alias or mod_userdir
+directive when jk and alias/userdir URLs matches. 
+</p>
+
+<screen>
+<note># All URL goes to tomcat except the one containing /home</note>
+<read>&lt;VirtualHost *:80&gt;</read>
+<read> ServerName testxxx.mysys</read>
+<read> DocumentRoot /www/testxxx/htdocs</read>
+<read />
+<note># Use SetEnvIf to st no-jk when /home/ is encountered</note>
+<read>SetEnvIf Request_URI "/home/*" no-jk</read>
+<read />
+<note># Now /home will goes to /home/dataxxx/</note>
+<read>Alias /home /home/dataxxx/</read>
+<read />
+<read>&lt;Directory "/home/dataxxx"&gt;</read>
+<read>  Options Indexes MultiViews</read>
+<read>  AllowOverride None</read>
+<read>  Order allow,deny</read>
+<read>  Allow from all</read>
+<read>&lt;/Directory&gt;</read>
+<read />
+<read>JkMount /* myssys-xxx</read>
+<read />
+<read>&lt;/VirtualHost&gt;</read>
+</screen>
+
+
+<p>
+Use the mod_jk <b>JkAutoAlias</b> directive to map all web application context directories 
+into Apache's document space. 
+</p>
+
+<p>
+Attempts to access the WEB-INF or META-INF directories within a web application context 
+or a Web Archive *.war within the Tomcat Host appBase (webapps) directory will fail with an 
+<code>HTTP 403, Access Forbidden</code>
+</p>
+
+<screen>
+<note># Static files in all Tomcat webapp context directories are served by apache</note>
+<read>JkAutoAlias /var/tomcat3/webapps</read>
+<read>JkMount /*.jsp ajp13</read>
+<read>JkMount /*/servlet/ ajp13</read>
+</screen>
+
+</subsection>
+</section>
+
+<section name="Building mod_jk on Unix">
+<p>
+The mod_jk build use the widely used configure system.
+</p>
+<subsection name="Prepare your mod_jk configure from CVS">
+In case you get source from CVS, ie without an existing configure script,
+you should have autoconf for configuration and installation.
+<p>
+To create jakarta-tomcat-connectors's autoconf script, you will need libtool 1.3.3 or higher, 
+and autoconf 2.13 or newer.
+</p><p>
+Those tools will not be required if you are just using a package downloaded from apache.org, 
+they are only required for developers.
+</p>
+<p>
+To create the configure script just type :
+
+<screen>
+<type>./buildconf.sh</type>
+</screen>
+</p>
+</subsection>
+
+<subsection name="Using configure to build mod_jk">
+<p>Here's how to use configure to prepare mod_jk for building, just type: 
+<source>
+./configure [autoconf arguments] [jakarta-tomcat-connectors arguments]
+</source>
+</p>
+
+<p>
+You could set <b>CFLAGS</b> and <b>LDFLAGS</b> to add some platform specifics:
+</p>
+
+<screen>
+<type>LDFLAGS=-lc ./configure -with-apxs=/home2/local/apache/bin/apxs</type>
+</screen>
+
+<p>
+If you want to build mod_jk for Apache 1.3 and 2.0, you should 
+<ul>
+<li>
+use configure and indicate Apache 1.3 apxs location (--with-apxs)
+</li>
+<li>
+use make
+</li>
+<li>
+copy the mod_jk binary to the apache modules location
+</li>
+<li>
+make clean (to remove all previously compiled modules)
+</li>
+<li>
+use configure and indicate Apache 2.0 apxs location,
+</li>
+<li>
+then make.
+</li>
+</ul>
+
+</p>
+</subsection>
+
+<subsection name="configure arguments">
+<p>
+<table>
+  <tr><th>Apache related parameters</th><th></th></tr>
+  <tr>
+  <td>--with-apxs[=FILE]</td>
+  <td>FILE is the location of the apxs tool. Default is finding apxs in PATH.
+It builds a shared Apache module. It detects automaticly the Apache version.
+(2.0 and 1.3)</td>
+  </tr>
+  <tr><td>--with-apache=DIR</td>
+  <td>DIR is the path where apache sources are located.
+The apache sources should have been configured before configuring mod_jk.
+DIR is something like: /home/apache/apache_1.3.19
+It builds a static Apache module.</td>
+  </tr>
+  <tr><td>--enable-EAPI</td>
+  <td>This parameter is needed when using Apache-1.3 and mod_ssl, otherwise you will get the error message:
+"this module might crash under EAPI!" when loading mod_jk.so in httpd.
+Not needed when --with-apxs has been used</td>
+</tr>
+</table>
+<br/>
+<table>
+  <tr><th>JNI related parameters</th><th></th></tr>
+  <tr><td>--enable-jni</td>
+  <td>Build the JNI worker and so the build process will require 
+some informations about your Java Environment</td>
+  </tr>
+  <tr><td>--with-java-home=DIR</td>
+  <td>DIR is the  patch to the JDK root directory. Something like: /opt/java/jdk12</td>
+  </tr>
+  <tr><td>--with-os-type=SUBDIR</td><td>SUBDIR is the os-type subdirectory, 
+  configure should guess it correctly.</td>
+  </tr>
+  <tr><td>--with-arch-type=SUBDIR</td><td>SUBDIR is the arch subdirectory, 
+  configure should guess it correctly.</td>
+  </tr>
+  <tr><td>--with-java-platform=VAL</td><td>VAL is the Java platform 1 is 1.1.x and 2 is for 1.2 anf higher, 
+  configure should guess it correctly.</td>
+  </tr>
+</table>
+</p>
+</subsection>
+
+<subsection name="Examples of configure use">
+
+<screen>
+<note>Apache 1.3 and 2.0 build</note>
+<type>./configure --with-apxs=/usr/sbin/apxs</type><br/>
+<type>make</type><br/>
+<type>cp ./apache-1.3/mod_jk.so /usr/lib/apache</type><br/>
+<type>make clean</type><br/>
+<type>./configure --with-apxs=/usr/sbin/apxs2</type><br/>
+<type>make</type><br/>
+<type>cp ./apache-2.0/mod_jk.so /usr/lib/apache2</type><br/>
+</screen>
+
+<screen>
+<note>Apache 2.0 build with JNI support</note>
+<type>./configure --with-apxs=/opt/apache2/bin/apxs \</type>
+<typenext>--with-java-home=${JAVA_HOME} --with-java-platform=2 \</typenext>
+<typenext>--enable-jni</typenext><br/>
+</screen>
+
+<screen>
+<note>Apache 1.3 build without JNI support</note>
+<type>./configure --with-apxs=/usr/sbin/apxs</type><br/>
+</screen>
+
+</subsection>
+
+</section>
+
+<section name="Building mod_jk for Apache on Windows NT/2K/XP">
+<p>
+The module was developed using Visual C++ version 6.0, so having this environment is a prerequisite 
+if you want to perform a custom build.
+</p>
+<p>
+The steps that you need to take are:
+</p>
+<ul>
+<li>
+Change directory to the apache 1.3 or apache 2.0 source directory depending on your version of Apache.
+</li>
+<li>
+If you want to build mod_jk for Apache 1.3, set an <b>APACHE1_HOME</b> environment variable which points 
+to where your Apache 1.3 is installed.
+A mod_jk module for Apache 2.0 build will require <b>APACHE2_HOME</b> environment variable to be set. 
+</li>
+<li>
+Copy mod_jk.dll to Apache's modules directory.
+</li>
+</ul>
+<p>
+An example on how to build mod_jk for Apache 1.3:
+</p>
+<screendos>
+<notedos>Set location for Apache 1.3 sources</notedos>
+<typedos>set APACHE1_HOME=c:\apache13</typedos>
+<notedos>Change directory to the mod_jk module for Apache 1.3</notedos>
+<typedos>cd c:\home\apache\jk\native\apache-1.3</typedos>
+<notedos>Build the sources using MSDEV</notedos>
+<typedos>MSDEV mod_jk.dsp /MAKE ALL</typedos>
+<notedos>Copy the dll to your apache modules directory</notedos>
+<typedos>cp release\mod_jk.dll c:\apache13\modules\</typedos>
+</screendos>
+
+<p>
+An example on how to build mod_jk for Apache 2.0:
+</p>
+<screendos>
+<notedos>Set location for Apache 2.0 sources</notedos>
+<typedos>set APACHE2_HOME=c:\apache20</typedos>
+<notedos>Change directory to the mod_jk module for Apache 2.0</notedos>
+<typedos>cd c:\home\apache\jk\native\apache-2.0</typedos>
+<notedos>Build the sources using MSDEV</notedos>
+<typedos>MSDEV mod_jk.dsp /MAKE ALL</typedos>
+<notedos>Copy the dll to your apache modules directory</notedos>
+<typedos>cp release\mod_jk.dll c:\apache20\modules\</typedos>
+</screendos>
+
+<p>
+If msdev is not in your path, enter the full path to msdev.exe. 
+Also, ApacheCore.lib is expected to exist in the <b>${APACHEX_HOME}\src\CoreD</b> and 
+<b>${APACHEX_HOME}\src\CoreR</b> directories before linking will succeed.
+You will need to build enough of the Apache source to create these libraries.
+This will build both release and debug versions of the redirector plug-in (mod_jk).
+An alternative will be to open mod_jk.dsp in msdev and build it using the build menu.
+</p>
+</section>
+
+<section name="Building mod_jk for Apache on iSeries/OS400">
+<p>
+Since OS400 V4R5, iSeries (AS/400) has used Apache 2.0 as their primary web server, 
+replacing the old IBM webserver.
+It's now possible to build mod_jk on iSeries thanks to the help of the IBM 
+Rochester Labs which has provided information and patches
+to adapt mod_jk to OS400.
+</p>
+<p>
+You should have at least Apache 2.0.39, a C Compiler and IFS.
+Apache 2.0.39 is provided with the most recent set of PTFs for the iSeries Apache
+server, which can be found at <a href="http://www.ibm.com/servers/eserver/iseries/software/http/">
+http://www.ibm.com/servers/eserver/iseries/software/http/</a>
+</p>
+
+<p>
+To configure mod_jk on iSeries use the CL source provided with the mod_jk source.
+</p>
+<ul>
+<li>
+Get the latest mod_jk source and untar it on a Windows or Unix boxes
+</li>
+<li>
+Create a directory in IFS, ie /home/apache
+</li>
+<li>
+Send the whole jk source directory to iSeries directory via FTP.
+</li>
+<li>
+Then go to the iSeries command line :
+</li>
+</ul>
+<screen5250>
+<note5250>Create mod_jk library</note5250>
+<type5250>CRTLIB MOD_JK TEXT(‘Apache mod_jk tomcat connector module’)</type5250>
+<note5250>Create service program source file</note5250>
+<type5250>CRTSRCPF MOD_JK/QSRVSRC TEXT(‘Service program source file’)</type5250>
+<note5250>Create the CL build program source file</note5250>
+<type5250>CRTSRCPF FILE(MOD_JK/QCLSRC) TEXT(‘Build program source file’)</type5250>
+<note5250>Edit the service program source file</note5250>
+<type5250>STRSEU MOD_JK/QSRVSRC MOD_JK</type5250>
+</screen5250>
+<p>
+In the edited file, specify that only jk_module should be exported :
+<screen5250>
+<type5250next> Columns   . . :    1  71     Edit                               MOD_JK/QSRVSRC </type5250next>
+<type5250next> SEU==>                                                                  MOD_JK </type5250next>
+<type5250next>        *************** Beginning of data ************************************* </type5250next>
+<type5250next>0001.00 STRPGMEXP PGMLVL(*CURRENT)                                              </type5250next>
+<type5250next>0002.00 EXPORT SYMBOL("jk_module")                                              </type5250next>
+<type5250next>0003.00 ENDPGMEXP                                                               </type5250next>
+<type5250next>        ****************** End of data **************************************** </type5250next>        
+</screen5250>
+</p>
+<p>
+You could start to build all the modules of mod_jk :
+</p>
+<screen5250>
+<note5250>Copy the CL build program source from IFS</note5250>
+<type5250>CPYFRMSTMF FROMSTMF('/home/apache/jk/native/apache-2.0/bldjk.qclsrc') +</type5250>
+<type5250next>TOMBR('/QSYS.LIB/MOD_JK.LIB/QCLSRC.FILE/BLDJK.MBR') MBROPT(*REPLACE)</type5250next>
+<note5250>Build the CL build program</note5250>
+<type5250>CRTCLPGM PGM(MOD_JK/BLDJK) SRCFILE(MOD_JK/QCLSRC) TEXT('Apache mod_jk build program')</type5250>
+<note5250>Launch the build</note5250>
+<type5250>CALL MOD_JK/BLDJK</type5250><br/>
+<note5250>If the build if successfull, copy the new mod_jk module</note5250>
+<type5250>CRTDUPOBJ OBJ(MOD_JK) FROMLIB(MOD_JK) OBJTYPE(*SRVPGM) TOLIB(QHTTPSVR) NEWOBJ(MOD_JK)</type5250>
+</screen5250>
+<p>
+Next, you should restart your Apache 2.0 server and enjoy this piece of OpenSource on iSeries.
+</p>
+</section>
+
+<section name="Building mod_jk for Apache on MacOS/X">
+<p>
+Mac OS X (10.2.x) build notes :
+</p>
+<p>
+Assuming that you are root :
+</p>
+<screen>
+<note>For Apache 1.3:</note>
+<type>./configure --with-apxs=/usr/sbin/apxs</type>
+<type>cd apache-1.3</type>
+<type>make -f Makefile.apxs</type>
+<type>cp mod_jk.so /etc/libexec/httpd</type>
+
+<note>For Apache 2.0:</note>
+<type>./configure --with-apxs=/usr/local/apache2/bin/apxs</type>
+<note>(you should point to the directory where you installed Apache 2.0)</note>
+<type>cd apache-2.0</type>
+<type>make -f Makefile.apxs install</type>
+</screen>
+</section>
+
+<section name="Getting mod_jk linked statically with Apache">
+<p>
+mod_jk allows to install mod_jk in the Apache source tree to get a statically
+linked mod_jk. Having mod_jk in the httpd executable brings some performance
+improvements. The configure option --with-apache prepare mod_jk to install it
+in the Apache source tree.
+The option --with-apache works both for Apache-1.3 and Apache-2.0.
+The examples below show how to get mod_jk in the httpd process.
+</p>
+
+<subsection name="Installation in Apache-2.0">
+<screen>
+<note> /home/apache20/httpd-2.0.43 is the directory where the httpd-2.0 sources
+are located. </note>
+<type>./configure --with-apache=/home/apache20/httpd-2.0.43</type><br/>
+<type>make</type><br/>
+<note>Install the mod_jk library and other files in
+/home/apache20/httpd-2.0.43/modules: </note>
+<type>make install</type><br/>
+<note> It is not possible to configure Apache directly because the config.m4 of mod_jk must
+be added to the configure of httpd-2.0. </note>
+<type>cd /home/apache20/httpd-2.0.43</type>
+<type>sh buildconf</type>
+<type>configure ... --with-mod_jk</type>
+<type>make</type>
+<type>make install</type><br/>
+</screen>
+<p>
+The enable-jk=share and enable-jk=static are not supported. --with-mod_jk only
+allow static linking of mod_jk.
+</p>
+</subsection>
+
+<subsection name="Installation in Apache-1.3">
+<screen>
+<note> /home/apache/apache_1.3.27 is the directory where the apache-1.3 sources
+are located. </note>
+<type>./configure --with-apache=/home/apache/apache_1.3.27</type><br/>
+<type>make</type><br/>
+<note>Install the libjk library, mod_jk.c, includes and other files in
+/home/apache/apache_1.3.27/src/modules/jk: </note>
+<type>make install</type><br/>
+<note> Configure in the Apache sources: </note>
+<type>cd /home/apache/apache_1.3.27</type>
+<type>configure ... --enable-module=dir --disable-shared=dir \</type>
+<typenext>              --activate-module=src/modules/jk/libjk.a \</typenext>
+<typenext>              --disable-shared=jk</typenext>
+<type>make</type>
+<type>make install</type><br/>
+</screen>
+<p>
+The --enable-shared=jk is also working and builds a dso file.
+</p>
+<screen>
+<note> Just change the configure in the Apache sources: </note>
+<type>configure ... --enable-module=dir --enable-shared=dir \</type>
+<typenext>              --activate-module=src/modules/jk/libjk.a \</typenext>
+<typenext>              --enable-shared=jk</typenext>
+</screen>
+</subsection>
+
+</section>
+
+</document>
diff --git a/connectors/jk/xdocs/jk/domhowto.xml b/connectors/jk/xdocs/jk/domhowto.xml
new file mode 100644
index 0000000..addb65f
--- /dev/null
+++ b/connectors/jk/xdocs/jk/domhowto.xml
@@ -0,0 +1,528 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>Domino HowTo</title>
+<author email="andy@tagish.com">Andy Armstrong</author>
+<author email="hgomez@apache.org">Henri Gomez</author>
+<date>$Date$</date>
+</properties>
+
+<section name="Introduction">
+<p><b>Important Note:</b> The JK based connector described here has been superceded by a JK2 based connector
+that works with all current Tomcat versions. Use of this connector has been deprecated. Go
+<a href="jk2/installhowto.html">here</a> to find out how to install the new connector.</p>
+
+<p>
+This document explains how to set up Domino to cooperate with Tomcat. 
+</p>
+
+<p>
+It is recommended that you also read the <a href="jk/workershowto.html">Workers HowTo</a> document
+to learn how to setup the working entities between your WebServer and Tomcat Engines.
+</p>
+
+<p>
+Recent versions of the Lotus Domino web server have had the ability to host Java servlets, 
+but at the time of writing the Domino servlet container uses JDK 1.2.2 and it is not (apparently) 
+possible to replace this with JDK 1.3. 
+</p>
+
+<p>
+That means if you want to use JAAS or any other API 
+that is JDK 1.3 only in your servlets you're stuck. 
+</p>
+
+<subsection name="Document Conventions and Assumptions">
+<p>
+${tomcat_home} is the root directory of tomcat. 
+Your Tomcat installation should have the following subdirectories:
+
+<ul>
+<li>
+${tomcat_home}\conf - Where you can place various configuration files
+</li>
+<li>
+${tomcat_home}\webapps - Containing example applications
+</li>
+<li>
+${tomcat_home}\bin - Where you place web server plugins
+</li>
+</ul>
+</p>
+<p>
+In all the examples in this document ${tomcat_home} will be <b>c:\jakarta-tomcat</b>.
+A worker is defined to be a tomcat process that accepts work from the Domino server.
+</p>
+</subsection>
+
+
+<subsection name="Supported Configuration">
+<p>
+The Domino Tomcat redirector was developed and tested on:
+<ul>
+<li>
+WinNT4.0-i386 SP6a (it should be able to work on other versions of the NT service pack.) and Win2K Professional
+</li>
+<li>
+RedHat Linux 7
+</li>
+<li>
+Lotus Domino 5.0.6a
+</li>
+<li>
+Tomcat 3.2.x, Tomcat 3.3.x, Tomcat 4.0.x, Tomcat 4.1.x and Tomcat 5
+</li>
+</ul>
+</p>
+
+<p>
+The redirector uses <b>ajp12</b> and <b>ajp13</b> to send requests to the Tomcat containers.
+It probably also works with Tomcat in process, but that hasn't been tested.
+</p>
+</subsection>
+
+<subsection name="Who support ajp protocols ?">
+<p>
+The ajp12 protocol is only available in Tomcat 3.2.x and 3.3.x.
+</p>
+
+<p>
+The <b>ajp12</b> has been <b>deprecated</b> with Tomcat 3.3.x and you should use instead 
+<b>ajp13</b> which is the only ajp protocol known by Tomcat 4.0.x, 4.1.x and 5.
+</p>
+
+<p>
+Of course Tomcat 3.2.x and 3.3.x also support ajp13 protocol.
+</p>
+
+<p>
+Others servlet engines such as <b>jetty</b> have support for ajp13 protocol
+</p>
+
+</subsection>
+</section>
+
+<section name="Installation on Windows">
+<p>
+The Tomcat redirector requires 3 entities:
+</p>
+
+<ul>
+<li>
+tomcat_redirect.dll - The Domino plugin; either obtain a pre-built DLL or build it yourself 
+(see the build section).
+</li>
+<li>
+workers.properties - A file that describes the host(s) and port(s) used by the workers (Tomcat processes). 
+A sample workers.properties can be found under the conf directory.
+</li>
+<li>
+tomcat_redirector.reg - Registry entries
+</li>
+</ul>
+
+<p>
+We'll assume that tomcat redirector is placed in <b>c:\jk\lib\tomcat_redirector.dll</b>, 
+the properties file is in<b>c:\jk\conf</b>
+and you created a log directory <b>c:\jk\logs</b>
+</p>
+
+<p>
+Copy the file <b>tomcat_redirector.dll</b> to the Domino program directory 
+(this is the directory, which may be called something like <b>c:\Lotus\Domino</b>, that contains a file called 
+<b>nlnotes.exe</b>). 
+</p>
+
+<screendos>
+<notedos>Copy redirector dll to Domino program directory</notedos>
+<typedos>copy c:\jk\lib\tomcat_redirector.dll c:\Lotus\Domino</typedos>
+</screendos>
+
+<p>
+Shortly we will tell Domino where to find this file, but before we do that we need to make some registry entries. 
+The simplest way is to edit the supplied file <b>tomcat_redirector.reg</b>, which initially will look like this :
+</p>
+
+<screen>
+<read>REGEDIT4</read>
+<read/>
+<read>[HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Dsapi Redirector\1.0]</read>
+<read>"log_file"="c:\\jk\\logs\\domino.log"</read>
+<read>"log_level"="debug"</read>
+<read>"worker_file"="c:\\jk\\conf\\workers.properties"</read>
+<read>"worker_mount_file"="c:\\jk\\conf\\uriworkermap.properties"</read>
+<read>"tomcat_start"="c:\\jakarta-tomcat\\bin\\tomcat.bat start"</read>
+<read>"tomcat_stop"="c:\\jakarta-tomcat\\bin\\tomcat.bat stop"</read>
+</screen>
+
+<p>
+Edit this file to reflect the location where Tomcat has been installed, i.e. replace the instances 
+of <b>c:\\jakarta-tomcat</b> and <b>c:\\jk</b> with the appropriate path remembering to <b>retain the double backslashes</b>. 
+</p>
+
+<p>
+Once you've made the necessary changes save this file and double click on it to enter it into the registry.
+</p>
+
+<p>
+Note that the files referred to by the worker_file and worker_mount_file keys need to exist and contain sane values. 
+Default Tomcat installations will have these files. Note also that recent versions of Tomcat write a file called uriworkermap.properties-auto when they start up that can be renamed uriworkermap.properties to obtain default behaviour.
+</p>
+
+<subsection name="Note for Windows 2000 users">
+<p>
+For some reason Windows 2000 seems to have a problem resolving the references to localhost 
+in the default workers.properties. 
+</p>
+
+<p>
+The easiest solution is to replace 'localhost' with '127.0.0.1' everywhere it appears.
+</p>
+</subsection>
+
+<subsection name="Automatically Starting Tomcat">
+<p>
+The last two registry entries above provide commands that the redirector DLL will use to 
+start and stop Tomcat when the Domino http server starts and stops respectively. 
+</p>
+
+<p>
+If you don't require this behaviour these two lines can be omitted 
+(or deleted if you've already placed them in the registry).
+</p>
+</subsection>
+
+<subsection name="Configuring Domino">
+<p>
+Finally we need to configure Domino to use the DSAPI extension DLL. 
+For those who are unfamiliar with Domino server configuration most of a server's configurable behavior 
+is dictated by a document called the "server document" in a database called the 
+<b>"Public Name and Address Book"</b> or <b>"NAB"</b> for short
+</p>
+<p>
+(N.B. Lotus have renamed the NAB to "Domino Directory" from Domino 5 onwards). 
+Each Domino server will have a NAB (called names.nsf) and each NAB will have a number of server documents 
+including one for the current server. 
+</p>
+
+<p>
+If you have not previously configured a Domino server you may need to refer to the supplied documentation, 
+or you may need to pass this document to your tame Domino administrator.
+</p>
+
+<p>
+Assuming you know your way around a Domino server document what we're going to do is actually quite simple. 
+Open the server document for this server, place it in Edit mode, then locate the DSAPIsection and the 'DSAPI filter file names' field on the Internet Protocols tab, HTTP sub-tab. Add "tomcat_redirector.dll" to the DSAPI field, then save and close the document.
+</p>
+</subsection>
+
+<subsection name="Restart Domino">
+<p>
+In order to get these settings to take effect and make sure that you haven't disrupted anything 
+else you should now restart the Domino server. 
+</p>
+
+<p>If the server is running as a service and you have changed any relevant system variables 
+(JAVA_HOME, TOMCAT_HOME, CLASSPATH) since the last time you restarted the computer you should 
+do a complete restart now because updates to system variables are not seen by services until after a reboot. 
+</p>
+
+<p>
+If all goes well you should see something like this on the server console when the web server starts up :
+</p>
+
+<screen>
+<read>29/05/2001 18:54:13   JVM: Java Virtual Machine initialized.</read>
+<read>29/05/2001 18:54:14   Attempting to start Tomcat: c:\jakarta-tomcat\bin\tomcat.bat start</read>
+<read>Including all jars in c:\jakarta-tomcat\lib in your CLASSPATH.</read>
+<read/>
+<read>Using CLASSPATH: c:\jakarta-tomcat\classes;c:\jakarta-tomcat\lib\ant.jar;c:\jakarta-tomcat\lib\servlet.jar</read>
+<read/>
+<read>Starting Tomcat in new window</read>
+<read>29/05/2001 18:54:15   Apache Tomcat Interceptor (Jakarta/DSAPI/1.0) loaded</read>
+<read>29/05/2001 18:54:16   HTTP Web Server started</read>
+</screen>
+
+<p>
+At about the same time Tomcat should open in a new window (assuming you enabled the autostart option in the registry settings). 
+</p>
+
+<p>
+You should now be able to visit a URL that is handled by Tomcat. 
+Something like may be available, depending on how Tomcat is configured :
+</p>
+
+<source>
+http://name of server/servlet/SnoopServlet
+</source>
+
+<p>
+If that all works you're done ;-) 
+</p>
+
+</subsection>
+
+</section>
+
+<section name="Installation on Linux">
+<p>
+The Tomcat redirector requires 3 entities:
+</p>
+
+<ul>
+<li>
+libtomcat.so - The Domino plugin; either obtain a pre-built shared lib or build it yourself 
+(see the build section).
+</li>
+<li>
+workers.properties - A file that describes the host(s) and port(s) used by the workers (Tomcat processes). 
+A sample workers.properties can be found under the conf directory.
+</li>
+<li>
+libtomcat.ini - configuration entries
+</li>
+</ul>
+
+<p>
+Copy the file <b>libtomcat.so</b> to the Domino program directory which may be called 
+something like <b>/opt/lotus/notes/5601/linux</b>, it should contains a file called <b>libnotes.so</b>
+and copy <b>libtomcat.ini</b> to the Domino data directory.
+</p>
+
+<screen>
+<note>Copy redirector shared lib to Domino program directory</note>
+<type>cp c:\jk\lib\libtomcat.so /opt/lotus/notes/5601/linux</type>
+<note>Copy config to Domino data directory</note>
+<type>cp c:\jk\conf\libtomcat.ini /opt/datalotus</type>
+</screen>
+
+
+<p>
+Note that if you're building the redirector from source these files should already 
+have been copied to the appropriate locations. 
+</p>
+
+<p>
+Before using the redirector you may like to review the settings in <b>libtomcat.ini</b> which, 
+by default, will look something like this:
+</p>
+
+<screen>
+<read>log_file=/var/log/domino.log</read>
+<read>log_level=debug</read>
+<read>worker_file=/var/tomcat3/conf/workers.properties</read>
+<read>worker_mount_file=/var/tomcat3/conf/uriworkermap.properties</read>
+<read>tomcat_start=/var/tomcat3/bin/tomcat.sh start</read>
+<read>tomcat_stop=/var/tomcat3/bin/tomcat.sh stop</read>
+</screen>
+
+<p>
+If you're building the redirector from the source you may not need to make any changes, 
+otherwise you may have to edit this file to reflect the location where Tomcat has been installed, 
+i.e. replace the instances of /usr/local/apache/tomcat with the appropriate path.
+</p>
+
+<p>
+Note that the files referred to by the <b>worker_file</b> and <b>worker_mount_file</b> keys need to exist 
+and contain sane values. 
+</p>
+
+<p>
+Default Tomcat installations will have these files. 
+Note also that recent versions of Tomcat write a file called <b>uriworkermap.properties-auto</b> 
+when they start up that can be renamed <b>uriworkermap.properties</b> to obtain default behaviour.
+</p>
+
+<subsection name="Automatically Starting Tomcat">
+<p>
+The last two registry entries above provide commands that the redirector will use to start and stop 
+Tomcat when the Domino http server starts and stops respectively. 
+</p>
+
+<p>
+If you don't require this behaviour these two lines can be deleted.
+</p>
+</subsection>
+
+
+<subsection name="Configuring Domino">
+<p>
+Finally we need to configure Domino to use the DSAPI extension. 
+</p>
+
+<p>
+For those who are unfamiliar with Domino server configuration most of a server's 
+configurable behavior is dictated by a document called the "server document" in a database called the 
+<b>"Public Name and Address Book"</b> or <b>"NAB"</b> for short
+</p>
+
+<p>
+N.B. Lotus have renamed the NAB to "Domino Directory" from Domino 5 onwards). 
+</p>
+
+<p>
+Each Domino server will have a NAB (called names.nsf) and each NAB will have a number of server documents 
+including one for the current server. 
+
+If you have not previously configured a Domino server you may need to refer to the supplied documentation, 
+or you may need to pass this document to your tame Domino administrator.
+</p>
+
+<p>
+Assuming you know your way around a Domino server document what we're going to do is actually quite simple. 
+</p>
+
+<p>
+Open the server document for this server, place it in Edit mode, then locate the 
+<b>DSAPIsection</b> and the <b>'DSAPI filter file names'</b> field on the Internet Protocols tab, 
+HTTP sub-tab. 
+</p>
+
+<p>
+Add <b>"libtomcat.so"</b> to the DSAPI field, then save and close the document.
+</p>
+
+</subsection>
+
+<subsection name="Restart Domino">
+<p>
+In order to get these settings to take effect and make sure that you haven't 
+disrupted anything else you should now restart the Domino HTTP server. 
+</p>
+
+<p>
+At the Domino console type
+</p>
+
+<screen>
+<type>tell http quit</type>
+<type>load http</type>
+</screen>
+
+<p>
+You should see the HTTP server reload along with messages that will confirm that the redirector 
+has loaded and that Tomcat has (if you used Tomcat autostart) started. 
+</p>
+
+<p>
+You should now be able to visit a URL that is handled by Tomcat. Something like
+</p>
+
+<source>
+http://name of server/servlet/SnoopServlet
+</source>
+
+may be available, depending on how Tomcat is configured.
+
+</subsection>
+
+</section>
+
+<section name="Building for Windows">
+<p>
+To compile it you'll need the JK Domino sources and Microsoft Visual C++ 6.0. 
+</p>
+
+<p>
+You will probably also want the Lotus Notes C API version 5.0.7 or later.
+You can build the DLL without the C API, in which case you'll need to define the macro NO_CAPI in config.h. 
+If you do this Domino logging from the DLL will be disabled.
+</p>
+
+<ul>
+<li>
+Change directory to the domino plugin source directory.
+</li>
+<li>
+Edit <b>dsapi.dsp</b> and update the include and library path to reflect your own Domino server installation 
+(search for a <b>/I compiler</b> option and <b>/libpath</b> linker option)
+</li>
+<li>
+Make the source with MSDEV
+</li>
+</ul>
+
+<screendos>
+<notedos>Change directory to the dsapi plugins source directory</notedos>
+<typedos>cd c:\home\apache\jk\domino</typedos>
+<notedos>Build the sources using MSDEV</notedos>
+<typedos>MSDEV dsapi.dsp /MAKE ALL</typedos>
+</screendos>
+
+</section>
+
+<section name="Building for Linux">
+<p>
+You will probably also want the Lotus Notes C API version 5.0.3 for Unix or later.
+</p>
+
+<p>
+You can build the redirector without the C API, in which case you'll need to define the macro NO_CAPI in config.h. 
+If you do this, Domino logging from the redirector will be disabled.
+</p>
+
+<ul>
+<li>
+Change directory to the Domino plugin source directory.
+</li>
+<li>
+Edit <b>Makefile</b> and update the include and library path to reflect your own Domino server installation 
+</li>
+</ul>
+
+<screen>
+<note>edit the Makefile providing appropriate values for these variables</note>
+<read># The root of your Domino installation. Mine's in /usr/local/lotus, but your's</read>
+<read># may well be /opt/lotus</read>
+<read>NOTESROOT=/usr/local/lotus</read>
+<read/>
+<read># The place where the Notes API is installed</read>
+<read>NOTESAPI=$(NOTESROOT)/notesapi</read>
+<read/>
+<read># The Domino program directory.</read>
+<read>NOTESHOME=$(NOTESROOT)/notes/5061/linux</read>
+<read/>
+<read># The Domino data directory (the directory containing names.nsf)</read>
+<read>NOTESDATA=$(NOTESROOT)/notes/data</read>
+<read/>
+<read># The include path for the Notes C API headers</read>
+<read>NOTESINC=$(NOTESAPI)/include</read>
+<read/>
+<read># Where tomcat is installed. This is where conf, lib, webapps et al normally are</read>
+<read>TOMCATHOME=/var/tomcat3</read>
+<read/>
+<read># Your JDK's include directory</read>
+<read>JAVAINC=$(JAVA_HOME)/include</read>
+</screen>
+
+<ul>
+<li>
+Now you should build via make
+</li>
+</ul>
+
+<screen>
+<note>Launch build via make</note>
+<type>make</type>
+<note>place the redirector (libtomcat.so) and its settings file (libtomcat.ini) in the appropriate places</note>
+<type>make install</type>
+</screen>
+
+</section>
+
+</document>
diff --git a/connectors/jk/xdocs/jk/iishowto.xml b/connectors/jk/xdocs/jk/iishowto.xml
new file mode 100644
index 0000000..089946e
--- /dev/null
+++ b/connectors/jk/xdocs/jk/iishowto.xml
@@ -0,0 +1,702 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>IIS HowTo</title>
+<author email="hgomez@apache.org">Henri Gomez</author>
+<author email="shachor@il.ibm.com">Gal Shachor</author>
+<author email="yoavs@apache.org">Yoav Shapira</author>
+<date>$Date$</date>
+</properties>
+
+<section name="Introduction">
+<p>
+This document explains how to set up IIS to cooperate with Tomcat. 
+</p>
+
+<p>
+Normally IIS can not execute Servlets and Java Server Pages (JSPs), 
+configuring IIS to use the JK ISAPI redirector plugin will let IIS send servlet and 
+JSP requests to Tomcat (and this way, serve them to clients).
+</p>
+
+<p>
+It is recommanded that you also read the <a href="jk/workershowto.html">Workers HowTo</a> document
+to learn how to setup the working entities between your WebServer and Tomcat Engines.
+</p>
+
+
+<subsection name="Document Conventions and Assumptions">
+<p>
+${tomcat_home} is the root directory of tomcat. 
+Your Tomcat installation should have the following subdirectories:
+
+<ul>
+<li>
+${tomcat_home}\conf - Where you can place various configuration files
+</li>
+<li>
+${tomcat_home}\webapps - Containing example applications
+</li>
+<li>
+${tomcat_home}\bin - Where you place web server plugins
+</li>
+</ul>
+</p>
+<p>
+In all the examples in this document ${tomcat_home} will be <b>c:\jakarta-tomcat</b>.
+A worker is defined to be a tomcat process that accepts work from the IIS server.
+</p>
+</subsection>
+
+
+<subsection name="Supported Configuration">
+<p>
+The IIS-Tomcat redirector was developed and tested on:
+<ul>
+<li>
+WinNT4.0-i386 SP4/SP5/SP6a (should be able to work with other service packs), Win2K and WinXP and Win98
+</li>
+<li>
+IIS4.0 and PWS4.0 (numerous people have working IIS 5 and IIS 6 configurations)
+</li>
+<li>
+Tomcat 3.2 and later, Tomcat 4.x and Tomcat 5
+</li>
+</ul>
+</p>
+
+<p>
+The redirector uses <b>ajp12</b> and <b>ajp13</b> to send requests to the Tomcat containers. There is also an option to use Tomcat in process, 
+more about the in-process mode can be found in the in process howto.
+</p>
+</subsection>
+
+<subsection name="IIS 5 and 6 Notes">
+<p>
+There are extra steps you need to take for configuring Tomcat with IIS 5 and 6.  Please see the appropriate links from 
+<a href="http://wiki.apache.org/jakarta-tomcat/Tomcat_2fLinks">Tomcat Useful Links</a>.
+</p>
+</subsection>
+
+<subsection name="Who support ajp protocols ?">
+<p>
+The ajp12 protocol is only available in Tomcat 3.2.x and 3.3.x.
+</p>
+
+<p>
+The <b>ajp12</b> has been <b>deprecated</b> with Tomcat 3.3.x and you should use instead 
+<b>ajp13</b> which is the only ajp protocol known by Tomcat 4.x and 5.
+</p>
+
+<p>
+Of course Tomcat 3.2.x and 3.3.x also support ajp13 protocol.
+</p>
+
+<p>
+Others servlet engines such as <b>jetty</b> have support for ajp13 protocol
+</p>
+
+</subsection>
+
+<subsection name="How does it work ?">
+<p>
+<ol>
+<li>
+The IIS-Tomcat redirector is an IIS plugin (filter + extension), IIS load the redirector plugin and calls its 
+filter function for each in-coming request.
+</li>
+<li>
+The filter then tests the request URL against a list of URI-paths held inside uriworkermap.properties, 
+If the current request matches one of the entries in the list of URI-paths, 
+the filter transfer the request to the extension.
+</li>
+<li>
+The extension collects the request parameters and forwards them to the appropriate worker using the defined
+protocol like <b>ajp13</b>.
+</li>
+<li>
+The extension collects the response from the worker and returns it to the browser.
+</li>
+</ol>
+</p>
+</subsection>
+
+</section>
+
+<section name="Installation">
+<p>
+A pre-built version of the ISAPI redirector server plugin, isapi_redirect.dll, is available under 
+the win32/i386 directory of jakarta-tomcat-connectors distribution. 
+For those using Netscape as your browser, try downloading a zip version of the file, if available. 
+There can be problems using Netscape to download DLL files.
+
+You can also build a copy locally from the source present in jakarta-tomcat-connectors distribution.
+
+The Tomcat redirector requires three entities:
+
+<ul>
+<li>
+<b>isapi_redirect.dll</b> - The IIS server plugin, either obtain a pre-built DLL or build it yourself (see the build section).
+</li>
+<li>
+<b>workers.properties</b> - A file that describes the host(s) and port(s) used by the workers (Tomcat processes). 
+A sample workers.properties can be found under the conf directory.
+</li>
+<li>
+<b>uriworkermap.properties</b> - A file that maps URL-Path patterns to workers. 
+A sample uriworkermap.properties can be found under the conf directory as well.
+</li>
+</ul>
+</p>
+
+<p>
+The installation includes the following parts:
+
+<ul>
+<li>
+Configuring the ISAPI redirector with a default /examples context and checking that you can serve servlets with IIS.
+</li>
+<li>
+Adding more contexts to the configuration.
+</li>
+</ul>
+</p>
+
+</section>
+
+<section name="Configuring the ISAPI Redirector">
+<p>
+In this document I will assume that isapi_redirect.dll is placed in 
+<b>c:\jakarta-tomcat\bin\win32\i386\isapi_redirect.dll</b> and 
+that you created the properties files are in <b>c:\jakarta-tomcat\conf</b>.
+</p>
+<p>
+<ol>
+<li>
+In the registry, create a new registry key named
+<b>"HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\1.0"</b>
+</li>
+<li>
+Add a string value with the name <b>extension_uri</b> and a value of <b>/jakarta/isapi_redirect.dll</b>
+</li>
+<li>
+Add a string value with the name <b>log_file</b> and a value pointing to where you want your 
+log file to be (for example <b>c:\jakarta-tomcat\logs\isapi.log</b>).
+</li>
+<li>
+Add a string value with the name <b>log_level</b> and a value for your log level 
+(can be debug, info, error or emerg).
+</li>
+<li>
+Add a string value with the name <b>worker_file</b> and a value which is the full path 
+to your workers.properties file (for example <b>c:\jakarta-tomcat\conf\workers.properties</b>)
+</li>
+<li>
+Add a string value with the name <b>worker_mount_file</b> and a value which is the full path 
+to your uriworkermap.properties file (for example <b>c:\jakarta-tomcat\conf\uriworkermap.properties</b>)
+</li>
+<li>
+Using the IIS management console, add a new virtual directory to your IIS/PWS web site.
+The name of the virtual directory must be jakarta. 
+Its physical path should be the directory where you placed isapi_redirect.dll 
+(in our example it is c:\jakarta-tomcat\bin\win32\i386). 
+While creating this new virtual directory assign it with execute access.
+</li>
+<li>
+Using the IIS management console, add isapi_redirect.dll as a filter in your IIS/PWS web site. 
+The name of the filter should reflect its task (I use the name jakarta), 
+its executable must be our c:\jakarta-tomcat\bin\win32\i386\isapi_redirect.dll. 
+For PWS, you'll need to use regedit and add/edit the <b>"Filter DLLs"</b> key under 
+<b>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\W3SVC\Parameters</b>. 
+This key contains a "," separated list of dlls ( full paths ) - 
+you need to insert the full path to isapi_redirect.dll.
+</li>
+<li>
+Restart IIS (stop + start the IIS service), make sure that the jakarta filter is marked with a green up-pointing arrow.
+Under Win98 you may need to <b>cd WINDOWS\SYSTEM\inetsrv</b> and type PWS /stop 
+( the DLL and log files are locked - even if you click the stop button, 
+PWS will still keep the DLLs in memory. ). Type pws to start it again.
+</li>
+</ol>
+</p>
+<p>
+That's all, you should now start Tomcat and ask IIS to serve you the /examples context. 
+Try <a href="http://localhost/examples/jsp/index.html">http://localhost/examples/jsp/index.html</a> for example and 
+execute some of the JSP examples. 
+</p>
+<p>
+If this does not work successfully, refer to the Troubleshooting section below for help on correcting the problem.
+</p>
+
+<subsection name="Adding additional Contexts">
+<p>
+The examples context is useful for verifying your installation, 
+but you will also need to add your own contexts. Adding a new context requires two operations:
+</p>
+<p>
+<ol>
+<li>
+Adding the context to Tomcat (I am not going to talk about this).
+</li>
+<li>
+Adding the context to the ISAPI redirector.
+</li>
+</ol>
+</p>
+<p>
+Adding a context to the ISAPI redirector is simple, all you need to do is to edit 
+your uriworkermap.properties and to add a line that looks like:
+</p>
+
+<screen>
+<read>/context/*=worker_name</read>
+</screen>
+
+<p>
+Workers and their name are defined in workers.properties, by default workers.properties comes 
+with a single pre-configured worker named <b>"defworker"</b> so you can use it. 
+As an example, if you want to add a context named "shop", the line that you should add to 
+uriworkermap.properties will be:
+</p>
+
+<screen>
+<read>/shop/*=defworker</read>
+</screen>
+
+After saving uriworkermap.properties restart IIS and it will serve the new context.
+
+<p>
+A feature is present till Tomcat 3.2, where a uriworkermap.properties-auto is automatically 
+written each time Tomcat is started. This file includes settings for each of the contexts that 
+Tomcat will serve during its run. 
+</p>
+<p>
+Each context has settings to have Tomcat handle servlet and JSP requests, 
+but by default static content is left to be served by IIS. 
+</p>
+<p>
+Each context also has a commented out setting to have Tomcat handle all requests to the context. 
+You can rename this file (so it won't be overwritten the next time Tomcat is started) and 
+uncomment this setting or make other customizations. 
+</p>
+<p>
+You may also use this file as is in your worker_mount_file setting.
+</p>
+</subsection>
+
+<subsection name="Advanced Context Configuration">
+<p>
+Sometimes it is better to have IIS serve the static pages (html, gif, jpeg etc.) 
+even if these files are part of a context served by Tomcat. 
+</p>
+<p>
+For example, consider the html and gif files in the examples context, there is no need 
+to serve them from the Tomcat process, IIS will suffice.
+</p>
+
+<p>
+Making IIS serve static files that are part of the Tomcat contexts requires the following:
+<ol>
+<li>
+Configuring IIS to know about the Tomcat contexts
+</li>
+<li>
+Configuring the redirector to leave the static files for IIS
+</li>
+</ol>
+</p>
+
+<p>
+Adding a Tomcat context to IIS requires the addition of a new IIS virtual directory that covers the Tomcat context. 
+For example adding a /example IIS virtual directory that covers the c:\jakarta-tomcat\webapps\examples directory.
+</p>
+
+<p>
+Configuring the redirector is somewhat harder, you will need to specify the exact 
+URL-Path pattern(s) that you want Tomcat to handle (usually only JSP files and servlets). 
+This requires a change to the uriworkermap.properties : 
+
+<screen>
+<note>For the examples context it requires to replace the following line</note>
+<read>/examples/*=defworker</read>
+<note>with the following two lines</note>
+<read>/examples/*.jsp=defworker</read>
+<read>/examples/servlet/*=defworker</read>
+</screen>
+</p>
+
+<p>
+As you can see the second configuration is more explicit, it actually instruct the redirector 
+to redirect only requests to resources under /examples/servlet/ and resources under /examples/ 
+whose name ends with .jsp. 
+This is similar to what is automically written to the uriworkermap.properties-auto file for each context.
+</p>
+
+<p>
+You can even be more explicit and provide lines such as:
+
+<screen>
+<read>/example/servletname=defworker</read>
+</screen>
+</p>
+
+<p>
+that instructs the redirector to redirect request whose URL-Path equals /example/servletname 
+to the worker named defworker.
+</p>
+
+</subsection>
+
+<subsection name="Protecting the WEB-INF Directory">
+<p>
+Each servlet application (context) has a special directory named WEB-INF, 
+this directory contains sensitive configurations data and Java classes and must be kept hidden from web users. 
+Using the IIS management console it is possible to protect the WEB-INF directory from user access, 
+this however requires the administrator to remember that. 
+</p>
+<p>
+To avoid this need the redirector plugin automatically protects your WEB-INF directories by rejecting 
+any request that contains WEB-INF in its URL-Path.
+</p>
+</subsection>
+
+<subsection name="Advanced Worker Configuration">
+<p>
+Sometimes you want to serve different contexts with different Tomcat processes 
+(for example to spread the load among different machines). 
+To achieve such goal you will need to define several workers and assign each context with its own worker.
+</p>
+<p>
+Defining workers is done in workers.properties, this file includes two types of entries:
+</p>
+
+<p>
+<screen>
+<note>An entry that lists all the workers defined</note>
+<read>worker.list=worker1, worker2</read>
+<note>Entries that define the host and port associated with these workers</note>
+<read>worker.worker1.host=localhost</read>
+<read>worker.worker1.port=8009</read>
+<read>worker.worker1.type=ajp13</read>
+<read>worker.worker2.host=otherhost</read>
+<read>worker.worker2.port=8009</read>
+<read>worker.worker2.type=ajp13</read>
+</screen>
+</p>
+
+<p>
+The above example defined two workers, now we can use these workers to serve two different contexts 
+each with its own worker : 
+<screen>
+<note>example uriworkermap.properties fragment</note>
+<read>/examples/*=worker1</read>
+<read>/webpages/*=worker2</read>
+</screen>
+</p>
+
+<p>
+As you can see the <b>examples</b> context is served by <b>worker1</b> while the 
+<b>webpages</b> context is served by <b>worker2</b>.
+</p>
+
+<p>
+More informations on using and configuring workers in the <a href="jk/workershowto.html">Workers HowTO</a>
+</p>
+
+</subsection>
+
+</section>
+
+<section name="Building ISAPI redirector">
+<p>
+The redirector was developed using Visual C++ Ver.6.0, so having this environment is a prereq if you want 
+to perform a custom build. You should also have IIS developer SDK
+
+The steps that you need to take are:
+<ul>
+<li>
+Change directory to the isapi plugins source directory.
+</li>
+<li>
+Make the source with MSDEV
+</li>
+</ul>
+<screendos>
+<notedos>Change directory to the isapi plugins source directory</notedos>
+<typedos>cd c:\home\apache\jk\isapi</typedos>
+<notedos>Build the sources using MSDEV</notedos>
+<typedos>MSDEV isapi.dsp /MAKE ALL</typedos>
+</screendos>
+</p>
+<p>
+If msdev is not in your path, enter the full path to msdev.exe. 
+This will build both release and debug versions of the redirector plugin.
+An alternative will be to open the isapi workspace file (isapi.dsw) in msdev and 
+build it using the build menu.
+</p>
+</section>
+
+<section name="Troubleshooting">
+<p>
+It is easy to have the ISAPI redirector not work the first time you try to install it.
+</p>
+<p>
+If this happens to you, here are some steps to follow to try to correct the problem.
+</p>
+<p>
+These steps aren't guaranteed to cover all possible problems, 
+but they should help find the typical mistakes.
+</p>
+<p>
+If you make any corrections during these steps, restart the IIS service as described above in the last step 
+of the installation, then retry the step.
+</p>
+
+<p>To enable error tracking, make sure web site activity is being logged. 
+For PWS 4.0 make sure "Save Web Site Activity Log" is checked in the Advanced Options of the Personal Web Manager.
+</p>
+   
+<p>
+Note: These steps assume your <b>worker_mount_file</b> setting points to an unmodified copy of the 
+<b>uriworkermap.properties</b> file.<br/>
+Results may be misleading if <b>worker_mount_file</b> points to a modified <b>uriworkermap.properties</b>
+or the <b>uriworkermap.properties-auto</b> file.<br/>
+It is also assumed that the <b>"/examples" context</b> works correcly if you access Tomcat directly.
+</p>
+
+<subsection name="Win98">
+<p>
+Start the IIS service and Tomcat.
+</p>
+<p>
+Check for the presence of the ISAPI redirector log file you specified in the log_file setting. 
+If not found, verify the following:
+</p>
+<ul>
+<li>
+Check the "Filter DLLs" setting in the "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\W3SVC\Parameters" 
+key and make sure the path is correct.
+</li>
+<li>
+Check the spelling of the "HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\1.0" key. 
+Case isn't important, but an incorrect letter will prevent the isapi_redirect.dll from finding its registry settings.
+</li>
+<li>
+Check the log_file setting for typos, name and data. Also insure the directory in which the log file will appear already exists.
+</li>
+If the above are set correctly, the ISAPI redirector should be able to create the log file.
+</ul>
+<p>
+Invoke the URL <a href="http://localhost/examples/jsp/index.html">http://localhost/examples/jsp/index.html</a>
+in your browser. 
+Case is important in Tomcat. The characters following "localhost" in the URL must be lower case. 
+If the page fails to appear, stop the IIS service (required to view the IIS log file). 
+Then examine the last line in the IIS log file in found in SYSTEM/LogFiles/W3SVC1 :
+</p>
+<p>
+If the last line contains: 
+</p>
+<screen>
+<read>GET "/examples/jsp/index.html HTTP/1.1" 404</read>
+</screen>
+<p> 
+then the ISAPI redirector is not recognizing that it should be handling requests for the "/examples" context. 
+Check the following:
+</p>
+<ul>
+<li>
+Check the extension_uri name for typos.
+</li>
+<li>
+Check the worker_file setting for typos, name and data.
+</li>
+<li>
+Check the worker_mount_file setting typos, name and data.
+</li>
+If these are set correctly, the ISAPI redirector should recognize that it should handle requests for the "/examples" context.
+</ul>
+
+<p>If the last line contains something like:
+</p>
+
+<screen>
+<read>GET "/jakarta/isapi_redirect.dll HTTP1.1"</read>
+</screen>
+
+<p>
+then the ISAPI redirector is recognizing that it should handle the request, 
+but is not successful at getting Tomcat to service the request.
+</p>
+
+<p>
+You should check the HTTP error code following GET "/..." :
+</p>
+
+<screen>
+<note>Error 404</note>
+<read>GET "/..." 404</read>
+</screen>
+
+<ul>
+<li>
+Make sure you entered the URL correctly.
+</li>
+<li>
+Make sure the virtual directory created was called "jakarta". 
+It should display in Personal Web Manager as "/jakarta" (without the quotes).
+</li>
+<li>
+Make sure the extension_uri data begins with "/jakarta/" (without the quotes).
+</li>
+</ul>
+
+<screen>
+<note>Error 500</note>
+<read>GET "/..." 500</read>
+</screen>
+
+<ul>
+<li>
+Make sure that "isapi_redirect.dll" follows "/jakarta/" in the extension_uri setting.
+</li>
+<li>
+Check the workers.properties file and make sure the port setting for worker.ajp12.port is the same as the port specified in the server.xml for the "Apache AJP12 support".
+</li>
+</ul>
+
+<screen>
+<note>Error 200 or 403</note>
+<read>GET "/..." 200</read>
+<read>GET "/..." 403</read>
+</screen>
+
+<ul>
+<li>
+Make sure you have checked Execute Access 
+for the jakarta virtual directory in the Advanced Options of the Personal Web Manager.
+</li>
+</ul>
+
+<p>
+If the above settings are correct, the index.html page should appear in your browser. 
+You should also be able to click the Execute links to execute the JSP examples.
+</p>
+
+</subsection>
+
+<subsection name="WinNT/Win2K/WinXP">
+<p>
+Start the World Wide Web Publishing Service and Tomcat.
+</p>
+<p>
+Check for the presence of the ISAPI redirector log file you specified in the log_file setting. 
+If not found, check the following:
+</p>
+<ul>
+<li>
+Check the "executable" you set for the filter in the IIS Management Console and make sure the path is correct.
+</li>
+<li>Check the spelling of the "HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\1.0" key.
+Case isn't important, but an incorrect letter will prevent the isapi_redirect.dll from finding its registry settings.
+</li>
+<li>
+Check the log_file setting for typos, name and data. Also insure the directory in which the log file will appear already exists.
+</li>
+If the above are set correctly, the ISAPI redirector should be able to create the log file.
+</ul>
+
+<p>
+Check the jakarta filter you added and make sure its status shows a green upward-pointing arrow. 
+If not, check the following:
+</p>
+<ul>
+<li>
+Check the worker_file setting for typos, name and data.
+</li>
+<li>
+Check the worker_mount_file setting typos, name and data.
+</li>
+If the above are set correctly, the green upward-pointing arrow should appear, even if the other settings are wrong.
+</ul>
+
+<p>
+Invoke the URL <a href="http://localhost/examples/jsp/index.html">http://localhost/examples/jsp/index.html</a> 
+in your browser. Case is important in Tomcat. The characters following "localhost" in the URL must be lower case. 
+If the page fails to appear, examine the last line in the IIS server log file in found in SYSTEM32/LogFiles/W3SVC1.
+</p>
+
+<p>
+The last line should contain something like: GET "/jakarta/isapi_redirect.dll HTTP1.1", 
+which indicates the ISAPI redirector is recognizing that it should handle the request.
+</p>
+
+<p>
+You should check the HTTP error code following GET "/..." :
+</p>
+
+<screen>
+<note>Error 404</note>
+<read>GET "/..." 404</read>
+</screen>
+
+<ul>
+<li>
+Make sure you entered the URL correctly.
+</li>
+</ul>
+
+<screen>
+<note>Error 500</note>
+<read>GET "/..." 500</read>
+</screen>
+
+<ul>
+<li>
+Make sure the virtual directory created was called "jakarta".
+</li>
+<li>
+Make sure that the extension_uri setting is correct.
+</li>
+<li>
+Check the workers.properties file and make sure the port setting for worker.ajp12.port is the same as the port specified in the server.xml for the "Apache AJP12 support".
+</li>
+</ul>
+
+<screen>
+<note>Error 200 or 403</note>
+<read>GET "/..." 200</read>
+<read>GET "/..." 403</read>
+</screen>
+
+<ul>
+<li>
+Make sure you have checked Execute Access for the jakarta virtual directory in the 
+Advanced Options of the Personal Web Manager.
+</li>
+</ul>
+
+<p>
+If the above settings are correct, the index.html page should appear in your browser. 
+You should also be able to click the Execute links to execute the JSP examples.
+</p>
+</subsection>
+
+
+</section>
+
+</document>
diff --git a/connectors/jk/xdocs/jk/neshowto.xml b/connectors/jk/xdocs/jk/neshowto.xml
new file mode 100644
index 0000000..34202d0
--- /dev/null
+++ b/connectors/jk/xdocs/jk/neshowto.xml
@@ -0,0 +1,416 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>Netscape/iPlanet HowTo</title>
+<author email="hgomez@apache.org">Henri Gomez</author>
+<author email="shachor@il.ibm.com">Gal Shachor</author>
+<date>$Date$</date>
+</properties>
+
+<section name="Introduction">
+<p>
+This document explains how to set up Netscape web servers to cooperate with Tomcat. 
+</p>
+
+<p>
+Normally the Netscape web servers come with their own Servlet engine, 
+but you can also configure them to send servlet and JSP requests to Tomcat 
+using the Tomcat redirector plugin.
+</p>
+
+<p>
+It is recommanded that you also read the <a href="jk/workershowto.html">Workers HowTo</a> document
+to learn how to setup the working entities between your WebServer and Tomcat Engines.
+</p>
+
+
+<subsection name="Document Conventions and Assumptions">
+<p>
+${tomcat_home} is the root directory of tomcat. 
+Your Tomcat installation should have the following subdirectories:
+
+<ul>
+<li>
+${tomcat_home}\conf - Where you can place various configuration files
+</li>
+<li>
+${tomcat_home}\webapps - Containing example applications
+</li>
+<li>
+${tomcat_home}\bin - Where you place web server plugins
+</li>
+</ul>
+</p>
+<p>
+In all the examples in this document ${tomcat_home} will be <b>c:\jakarta-tomcat</b>.
+A worker is defined to be a tomcat process that accepts work from the Netscape/iPlanet server.
+</p>
+</subsection>
+
+
+<subsection name="Supported Configuration">
+<p>
+The Netscape-Tomcat redirector was developed and tested on:
+<ul>
+<li>
+WinNT4.0-i386 SP4/SP5/SP6a (should be able to work with other service packs) and some Unixes
+</li>
+<li>
+Netscape Enterprise 3.0 and 3.61
+</li>
+<li>
+Tomcat 3.2.x, 3.3.x, Tomcat 4.0.x, Tomcat 4.1.x and Tomcat 5
+</li>
+</ul>
+</p>
+
+<p>
+The redirector uses <b>ajp12</b> and <b>ajp13</b> to send requests to the Tomcat containers. 
+There is also an option to use Tomcat in process, 
+more about the in-process mode can be found in the in process howto.
+</p>
+</subsection>
+
+<subsection name="Who support ajp protocols ?">
+<p>
+The ajp12 protocol is only available in Tomcat 3.2.x and 3.3.x.
+</p>
+
+<p>
+The <b>ajp12</b> has been <b>deprecated</b> with Tomcat 3.3.x and you should use instead 
+<b>ajp13</b> which is the only ajp protocol known by Tomcat 4.0.x, 4.1.x and 5.
+</p>
+
+<p>
+Of course Tomcat 3.2.x and 3.3.x also support ajp13 protocol.
+</p>
+
+<p>
+Others servlet engines such as <b>jetty</b> have support for ajp13 protocol
+</p>
+
+</subsection>
+
+
+<subsection name="How does it work ?">
+<p>
+<ol>
+<li>
+The Netscape-Tomcat redirector is an Netscape service step plugin, 
+Netscape load the redirector plugin and calls its service handler 
+function for request that are assigned to the "servlet" configuration object.
+</li>
+<li>
+For each in-coming request Netscape will execute the set of NameTrans directives 
+that we added to obj.conf, the assign-name function will check if it's from 
+parameter matches the request URL.
+</li>
+<li>
+If a match is found, assign-name will assign the servlet object name to the request. 
+This will cause Netscape to send the request to the servlet configuration object.
+</li>
+<li>
+Netscape will execute our jk_service extension. The extension collects the 
+request parameters and forwards them to the appropriate worker using the ajp13 protocol 
+(the worker="defworker" parameter in jk_service inform it that the worker for this request is named <b>defworker</b>).
+the workers properties files, <b>workers.properties</b>, will indicate that defworker use ajp13 protocol.
+</li>
+<li>
+The extension collects the response from the worker and returns it to the browser.
+</li>
+</ol>
+</p>
+</subsection>
+
+</section>
+
+<section name="Installation">
+<p>
+A pre-built version of the Netscape redirector, nsapi_redirect.dll, may be available under 
+the win32/i386 directory of jakarta-tomcat-connectors distribution. 
+For those using Netscape as your browser, try downloading a zip version of the file, if available. 
+There can be problems using Netscape to download DLL files.
+
+You can also build a copy locally from the source present in jakarta-tomcat-connectors distribution.
+
+
+The Tomcat redirector requires two entities:
+<ul>
+<li>
+nsapi_redirect.dll - The Netscape server plugin, either obtain a pre-built DLL or build it yourself 
+(see the build section).
+</li>
+<li>
+workers.properties - A file that describes the host(s) and port(s) used by the workers (Tomcat processes). 
+A sample workers.properties can be found under the conf directory.
+</li>
+</ul>
+
+The installation includes the following parts:
+
+<ul>
+<li>
+Configuring the NSAPI redirector with a default /examples context and checking that you can serve servlets 
+with Netscape.
+</li>
+<li>
+Adding more contexts to the configuration.
+</li>
+</ul>
+
+</p>
+</section>
+
+<section name="Configuring the NSAPI Redirector">
+<p>
+In this document we'll assume that nsapi_redirect.dll is placed in 
+<b>c:\jk\lib\nsapi_redirect.dll</b>, the properties file is in<b>c:\jk\conf</b>
+and you created a log directory <b>c:\jk\logs</b>
+</p>
+
+<ul>
+<li>
+If the Netscape built in servlet support is working disable it.
+</li>
+<li>
+Add the redirector plugin into the Netscape server configuration. 
+Edit your server <b>obj.conf</b> and add the following lines:
+</li>
+</ul>
+
+<screen>
+<note>In the Init section:</note>
+<read>Init fn="load-modules" funcs="jk_init,jk_service" shlib="c:/jk/lib/nsapi_redirect.dll"</read>
+<read>Init fn="jk_init" worker_file="c:/jk/conf/workers.properties" log_level="debug" log_file="c:/jk/logs/nsapi.log"</read>
+<note>In the default object NameTrans section</note>
+<read>NameTrans fn="assign-name" from="/servlet/*" name="servlet"</read>
+<read>NameTrans fn="assign-name" from="/examples/*" name="servlet"</read>
+<note>Create a new configuration object by adding the following lines to the end of the obj.conf file</note>
+<read>&lt;Object name=servlet&gt;</read>
+<read>ObjectType fn=force-type type=text/plain</read>
+<read>Service fn="jk_service" worker="worker1"</read>
+<read>&lt;/Object&gt;</read>
+</screen>
+
+<ul>
+<li>
+Restart Netscape (stop and start the server)
+</li>
+</ul>
+
+<p>
+That's all, now you should start tomcat and ask Netscape for http://server:port/examples/
+</p>
+
+<subsection name="Adding additional Contexts">
+<p>
+The examples context is useful for verifying your installation, but you will also need to add your own contexts. 
+Adding a new context requires two operations:
+</p>
+<ul>
+<li>
+Adding the context to Tomcat (I am not going to talk about this).
+</li>
+<li>
+Assigning the NSAPI redirector to handle this context.
+</li>
+</ul>
+
+<p>
+Assigning the NSAPI redirector to handle this context is simple, 
+all you need to do is to edit <b>obj.conf</b> and add a NameTrans line that looks like:
+</p>
+
+<screen>
+<read>NameTrans fn="assign-name" from="/&lt;context name&gt;/*" name="servlet"</read>
+</screen>
+
+<p>
+After saving <b>obj.conf</b> restart Netscape and it will serve the new context.
+</p>
+</subsection>
+
+<subsection name="Advanced Context Configuration">
+<p>
+Sometimes it is better to have Netscape serve the static pages (html, gif, jpeg etc.) 
+even if these files are part of a context served by Tomcat. For example, consider the html and gif files in the examples context, there is no need to serve them from the Tomcat process, Netscape will suffice.
+</p>
+<p>
+Making Netscape serve static files that are part of the Tomcat contexts requires the following:
+</p>
+<ul>
+<li>
+Configuring Netscape to know about the Tomcat contexts
+</li>
+<li>
+Make sure that the WEB-INF directory is protected from access.
+</li>
+<li>
+Configuring Netscape to assign the NSAPI redirector only specific requests that requires JSP/Servlet handling.
+</li>
+</ul>
+
+<p>
+Adding a Tomcat context to Netscape requires the addition of a new Netscape virtual directory 
+that covers the Tomcat context.
+</p>
+
+<p>
+For example, adding a /example Netscape virtual directory that 
+covers the <b>c:\jakarta-tomcat\webapps\examples</b> directory. 
+</p>
+
+<p>
+To add a new virtual directory add the following line to your <b>obj.conf</b>:
+</p>
+
+<screen>
+<read>NameTrans fn=pfx2dir from=/examples dir="c:/jakarta-tomcat/webapps/examples"</read>
+</screen>
+
+<p>
+WEB-INF protection requires some explanation; Each servlet application (context) has a special directory named <b>WEB-INF</b>,
+this directory contains sensitive configurations data and Java classes and must be kept hidden from web users. 
+WEB-INF can be protected by adding the following line to the PathCheck section in the default configuration object:
+</p>
+
+<screen>
+<read>PathCheck fn="deny-existence" path="*/WEB-INF/*"</read>
+<note>This line instructs the Netscape server to reject any request with a URL that contain the path /WEB-INF/.</note>
+</screen>
+
+<p>
+Configuring Netscape to assign the NSAPI redirector only specific requests is somewhat harder, 
+you will need to specify the exact URL-Path pattern(s) that you want Tomcat to handle 
+(usually only JSP files and servlets). 
+</p>
+
+<p>
+This requires a change to NemaTrans portion of <b>obj.conf</b>. 
+</p>
+
+<screen>
+<note>For the examples context it requires to replace the following line:</note>
+<read>NameTrans fn="assign-name" from="/examples/*" name="servlet"</read>
+<note>with the following two lines:</note>
+<read>NameTrans fn="assign-name" from="/examples/jsp/*.jsp" name="servlet"</read>
+<read>NameTrans fn="assign-name" from="/examples/servlet/*" name="servlet"</read>
+</screen>
+
+<p>
+As you can see the second configuration is more explicit, it actually instructs 
+Netscape to assign the redirector with only requests to resources under 
+<b>/examples/servlet/</b> and resources under <b>/examples/</b> whose name ends with <b>.jsp</b>.
+</p>
+
+<p>
+You can be even more explicit and provide lines such as:
+</p>
+
+<screen>
+<read>NameTrans fn="assign-name" from="/examples/servletname" name="servlet"</read>
+<note>Instructs Netscape to assign the redirector request whose URL-Path equals /example/servletname</note>
+</screen>
+
+</subsection>
+
+<subsection name="Advanced Worker Configuration">
+<p>
+Sometimes you want to serve different contexts with different Tomcat processes 
+(for example to spread the load among different machines). 
+To achieve such goal you will need to define several workers and assign each context with its own worker.
+</p>
+
+<p>
+Defining workers is done in <b>workers.properties</b>, this file includes two types of entries:
+</p>
+
+<screen>
+<note>An entry that lists all the workers defined. For example:</note>
+<read>worker.list=worker1, worker2</read>
+<note>Entries that define the host and port associated with these workers.</note>
+<read>worker.worker1.host=localhost</read>
+<read>worker.worker1.port=8009</read>
+<read>worker.worker1.type=ajp13</read>
+<read>worker.worker2.host=otherhost</read>
+<read>worker.worker2.port=8009</read>
+<read>worker.worker2.type=ajp13</read>
+</screen>
+
+<p>
+The above examples defined two workers, now we can use these workers to serve two different 
+contexts each with itÂ’s own worker. 
+Submitting requests to different workers is accomplished by using multiple Service directives 
+in the servlet configuration Object, each with a different path pattern parameter. 
+</p>
+
+<p>
+For example, if we want to submit the <b>/examples</b> context to the worker named <b>worker1</b> and the 
+<b>/webpages</b> context to the worker named <b>worker2</b> we should use the following configuration:
+</p>
+
+<screen>
+<read>&lt;Object name=servlet&gt;</read>
+<read>ObjectType fn=force-type type=text/plain</read>
+<read>Service fn="jk_service" worker="worker1" path="/examples/*"</read>
+<read>Service fn="jk_service" worker="worker2" path="/webpages/*"</read>
+<read>Service fn="jk_service" worker="worker1"</read>
+<read>&lt;/Object&gt;</read>
+</screen>
+
+<p>
+More informations on using and configuring workers in the <a href="jk/workershowto.html">Workers HowTO</a>
+</p>
+</subsection>
+
+</section>
+
+<section name="Building NSAPI redirector">
+<p>
+The redirector was developed using Visual C++ Ver.6.0, so having this environment is a prereq if you want 
+to perform a custom build. You should also have NES developer SDK
+
+The steps that you need to take are:
+<ul>
+<li>
+Change directory to the nsapi plugins source directory.
+</li>
+<li>
+Edit <b>nsapi.dsp</b> and update the include and library path to reflect your own Netscape server installation 
+(search for a <b>/I compiler</b> option and <b>/libpath</b> linker option)
+</li>
+<li>
+Make the source with MSDEV
+</li>
+</ul>
+<screendos>
+<notedos>Change directory to the nsapi plugins source directory</notedos>
+<typedos>cd c:\home\apache\jk\nsapi</typedos>
+<notedos>Build the sources using MSDEV</notedos>
+<typedos>MSDEV nsapi.dsp /MAKE ALL</typedos>
+</screendos>
+</p>
+<p>
+If msdev is not in your path, enter the full path to msdev.exe. 
+This will build both release and debug versions of the redirector plugin.
+An alternative will be to open the nsapi workspace file (nsapi.dsw) in msdev and 
+build it using the build menu.
+</p>
+</section>
+
+</document>
diff --git a/connectors/jk/xdocs/jk/quickhowto.xml b/connectors/jk/xdocs/jk/quickhowto.xml
new file mode 100644
index 0000000..6d3e5f2
--- /dev/null
+++ b/connectors/jk/xdocs/jk/quickhowto.xml
@@ -0,0 +1,173 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>Quick Start HowTo</title>
+<author email="hgomez@apache.org">Henri Gomez</author>
+<date>$Date$</date>
+</properties>
+<section name="Introduction">
+<p>
+  This document describes the configuration files used by JK on the
+  Web Server side for the 'impatients':
+    <ul>
+    <li>
+    <b>workers.properties</b> is a mandatory file used by the webserver and which
+    is the same for all JK implementations (Apache/IIS/NES).
+    </li>
+    <li>
+    <b>WebServers</b> add-ons to be set on the webserver side. 
+    </li>
+    </ul>
+</p>
+<p>        
+  We'll give here minimum servers configuration and an example <b>workers.properties</b> 
+  to be able to install and check quickly your configuration.
+</p>
+</section>
+
+<section name="Minimum workers.properties">
+<p>
+    Here is a minimum <b>workers.properties</b>, using just ajp13 to connect your Apache webserver
+    to the Tomcat engine, complete documentation is available in <a href="jk/workershowto.html">Workers HowTo</a>. 
+</p>
+<p>
+<screen>
+<note># Define 1 real worker using ajp13</note> 
+<read>worker.list=worker1</read>
+<note># Set properties for worker1 (ajp13)</note>
+<read>worker.worker1.type=ajp13</read>
+<read>worker.worker1.host=localhost</read>
+<read>worker.worker1.port=8009</read>
+<read>worker.worker1.lbfactor=50</read>
+<read>worker.worker1.cachesize=10</read>
+<read>worker.worker1.cache_timeout=600</read>
+<read>worker.worker1.socket_keepalive=1</read>
+<read>worker.worker1.socket_timeout=300</read>
+</screen>
+</p>
+</section>
+
+<section name="Minimum Apache WebServer configuration">
+<p>
+   Here is a minimun informations about Apache configuration, a 
+   complete documentation is available in <a href="jk/aphowto.html">Apache HowTo</a>.
+</p>
+<p>
+	You should first have <b>mod_jk.so</b> (unix) or <b>mod_jk.dll</b> (Windows) installed
+	in your Apache module directory (see your Apache documentation to locate it).
+</p>
+<p>
+	Usual locations for modules directory on Unix:
+	<ul>
+	<li>/usr/lib/apache/</li>
+	<li>/usr/lib/apache2/</li>
+	<li>/usr/local/apache/libexec/</li>
+	</ul>
+</p>
+<p>
+	Usual locations for modules directory on Windows :
+	<ul>
+	<li>C:\Program Files\Apache Group\Apache\modules\</li>
+	<li>C:\Program Files\Apache Group\Apache2\modules\</li>
+	</ul>
+</p>
+<p>
+	You'll find prebuilt binaries <a href="http://jakarta.apache.org/site/binindex.cgi/">here</a>
+</p>
+<p>
+    Here is the minimum which should be set in <b>httpd.conf</b> directly or 
+    included from another file:
+</p>    
+<p>
+	Usual locations for configuration directory on Unix:
+	<ul>
+	<li>/etc/httpd/conf/</li>
+	<li>/etc/httpd2/conf/</li>
+	<li>/usr/local/apache/conf/</li>
+	</ul>
+</p>
+<p>
+	Usual locations for configuration directory on Windows :
+	<ul>
+	<li>C:\Program Files\Apache Group\Apache\conf\</li>
+	<li>C:\Program Files\Apache Group\Apache2\conf\</li>
+	</ul>
+</p>
+<p>
+<screen>
+<note># Load mod_jk module</note>
+<note># Update this path to match your modules location</note>
+<read>LoadModule    jk_module  libexec/mod_jk.so</read>
+<note># Declare the module for &lt;IfModule directive&gt;</note>
+<read>AddModule     mod_jk.c</read>
+<note># Where to find workers.properties</note>
+<note># Update this path to match your conf directory location (put workers.properties next to httpd.conf)</note>
+<read>JkWorkersFile /etc/httpd/conf/workers.properties</read>
+<note># Where to put jk logs</note>
+<note># Update this path to match your logs directory location (put mod_jk.log next to access_log)</note>
+<read>JkLogFile     /var/log/httpd/mod_jk.log</read>
+<note># Set the jk log level [debug/error/info]</note>
+<read>JkLogLevel    info</read>
+<note># Select the log format</note>
+<read>JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "</read>
+<note># JkOptions indicate to send SSL KEY SIZE, </note>
+<read>JkOptions     +ForwardKeySize +ForwardURICompat -ForwardDirectories</read>
+<note># JkRequestLogFormat set the request format </note>
+<read>JkRequestLogFormat     "%w %V %T"</read>
+<note># Send everything for context /examples to worker named worker1 (ajp13)</note>
+<read>JkMount  /examples/* worker1</read>
+</screen>
+</p>
+</section>
+
+<section name="Minimum Domino WebServer configuration">
+<p>
+	A complete documentation is available in <a href="jk/domhowto.html">Domino HowTo</a>.
+</p>
+<todo>
+More informations to be added, Nacho ?
+</todo>
+</section>
+
+<section name="Minimum IIS WebServer configuration">
+<p>
+	A complete documentation is available in <a href="jk/iishowto.html">IIS HowTo</a>.
+</p>
+<todo>
+More informations to be added, Nacho ?
+</todo>
+</section>
+
+<section name="Minimum NES/iPlanet WebServer configuration">
+<p>
+	A complete documentation is available in <a href="jk/neshowto.html">Netscape/iPlanet HowTo</a>.
+</p>
+<todo>
+More informations to be added, Mike ?
+</todo>
+</section>
+
+
+<section name="Test your configuration">
+<p>
+	(Re)start the Web server and browse to the <a>http://localhost/examples/</a>
+</p>
+
+</section>
+
+</document>
diff --git a/connectors/jk/xdocs/jk/workershowto.xml b/connectors/jk/xdocs/jk/workershowto.xml
new file mode 100644
index 0000000..d3dde50
--- /dev/null
+++ b/connectors/jk/xdocs/jk/workershowto.xml
@@ -0,0 +1,710 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>Workers HowTo</title>
+<author email="hgomez@apache.org">Henri Gomez</author>
+<author email="shachor@il.ibm.com">Gal Shachor</author>
+<date>$Date$</date>
+</properties>
+
+<section name="Introduction">
+<p>
+A Tomcat worker is a Tomcat instance that is waiting to execute servlets on behalf of some web server. 
+For example, we can have a web server such as Apache forwarding servlet requests to a 
+Tomcat process (the worker) running behind it.
+</p>
+<p>
+The scenario described above is a very simple one; 
+in fact one can configure multiple Tomcat workers to serve servlets on 
+behalf of a certain web server. 
+The reasons for such configuration can be:
+</p>
+<ul>
+<li>
+We want different contexts to be served by different Tomcat workers to provide a 
+development environment where all the developers share the same web server but own a Tomcat worker of their own.
+</li>
+<li>
+We want different virtual hosts served by different Tomcat processes to provide a 
+clear separation between sites belonging to different companies.
+</li>
+<li>
+We want to provide load balancing, meaning run multiple Tomcat workers each on a 
+machine of its own and distribute the requests between them.
+</li>
+</ul>
+
+<p>
+There are probably more reasons for having multiple workers but I guess that this list is enough...
+Tomcat workers are defined in a properties file dubbed workers.properties and this tutorial 
+explains how to work with it.
+</p>
+
+<p>
+This document was originally part of <b>Tomcat: A Minimalistic User's Guide</b> written by Gal Shachor, 
+but has been split off for organizational reasons. 
+</p>
+</section>
+
+<section name="Defining Workers">
+<p>
+Defining workers to the Tomcat web server plugin can be done using a properties file 
+(a sample file named workers.properties is available in the conf/ directory).
+</p>
+
+<p>
+the file contains entries of the following form:
+</p>
+
+<p>
+<b>worker.list</b>=&lt;a comma separated list of worker names&gt;
+</p>
+
+<screen>
+<note>the list of workers</note>
+<read>worker.list= worker1, worker2</read>
+</screen>
+
+<p>
+When starting up, the web server plugin will instantiate the workers whose name appears in the 
+<b>worker.list</b> property, these are also the workers to whom you can map requests.
+</p>
+
+<subsection name="Workers Type">
+<p>
+Each named worker should also have a few entries to provide additional information on his behalf.
+This information includes the worker's type and other related worker information. 
+Currently the following worker types that exists are (JK 1.2.5):
+</p>
+
+<table>
+  <tr><th>Type</th><th>Description</th></tr>
+  <tr><td>ajp12</td><td>This worker knows how to forward requests to out-of-process Tomcat workers using the ajpv12 protocol.</td></tr>
+  <tr><td>ajp13</td><td>This worker knows how to forward requests to out-of-process Tomcat workers using the ajpv13 protocol.</td></tr>
+  <tr><td>jni</td><td>This worker knows how to forward requests to in-process Tomcat workers using JNI.</td></tr>
+  <tr><td>lb</td><td>This is a load-balancing worker; it knows how to provide round-robin based sticky load balancing with a certain level of fault-tolerance.</td></tr>
+</table>
+
+<p>
+Defining workers of a certain type should be done with the following property format:
+</p>
+
+<p>
+<b>worker</b>. <b>worker name</b>.<b>type</b>=&lt;worker type&gt;
+Where worker name is the name assigned to the worker and the worker type is one of the four types defined 
+in the table (a worker name may not contain any space (a good naming convention for queue named should 
+follow the Java variable naming rules).
+</p>
+
+<screen>
+<note>Defines a worker named "local" that uses the ajpv12 protocol to forward requests to a Tomcat process.</note>
+<read>worker.local.type=ajp12</read>
+<note>Defines a worker named "remote" that uses the ajpv13 protocol to forward requests to a Tomcat process.</note>
+<read>worker.remote.type=ajp13</read>
+<note>Defines a worker named "fast" that uses JNI to forward requests to a Tomcat process.</note>
+<read>worker.fast.type=jni</read>
+<note>Defines a worker named "loadbalancer" that loadbalances several Tomcat processes transparently.</note>
+<read>worker.loadbalancer.type=lb</read>
+</screen>
+
+</subsection>
+
+</section>
+
+<section name="Setting Worker Properties">
+<p>
+After defining the workers you can also specify properties for them. 
+Properties can be specified in the following manner:
+</p>
+
+<p>
+worker.&lt;worker name&gt;.&lt;property&gt;=&lt;property value&gt;
+</p>
+
+Each worker has a set of properties that you can set as specified in the following subsections:
+
+<subsection name="ajp12 Worker properties">
+<p>
+The ajp12 typed workers forward requests to out-of-process Tomcat workers 
+using the ajpv12 protocol over TCP/IP sockets.
+</p>
+
+<p>
+the ajp12 worker properties are :
+</p>
+
+<p>
+<b>host</b> property set the host where the Tomcat worker is listening for ajp12 requests.
+</p>
+
+<p>
+<b>port</b> property set The port where the Tomcat worker is listening for ajp12 requests
+</p>
+
+<p>
+<b>lbfactor</b>property is used when working with a load balancer worker, this is the load-balancing factor for the worker.
+We'll see more on this in the lb worker section.
+</p>
+
+<screen>
+<note>worker "worker1" will talk to Tomcat listening on machine www.x.com at port 8007 using 2.5 lb factor</note>
+<read>worker.worker1.host=www.x.com</read>
+<read>worker.worker1.port=8007</read>
+<read>worker.worker1.lbfactor=2.5</read>
+</screen>
+
+<p>
+Notes: In the ajpv12 protocol, connections are created, used and then closed at each request.
+The default port for ajp12 is 8007
+</p>
+
+</subsection>
+
+<subsection name="ajp13 Worker properties">
+<p>
+The ajp13 typed workers forward requests to out-of-process Tomcat workers using the ajpv13 protocol over TCP/IP sockets.
+The main difference between ajpv12 and ajpv13 are that:
+<ul>
+<li>
+ajpv13 is a more binary protocol and it try to compress some of the request data by coding 
+frequently used strings as small integers.
+</li>
+<li>
+ajpv13 reuse open sockets and leaves them open for future requests (remember when you've got a Firewall between your 
+WebServer and Tomcat).
+</li>
+<li>
+ajpv13 has special treatment for SSL information so that the container can implement 
+SSL related methods such as isSecure().
+</li>
+</ul>
+
+</p>
+
+<p>
+You should note that Ajp13 is now the only out-process protocol supported by Tomcat 4.0.x, 4.1.x and 5.
+</p>
+
+<p>
+The following table specifies properties that the ajp13 worker can accept:
+</p>
+
+
+<p>
+<b>host</b> property set the host where the Tomcat worker is listening for ajp13 requests.
+</p>
+
+<p>
+<b>port</b> property set The port where the Tomcat worker is listening for ajp13 requests
+</p>
+
+<p>
+<b>lbfactor</b> property is used when working with a load balancer worker, this is the load-balancing factor for the worker.
+We'll see more on this in the lb worker section.
+</p>
+
+<p>
+<b>cachesize</b> property is usefull when you're using JK in multithreaded 
+web servers such as Apache 2.0, IIS and Netscape. They will benefit the most by 
+setting this value to a higher level (such as the estimated average concurrent users for Tomcat).
+If cachesize is not set, the connection cache support is disabled.
+</p>
+
+<p>
+<b>cache_timeout</b> property should be used with <b>cachesize</b> to specify how to time JK should keep
+an open socket in cache before closing it. This property should be used to reduce the number of threads 
+on the Tomcat WebServer.
+</p>
+
+<p>
+You should know that under heavy load some WebServers, for example Apache's create many childs/threads
+to handle the load and they destroy the childs/threads only when the load decrease.
+</p>
+
+<p> 
+Each child could open an ajp13 connection if it have to forward a request to Tomcat, creating
+a new ajp13 thread on Tomcat side.
+</p>
+
+<p>
+The problem is that after an ajp13 connection is created, the child won't drop it
+until killed. And since the webserver will keep its childs/threads running
+to handle high-load, even it the child/thread handle only static contents, you could
+finish having many unused ajp13 threads on the Tomcat side.
+</p>
+
+<p>
+<b>socket_keepalive</b> property should be used when you have a firewall between your webserver
+and the Tomcat engine, who tend to drop inactive connections. This flag will told Operating System
+to send <code>KEEP_ALIVE</code> message on inactive connections (interval depend on global OS settings,
+generally 120mn), and sus prevent the firewall to cut the connection.
+</p>
+
+<p>
+The problem with Firewall cutting inactive connections is that sometimes, neither webserver or tomcat
+have informations about the cut and couldn't handle it.
+</p>
+
+<p>
+<b>socket_timeout</b> property told webserver to cut an ajp13 connection after some time of 
+inactivity. When choosing an endpoint for a request and the assigned socket is open, it will be
+closed if it was not used for the configured time.
+It's a good way to ensure that there won't too old threads living on Tomcat side, 
+with the extra cost you need to reopen the socket next time a request be forwarded.
+This property is very similar to <b>cache_timeout</b> but works also in non-cache mode.
+</p>
+
+<p>
+<b>connect_timeout</b> property told webserver to send a PING request on ajp13 connection after 
+connection is established. The parameter is the delay in milliseconds to wait for the PONG reply.
+</p>
+<p>
+This features has been added in <b>jk 1.2.6</b> to avoid problem with hung tomcat's and require ajp13 
+ping/pong support which has been implemented on Tomcat <b>3.3.2+, 4.1.28+ and 5.0.13+</b>.
+Disabled by default.
+</p>
+
+<p>
+<b>prepost_timeout</b> property told webserver to send a PING request on ajp13 connection before 
+forwarding to it a request. The parameter is the delay in milliseconds to wait for the PONG reply.
+</p>
+<p>
+This features has been added in <b>jk 1.2.6</b> to avoid problem with hung tomcat's and require ajp13 
+ping/pong support which has been implemented on <b>Tomcat 3.3.2+, 4.1.28+ and 5.0.13+</b>.
+Disabled by default.
+</p>
+
+<p>
+<b>reply_timeout</b> property told webserver to wait some time for reply to a forwarded request
+before considering the remote tomcat is dead and eventually switch to another tomcat in a cluster
+group. By default webserver will wait forever which could be an issue for you.
+The parameter is the number of milliseconds to wait for reply, so adjust it carrefully if you 
+have long running servlets.
+</p>
+<p>
+This features has been added in <b>jk 1.2.6</b> to avoid problem with hung tomcat's and works on all
+servlet engines supporting ajp13.
+Disabled by default.
+</p>
+
+<p>
+<b>recovery_options</b> property told webserver how to handle recovery when
+it detect that tomcat failed.
+By default, webserver will forward the request to another tomcat in LB mode
+(or to another ajp thread in ajp13 mode).
+values are : 0 (full recovery), 1 (don't recover if tomcat failed after getting the request),
+2 (don't recover if tomcat failed after sending the headers to client), 3 (don't recover if tomcat failed 
+getting the request or after sending the headers to client).
+</p>
+<p>
+This features has been added in <b>jk 1.2.6</b> to avoid problem with hung/broken tomcat's 
+and works on all servlet engines supporting ajp13.
+Full recovery by default.
+</p>
+
+<screen>
+<note>worker "worker2" will talk to Tomcat listening on machine www2.x.com at port 8009 using 3.5 lb factor</note>
+<read>worker.worker2.host=www2.x.com</read>
+<read>worker.worker2.port=8009</read>
+<read>worker.worker2.lbfactor=3.5</read>
+<note>worker "worker2" use up to 10 sockets, which will stay no more than 10mn in cache</note>
+<read>worker.worker2.cachesize=10</read>
+<read>worker.worker2.cache_timeout=600</read>
+<note>worker "worker2" ask operating system to send KEEP-ALIVE signal on the connection</note>
+<read>worker.worker2.socket_keepalive=1</read>
+<note>worker "worker2" want ajp13 connection to be dropped after 5mn (timeout)</note>
+<read>worker.worker2.socket_timeout=300</read>
+</screen>
+
+<p>
+Notes: In the ajpv13 protocol, the default port is 8009
+</p>
+
+</subsection>
+
+<subsection name="lb Worker properties">
+<p>
+The load-balancing worker does not really communicate with Tomcat workers.
+Instead it is responsible for the management of several "real" workers. 
+This management includes:
+</p>
+
+<ul>
+<li>
+Instantiating the workers in the web server.
+</li>
+<li>
+Using the worker's load-balancing factor, perform weighed-round-robin load balancing where 
+high lbfactor means stronger machine (that is going to handle more requests)
+</li>
+<li>
+Keeping requests belonging to the same session executing on the same Tomcat worker.
+</li>
+<li>
+Identifying failed Tomcat workers, suspending requests to them and instead fall-backing on 
+other workers managed by the lb worker.
+</li>
+</ul>
+
+<p>
+The overall result is that workers managed by the same lb worker are load-balanced (based on their lbfactor and current user session) and also fall-backed so a single Tomcat process death will not "kill" the entire site.
+The following table specifies properties that the lb worker can accept:
+<ul>
+<li><b>balanced_workers</b> is a comma separated list of workers that the load balancer need to manage. 
+These workers should not appear in the worker.list property.</li>
+<li><b>sticky_session</b> specifies whether requests with SESSION ID's should be routed back to the same
+Tomcat worker. If sticky_session is an int and is not 0 it is set to JK_TRUE and sessions are sticky, otherwise
+sticky_session is set to false. Set sticky_session to JK_FALSE when Tomcat is using a Session Manager which
+can persist session data across multiple instances of Tomcat. By default sticky_session is set to JK_TRUE.</li>
+</ul>
+</p>
+
+<screen>
+<note> The worker balance1 while use "real" workers worker1 and worker2</note>
+<read>worker.balance1.balanced_workers=worker1, worker2</read>
+</screen>
+
+</subsection>
+
+<subsection name="Advanced lb Worker properties">
+<p>
+With JK 1.2.x, new load-balancing and fault-tolerant support has been added via
+2 new properties, <b>local_worker_only</b> and <b>local_worker</b>.
+</p>
+
+<p>
+Let's take an example environment:
+</p>
+
+<p>
+A cluster with two nodes (worker1+worker2), running a webserver + tomcat tandem on each node and 
+a loadbalancer in front of the nodes.
+</p>
+
+<screen>
+<note>The advanced router LB worker</note>
+<read>worker.list=router</read>
+<note># Define a 'local_worker' worker using ajp13</note>
+<read>worker.worker1.port=8009</read>
+<read>worker.worker1.host=node1.domain.org</read>
+<read>worker.worker1.type=ajp13</read>
+<read>worker.worker1.lbfactor=1</read>
+<read>worker.worker1.local_worker=1</read>
+<note># Define another 'local_worker' worker using ajp13</note>
+<read>worker.worker2.port=8009</read>
+<read>worker.worker2.host=node2.domain.org</read>
+<read>worker.worker2.type=ajp13</read>
+<read>worker.worker2.lbfactor=1</read>
+<read>worker.worker2.local_worker=0</read>
+<note># Define the LB worker</note>
+<read>worker.router.type=lb</read>
+<read>worker.router.balanced_workers=worker1,worker2</read>
+<read>worker.router.local_worker_only=1</read>
+</screen>
+
+<p>
+The <b>local_worker</b> flag on worker1 and worker2 tells the <b>lb_worker</b> which connections are going 
+to the local worker. 
+</p>
+
+<p>
+If local_worker is an int and is not 0 it is set to JK_TRUE and marked as local worker, JK_FALSE otherwise. 
+If in minimum one worker is marked as local worker, lb_worker is in local worker mode. 
+All local workers are moved to the beginning of the internal worker list in lb_worker during validation.
+</p>
+
+<p>
+This means that if a request with a session id comes in it would be routed to the appropriate worker. 
+If this worker is down it will be send to the first local worker which is not in error state.
+</p>
+
+<p>
+If a request without a session comes in, it would be routed to the first local worker. 
+If all local worker are in error state, then the 'local_worker_only' flag is important. 
+If it was set to an int and this wasn't 0 it is set to JK_TRUE, JK_FALSE otherwise. With set to JK_TRUE, this request gets an error response. If set to JK_FALSE lb_worker tries to route the request to another balanced worker.
+</p>
+
+<p>
+If one of the worker was in error state and has recovered nothing changes. 
+The local worker will be check for requests without a session id (and with a session on himself) and 
+the other worker will only be checked if a request with a session id of this worker comes in.
+</p>
+
+<p>
+Why do we need souch a complex behavior ?
+</p>
+
+<p>
+We need a graceful shut down of a node for maintenance. The balancer in front asks a special port on each 
+node periodically. If we want to remove a node from the cluster, we switch off this port. 
+The loadbalancer can't connect to it and marks the node as down. 
+But we don't move the sessions to another node. In this environment it is an error if the balancer sends a request without a session to an apache+mod_jk+tomcat which port is switched off. And if the load balancer determines that a node is down no other node is allowed to send a request without a session to it. Only requests with old sessions on the switched off node would be routed to this node. After some time nobody uses the old sessions and the sessions will time out. Then nobody uses this node, because all session are gone and the node is unreachable without a session-id in the request. If someone uses a session which timed out, our servlet system sends a redirect response without a session id to the browser. This is necessary for me, because on a switched off node apache and tomcat can still be up and running, but they are in an old state and should only be asked for valid old sessions. After the last session timed out, I could update the node etc. without killing sessions or moving them to another node. Sometimes we have a lot of big objects in our sessions, so it would be really time consuming to move them.
+</p>
+
+<p>
+The defaults are still local_worker: 0 and local_worker_only:0
+</p>
+
+</subsection>
+
+<subsection name="jni Worker properties">
+<p>
+The jni worker opens a JVM inside the web server process and executes Tomcat within it (that is in-process). 
+Following that, messages to and from the JVM are passed using JNI method calls, this makes the jni worker faster 
+then the out-of-process workers that need to communicate to the Tomcat workers by writing AJP messages over 
+TCP/IP sockets.
+</p>
+
+<p>
+Note: Since the JVM is multithreaded; the jni worker should be used only within multithreaded servers 
+such as AOLServer, IIS, Netscape and Apache 2.0.<br/> 
+You should also make sure that the threading scheme used by the web servers match the one 
+used to build the JK web server plugin.
+</p>
+
+<p>
+Since the jni worker opens a JVM it can accept many properties that it forward to the JVM such as 
+the classpath etc. as we can see in the following table.
+</p>
+
+<p>
+<b>class_path</b> is the classpath as used by the in-process JVM. This should point to all Tomcats' 
+jar/file files as well as any class or other jar file that you want to add to the JVM.
+</p>
+
+<p>
+To have JSP compile support, you should remember to also add Javac to the classpath. 
+This can be done in Java2 by adding tools.jar to the classpath. 
+In JDK1.xx you should just add classes.zip.
+</p>
+
+<p>
+The <b>class_path</b> property can be given in multiple lines. 
+In this case the JK environment will concatenate all the classpath entries together 
+by putting path delimiter (":"/";") between the entries.
+</p>
+
+<screen>
+<note>Set the classpath for worker "wrkjni"</note>
+<read>worker.wrkjni.class_path=/var/tomcat3/lib/tomcat.jar</read>
+<note>we don't forget to add JAVAC (tools.jar)</note>
+<read>worker.wrkjni.class_path=/opt/IBMJava2-131/lib/tools.jar</read>
+</screen>
+
+<p>
+<b>bridge</b> indicate the kind of Tomcat you'll use via JNI.
+</p>
+
+<p>
+The bridge property could be for now <b>tomcat32</b> or <b>tomcat33</b>.
+Tomcat 3.2.x is deprecated but still present on some distribution like iSeries. 
+By default the bridge type is set tomcat33.
+</p>
+
+<screen>
+<note>Set bridge type for "wrkjni", we'll use tomcat 3.3</note>
+<read>worker.wrkjni.bridge=tomcat33</read>
+</screen>
+
+<p>
+<b>cmd_line</b> is the command line that is handed over to Tomcats' startup code.
+</p>
+
+<p>
+The cmd_line property can be given in multiple lines. 
+In this case the JK environment will concatenate all the cmd_line entries together by putting spaces 
+between the entries.
+</p>
+
+<screen>
+<note>Set command line for "wrkjni"</note>
+<read>worker.wrkjni.cmd_line=-config</read>
+<note>Next arg</note>
+<read>worker.wrkjni.cmd_line=/etc/tomcat3/conf/alt-server.xml</read>
+<note>Very important tomcat.home</note>
+<read>worker.wrkjni.cmd_line=-home</read>
+<note>Location of tomcat.home</note>
+<read>worker.wrkjni.cmd_line=/var/tomcat3</read>
+</screen>
+
+<p>
+<b>jvm_lib</b> is the full path to the JVM implementation library. 
+The jni worker will use this path to load the JVM dynamically.
+</p>
+
+<screen>
+<note>Set full path for JVM shared lib (IBM SDK on Linux)</note>
+<read>worker.wrkjni.jvm_lib=/opt/IBMJava2-131/jre/bin/classic/libjvm.so</read>
+<note>Set full path for JVM shared lib (Sun SDK on Windows)</note>
+<read>worker.wrkjni.jvm_lib=c:\JDK\1.3.1\jre\bin\classic</read>
+</screen>
+
+<p>
+<b>stdout</b> is full path to where the JVM write its System.out
+</p>
+
+<screen>
+<note>Put logs in /var/log/http/jk-jvm-out.log</note>
+<read>worker.wrkjni.stdout=/var/log/http/jk-jvm-out.log</read>
+</screen>
+
+<p>
+<b>stderr</b> is full path to where the JVM write its System.err
+</p>
+
+<screen>
+<note>Put logs in /var/log/http/jk-jvm-err.log</note>
+<read>worker.wrkjni.stderr=/var/log/http/jk-jvm-err.log</read>
+</screen>
+
+<p>
+<b>ms</b> set initial HEAP size for the JVM
+</p>
+
+<screen>
+<note>Told JVM to use 64MB of initial heap size</note>
+<read>worker.wrkjni.ms=64</read>
+</screen>
+
+<p>
+<b>mx</b> set maximal HEAP size for the JVM
+</p>
+
+<screen>
+<note>Told JVM to not use more than 128MB for heap</note>
+<read>worker.wrkjni.mx=128</read>
+</screen>
+
+<p>
+<b>sysprops</b> set the system properties for the JVM
+</p>
+
+<screen>
+<note>Told JVM to use french language</note>
+<read>worker.wrkjni.sysprops=-Duser.region=FR</read>
+</screen>
+
+<p>
+<b>ld_path</b> set the additional dynamic libraries path (similar in nature to LD_LIBRARY_PATH)
+</p>
+
+<screen>
+<note>Told system which library path to be added to access Java Env</note>
+<read>worker.wrkjni.ld_path=/opt/IBMJava2-131/jre/bin/</read>
+<read>worker.wrkjni.ld_path=/opt/IBMJava2-131/jre/bin/classic</read>
+</screen>
+
+<p>
+Notes: Under Linux it seems that processes can't update their own LD_LIBRARY_PATH,
+so you'll have to update it BEFORE launching the webserver...
+</p>
+
+</subsection>
+
+<subsection name="Property file macros">
+<p>
+You can define "macros" in the property files. 
+These macros let you define properties and later on use them while 
+constructing other properties and it's very usefull when you want to
+change your Java Home, Tomcat Home or OS path separator
+</p>
+
+<screen>
+<note>property example, don't hardcode path separator</note>
+ps=\
+<read>workers.tomcat_home=d:\tomcat</read>
+<read>workers.java_home=d:\sdk\jdk1.2.2</read>
+<note>Using macros we'll have : worker.inprocess.class_path=d:\tomcat\classes</note>
+<read>worker.inprocess.class_path=$(workers.tomcat_home)$(ps)classes</read>
+<note>Using macros we'll have : worker.inprocess.class_path=d:\sdk\jdk1.2.2\lib\tools.jar</note>
+<read>worker.inprocess.class_path=$(workers.java_home)$(ps)lib$(ps)tools.jar</read>
+</screen>
+
+</subsection>
+
+</section>
+
+<section name="A sample worker.properties">
+<p>
+Since coping with worker.properties on your own is not an easy thing to do, 
+a sample worker.properties file is bundled along JK. 
+</p>
+
+<p>
+You could also find here a sample workers.properties defining :
+</p>
+
+<ul>
+<li>
+An ajp12 worker that used the host localhost and the port 8007
+</li>
+<li>
+An ajp13 worker that used the host localhost and the port 8008
+</li>
+<li>
+A jni worker
+</li>
+<li>
+A lb worker that load balance the ajp12 and ajp13 workers
+</li>
+</ul>
+
+<screen>
+<note># Define some properties</note>
+<read>workers.apache_log=/var/log/httpd/</read>
+<read>workers.tomcat_home=/var/tomcat3</read>
+<read>workers.java_home=/opt/IBMJava2-131/</read>
+<read>ps=/</read>
+<note># Define 4 workers, 3 real workers using ajp12, ajp13, jni, the last one being a loadbalancing worker</note> 
+<read>worker.list=worker1, worker2, worker3, worker4</read>
+<note># Set properties for worker1 (ajp12)</note>
+<read>worker.worker1.type=ajp12</read>
+<read>worker.worker1.host=locahost</read>
+<read>worker.worker1.port=8007</read>
+<read>worker.worker1.lbfactor=5</read>
+<note># Set properties for worker2 (ajp13)</note>
+<read>worker.worker2.type=ajp13</read>
+<read>worker.worker2.host=locahost</read>
+<read>worker.worker2.port=8009</read>
+<read>worker.worker2.lbfactor=50</read>
+<read>worker.worker2.cachesize=10</read>
+<read>worker.worker2.cache_timeout=600</read>
+<read>worker.worker2.socket_keepalive=1</read>
+<read>worker.worker2.socket_timeout=300</read>
+<note># Set properties for worker3 (jni)</note>
+<read>worker.worker3.type=jni</read>
+<note># Set worker3 bridge type, here Tomcat 3.3</note>
+<read>worker.worker3.bridge=tomcat33</read>
+<note># Set worker3 classpath</note>
+<read>worker.worker3.class_path=$(workers.tomcat_home)$(ps)classes</read>
+<read>worker.worker3.class_path=$(workers.tomcat_home)$(ps)lib$(ps)tomcat.jar</read>
+<note># Set worker3 tomcat command line</note>
+<read>worker.worker3.cmd_line=-home</read>
+<read>worker.worker3.cmd_line=$(workers.tomcat_home)</read>
+<note># Set worker3 Tomcat/JVM settings</note>
+<read>worker.worker3.jvm_lib=$(workers.java_home)$(ps)jre$(ps)bin$(ps)classic$(ps)libjvm.so</read>
+<read>worker.worker3.stdout=$(workers.apache_log)$(ps)inprocess.stdout</read>
+<read>worker.worker3.stderr=$(workers.apache_log)$(ps)inprocess.stderr</read>
+<read>worker.worker3.sysprops=tomcat.home=$(workers.tomcat_home)</read>
+<note># Set properties for worker4 (lb) which use worker1 and worker2</note>
+<read>worker.worker4.balanced_workers=worker1,worker2</read>
+</screen>
+
+</section>
+
+</document>
diff --git a/connectors/jk/xdocs/jk2/confighowto.xml b/connectors/jk/xdocs/jk2/confighowto.xml
new file mode 100644
index 0000000..05a5cf5
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/confighowto.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>Quick Start JK2 Configuration Guide</title>
+<author email="mturk@mappingsoft.com">Mladen Turk</author>
+<date>$Date$</date>
+</properties>
+<section name="Introduction">
+<p>
+  This document describes the configuration files used by JK2 on the
+  Tomcat and Web Server side:
+    <ul>
+    <li>
+    <b>jk2.properties</b> is used on the Tomcat side. It's installation path in
+    the $TOMCAT_HOME/conf directory.
+    </li>
+    <li>
+    <b>workers2.properties</b> is used on the webserver side. For the Apache
+    servers the default path is in the ServerRoot/conf directory.
+    </li>
+    </ul>    
+  Although JK2 has many things in common with mod_jk, the configuration is quite
+  different, and all the directives should be inside the workers2.properties.
+  This enables JK2 to have the same configuration file no mater what the web server
+  it's connected to.
+</p>
+</section>
+
+<section name="Minimum Configuration">
+<p>
+    Minimum configuration is the simplest one to make the JK2 working. The used
+    channel will be socket, and lot of options are used by default. Both the
+    Tomcat and web server are on the same computer.
+</p>
+<p>
+jk2.properties:
+<source>
+# The default port is 8009 but you can use another one
+# channelSocket.port=8019
+</source>
+That is all needed on the Tomcat side to configure the JK2.
+</p>
+<p>
+workers2.properties:
+<source>
+# Define the communication channel 
+[channel.socket:localhost:8009]
+info=Ajp13 forwarding over socket
+tomcatId=localhost:8009
+
+# Map the Tomcat examples webapp to the Web server uri space
+[uri:/examples/*]
+info=Map the whole webapp
+</source>
+</p>
+<p>
+Start the Tomcat and Web server and browse to the <a>http://localhost/examples/</a>
+</p>
+</section>
+
+<section name="Minimum JNI Configuration">
+<p>
+    Minimum JNI configuration is the simplest one to make the Tomcat working
+    from inside the web server as inprocess. The only comunication channel used
+    is JNI. The JK2 will register all the native calls by itself, so there is
+    no need to specify the native library on Java side.
+</p>
+<p>
+jk2.properties:
+<source>
+# Add the apr and channelJni to the list of handlers
+handler.list=apr,request,container,channelJni
+# The native libraries will be registered by JK2
+apr.jniModeSo=inprocess
+</source>
+</p>
+<p>
+workers2.properties:
+<source>
+# Define the comunication channel 
+[channel.jni:jni]
+info=The jni channel, used if tomcat is started inprocess
+
+# Define the parameters for the Java Virtual Machine
+[vm:]
+info=Parameters used to load a JVM in the server process
+OPT=-Djava.class.path=${TOMCAT_HOME}/lib/tomcat-jni.jar;${TOMCAT_HOME}/lib/tomcat.jar
+OPT=-Dtomcat.home=${TOMCAT_HOME}
+OPT=-Dcatalina.home=${TOMCAT_HOME}
+OPT=-Xmx128M
+
+# JNI worker startup handler
+[worker.jni:onStartup]
+info=Command to be executed by the VM on startup. This one will start tomcat.
+class=org/apache/jk/apr/TomcatStarter
+ARG=start
+stdout=${serverRoot}/logs/stdout.log
+stderr=${serverRoot}/logs/stderr.log
+
+# JNI worker shutdown handler
+[worker.jni:onShutdown]
+info=Command to be executed by the VM on shutdown. This one will stop tomcat.
+class=org/apache/jk/apr/TomcatStarter
+ARG=stop
+
+# Map the Tomcat examples webapp to the Web server uri space
+[uri:/examples/*]
+info=Map the whole webapp
+</source>
+</p>
+<p>
+Start the Web server and browse to the <a>http://localhost/examples/</a>
+</p>
+
+</section>
+
+</document>
\ No newline at end of file
diff --git a/connectors/jk/xdocs/jk2/configtc.xml b/connectors/jk/xdocs/jk2/configtc.xml
new file mode 100644
index 0000000..653326f
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/configtc.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+	<properties>
+		<title>Configuration options</title>
+		<author email="jfrederic.clere@fujitsu-siemens.com">Jean-Frederic Clere</author>
+		<date>$Date$</date>
+	</properties>
+	<section name="Intro">
+		<p>
+  This document describes the configuration file used by mod_jk2 on the
+  Tomcat site. Its default name is ${jkHome}/conf/jk2.properties,
+  where ${jkHome} is the well known ${catalina.base} property.
+</p>
+	</section>
+	<section name="Config File Format">
+	<p> settings are specified in the following format:
+	<source>
+	handler.propertie=value
+	</source>
+	</p></section>
+	<section name="Generic properties">
+		<p>
+			<table>
+				<tr>
+					<th>Property name</th>
+					<th>Default</th>
+					<th>Description</th>
+				</tr>
+				<tr>
+					<td>handler.list</td>
+					<td>request,container,channelSocket</td>
+					<td>Handlers to load.</td>
+				</tr>
+				<tr>
+					<td>class.myhandler</td>
+					<td>No default value</td>
+					<td>Define the class of the handler myhandler.</td>
+				</tr>
+			</table>
+		</p>
+	</section>
+</document>
diff --git a/connectors/jk/xdocs/jk2/configtccom.xml b/connectors/jk/xdocs/jk2/configtccom.xml
new file mode 100644
index 0000000..8a01998
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/configtccom.xml
@@ -0,0 +1,314 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+	<properties>
+		<title>Coyote/JK2 Handlers</title>
+		<author email="jfrederic.clere@fujitsu-siemens.com">Jean-Frederic Clere</author>
+		<date>$Date$</date>
+	</properties>
+	<section name="apr">
+		<p>
+APR descriptor
+</p>
+		<p>
+			<table>
+				<tr>
+					<th>Property name</th>
+					<th>Default</th>
+					<th>Description</th>
+				</tr>
+				<tr>
+					<td>NativeSo</td>
+					<td>jkjni</td>
+					<td>
+    Location of the jkjni dynamic library.
+    It is searched in java.library.path but a absolute path can be specified.
+  </td>
+				</tr>
+				<tr>
+					<td>jniModeSo</td>
+					<td>inprocess</td>
+					<td>
+    If set to "inprocess" the jk2 will regiter native library functions by itself.
+	If not then it has to be the absolute path of the jkjni dynamic library.
+  </td>
+				</tr>
+				<tr>
+					<td>baseDir</td>
+					<td></td>
+					<td></td>
+				</tr>
+				<tr>
+					<td>aprHome</td>
+					<td></td>
+					<td></td>
+				</tr>
+				<tr>
+					<td>soExt</td>
+					<td></td>
+					<td></td>
+				</tr>
+			</table>
+		</p>
+	</section>
+	<section name="channelSocket">
+		<p>
+					A communication transport from a remote Web Server.
+</p>
+		<p>
+			<table>
+				<tr>
+					<th>Property name</th>
+					<th>Default</th>
+					<th>Description</th>
+				</tr>
+				<tr>
+					<td>port</td>
+					<td>8009</td>
+					<td>First port where Tomcat is listening</td>
+				</tr>
+				<tr>
+					<td>address</td>
+					<td>127.0.0.1</td>
+					<td>Local address where Tomcat is listening.</td>
+				</tr>
+				<tr>
+					<td>maxPort</td>
+					<td>port+10</td>
+					<td>Max port used to listen.</td>
+				</tr>
+				<tr>
+					<td>maxThreads</td>
+					<td></td>
+					<td></td>
+				</tr>
+				<tr>
+					<td>backLog</td>
+					<td></td>
+					<td></td>
+				</tr>
+				<tr>
+					<td>tcpNoDelay</td>
+					<td></td>
+					<td></td>
+				</tr>
+				<tr>
+					<td>soTimeout</td>
+					<td></td>
+					<td></td>
+				</tr>
+				<tr>
+					<td>soLinger</td>
+					<td></td>
+					<td></td>
+				</tr>
+				<tr>
+					<td>serverTimeout</td>
+					<td></td>
+					<td></td>
+				</tr>
+			</table>
+		</p>
+	</section>
+	<section name="channelUnix">
+		<p>	A AF_UNIX socket communication transport from a local Web Server.</p>
+		<p>
+			<table>
+				<tr>
+					<th>Property name</th>
+					<th>Default</th>
+					<th>Description</th>
+				</tr>
+				<tr>
+					<td>file</td>
+					<td>No default value</td>
+					<td>
+    Name of the "file" associate with the socket.
+    That must be absolut path name.
+  </td>
+				</tr>
+			</table>
+		</p>
+	</section>
+	<section name="channelJni">
+		<p>A in Web Server process communication.</p>
+		<p>
+			<table>
+				<tr>
+					<th>Property name</th>
+					<th>Default</th>
+					<th>Description</th>
+				</tr>
+			</table>
+		</p>
+	</section>
+	<section name="mx">
+		<p>mx4j adapter.</p>
+		<p>
+			<table>
+				<tr>
+					<th>Property name</th>
+					<th>Default</th>
+					<th>Description</th>
+				</tr>
+				<tr>
+					<td>port</td>
+					<td>-1 (Disabled)</td>
+					<td>Port Number.</td>
+				</tr>
+			</table>
+		</p>
+	</section>
+	<section name="shm">
+		<p>				shared memory objects handler.</p>
+		<p>
+			<table>
+				<tr>
+					<th>Property name</th>
+					<th>Default</th>
+					<th>Description</th>
+				</tr>
+				<tr>
+					<td>file</td>
+					<td>/tmp/shm.file</td>
+					<td>Shared memory file.</td>
+				</tr>
+				<tr>
+					<td>host</td>
+					<td>localhost</td>
+					<td>Host name.</td>
+				</tr>
+				<tr>
+					<td>port</td>
+					<td>8009</td>
+					<td>Port number.</td>
+				</tr>
+				<tr>
+					<td>unixSocket</td>
+					<td>No default value</td>
+					<td>Unix socket where tomcat is listening.</td>
+				</tr>
+			</table>
+		</p>
+	</section>
+	<section name="request">
+		<p>Request handler</p>
+		<p>
+			<table>
+				<tr>
+					<th>Property name</th>
+					<th>Default</th>
+					<th>Description</th>
+				</tr>
+				<tr>
+					<td>tomcatAuthentication</td>
+					<td>true</td>
+					<td>the request handler will get the authenticated user from the HTTP server, honoring any auth done there.</td>
+				</tr>
+				<tr>
+					<td>ajpidDir</td>
+					<td></td>
+					<td></td>
+				</tr>
+				<tr>
+					<td>decodedUri</td>
+					<td></td>
+					<td></td>
+				</tr>
+				<tr>
+					<td>secret</td>
+					<td></td>
+					<td></td>
+				</tr>
+				<tr>
+					<td>useSecret</td>
+					<td></td>
+					<td></td>
+				</tr>
+			</table>
+		</p>
+	</section>
+	<section name="modeler">
+		<p>????</p>
+		<p>
+			<table>
+				<tr>
+					<th>Property name</th>
+					<th>Default</th>
+					<th>Description</th>
+				</tr>
+				<tr/>
+			</table>
+		</p>
+	</section>
+	<section name="container">
+		<p>????</p>
+		<p>
+			<table>
+				<tr>
+					<th>Property name</th>
+					<th>Default</th>
+					<th>Description</th>
+				</tr>
+				<tr/>
+			</table>
+		</p>
+	</section>
+	<section name="modjk">
+		<p>????</p>
+		<p>
+			<table>
+				<tr>
+					<th>Property name</th>
+					<th>Default</th>
+					<th>Description</th>
+				</tr>
+				<tr>
+					<td>pass</td>
+					<td></td>
+					<td></td>
+				</tr>
+				<tr>
+					<td>statusPath</td>
+					<td></td>
+					<td></td>
+				</tr>
+				<tr>
+					<td>updateInterval</td>
+					<td></td>
+					<td></td>
+				</tr>
+				<tr>
+					<td>user</td>
+					<td></td>
+					<td></td>
+				</tr>
+				<tr>
+					<td>webServerHost</td>
+					<td></td>
+					<td></td>
+				</tr>
+				<tr>
+					<td>webServerPort</td>
+					<td></td>
+					<td></td>
+				</tr>
+			</table>
+		</p>
+	</section>
+</document>
+
diff --git a/connectors/jk/xdocs/jk2/configtcex.xml b/connectors/jk/xdocs/jk2/configtcex.xml
new file mode 100644
index 0000000..83d6f10
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/configtcex.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>Examples</title>
+<author email="jfrederic.clere@fujitsu-siemens.com">Jean-Frederic Clere</author>
+<date>$Date$</date>
+</properties>
+
+<section name="jk2.properties">
+<p>
+The examples below are working when the Web Server is configured according the 
+examples described in the configweb file.
+</p>
+<subsection name="using normal socket">
+<p>
+There is no need to use the jkjni logic to use normal socket, so that just for
+Fun.
+</p>
+
+<p>
+<source>
+# list of needed handlers.
+handler.list=apr,channelSocket,request
+
+# Override the default port for the channelSocket
+channelSocket.port=8019
+
+# Dynamic library
+apr.NativeSo=/home1/jakarta/jakarta-tomcat-connectors/jk/build/jk2/apache2/jkjni.so
+</source>
+</p>
+</subsection>
+
+<subsection name="using AF_UNIX socket">
+<p>
+Create and listen on a AF_UNIX socket. The location of the socket must be the
+same in the Web Server configuration file.
+</p>
+
+<p>
+<source>
+# list of needed handlers.
+handler.list=apr,channelUnix,request
+
+# Location of the socket.
+channelUnix.file=${jkHome}/work/jk2.socket
+
+# Dynamic library
+jtc=/home1/jakarta/jakarta-tomcat-connectors
+apr.NativeSo=${jtc}/jk/build/jk2/apache2/jkjni.so
+</source>
+</p>
+</subsection>
+
+<subsection name="using user defined class for communication">
+<p>
+It is possible to have a user defined class for the communication.
+Here we have used the ChannelUn as example.
+</p>
+
+<p>
+<source>
+# Define our own handler.
+class.mychannel=org.apache.jk.common.ChannelUn
+# list of needed handlers.
+handler.list=apr,mychannel,request
+
+# Location of the socket.
+channelUnix.file=${jkHome}/work/jk2.socket
+
+# Dynamic library
+jtc=/home1/jakarta/jakarta-tomcat-connectors
+apr.NativeSo=${jtc}/jk/build/jk2/apache2/jkjni.so
+</source>
+</p>
+</subsection>
+
+<subsection name="using jni channel class for communication">
+<p>
+Here we have the minimum configuration needed for the jni communication.
+</p>
+
+<p>
+<source>
+# list of needed handlers.
+handler.list=apr,request,channelJni
+
+# Dynamic library needs to be defined only if Tomcat is used
+# out of process
+jtc=/home1/jakarta/jakarta-tomcat-connectors
+apr.NativeSo=${jtc}/jk/build/jk2/apache2/jkjni.so
+# Or you can use the mod_jk2 directly
+apr.jniModeSo=/opt/apache2/modules/mod_jk2.so
+
+# If you wish to start the Tomcat from inside web server then
+# you don't need any above directive. Here is shown the default
+# value for the apr that you can ommit
+apr.jniModeSo=inprocess
+
+</source>
+</p>
+</subsection>
+
+</section>
+</document>
diff --git a/connectors/jk/xdocs/jk2/configweb.xml b/connectors/jk/xdocs/jk2/configweb.xml
new file mode 100644
index 0000000..7ac8870
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/configweb.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>Configuration file</title>
+<author email="cmanolache@yahoo.com">Costin Manolache</author>
+<author email="jfrederic.clere@fujitsu-siemens.com">Jean-Frederic Clere</author>
+<date>$Date$</date>
+</properties>
+    <section name="Intro">
+
+        <p>Jk2 uses an architecture and configuration mechanism modeled after JMX. It consist of 
+"jk_bean" components, with a registry and API that attempts to mirror JMX.</p>
+
+        <p>As in JMX, multiple config formats and stores are possible. The default is a neutral .INI-style 
+file, and Apache2 also supports configuration in httpd.conf. Other formats and repositories can be
+easily implemented, but the general concept is the same.</p>
+
+        <p>Each component has a name, a type and a set of attributes. Reasonable defaults are provided, and 
+some components are created automatically using the defaults if not explicitely configured. 
+You need to specify the config only where you want to override the defaults.</p>
+
+    </section>
+
+    <section name="Config file format">
+
+        <p>The config file is named "workers2.properties", located by default in ${serverRoot}/conf, 
+where ${serverRoot} is the web server dir, like /usr/local/apache. It is possible to modify the location
+of the file using server-specific directives.</p>
+
+        <p>Settings are grouped in sections - one section for each object. The section head is the component
+name, and must include the type and local name of the component, separated by ":". Inside each section
+you must define the attributes of the component. The attribute name is a simple string, with no '.' or
+special characters. The value is a string - no quoting is currently supported. It should be noted that
+the component name is processed to compute default for the component attributes - for example 
+[channel.socket:localhost:8009] name will create a socket channel object with host=locahost and 
+port=8009. You don't need to provide this information twice. It is highly recommended to use
+this naming scheme for consistency, even if you could use any name and then specify the properties
+explicitely.</p>
+
+        <p>The general syntax is:
+            <source>
+                [TYPE:NAME]
+                PROPERTY=VALUE
+            </source>
+        </p>
+
+        <p>It is also possible to use an alternate format, mostly for backward compatibility:
+            <source>
+                TYPE:NAME.PROPERTY=VALUE 
+            </source>
+        </p>
+    </section>
+
+    <section name="Runtime reconfiguration">
+
+      <p>The main purpose of this reconfiguration is to implement "graceful" shutdown and 
+to allow adding or disabling of more tomcat instances in the load balancing mode</p>
+
+      <p>Each component has a "ver" attribute - it is initially set to the value in the 
+config file or 0 if this is not specified. Every time the config file is read, jk will 
+check the version number in the component, and reconfigure if it is different.</p>
+
+   <p>If Jk2 reloads the config file, it detect modified components using "ver" and reconfigures them.
+To avoid performance hits, the check is done only when the /jkstatus page is accessed - or 
+if the scoreboard signature changes. An access to /jkstatus will check the timestamp of the 
+config file and if a change is detected it'll reload the config file. In apache and multiprocess
+servers - this can only affect the current process, so /jkstatus will increment the scoreboard mark.
+All other processes in a multiprocess server will see the modified byte and reload before serving the next request.</p>
+
+<p>Changing the file and forcing a reload is currently the easiest way to reconfigure. A JMX proxy 
+is in experimental stage and will allow the user to perform all configuration in JMX - using same 
+tools that he uses for tomcat, and completely transparent. The internal implementation will also
+save the file - it is the cleanest way to sync multi-process web servers.
+</p>
+
+
+    </section>
+
+    <section name="Changing 'graceful' stop state">
+
+      <p>Each tomcat instance corresponds to a "channel" jk component that defines the host and port. The 
+channel can be set in a special "graceful" mode or back to active by setting the corresponding attribute.
+This mode disables sending any new requests to that tomcat instance - only requests for
+an existing session are permitted.</p>
+
+      <p>When you want to disable a tomcat instance, you should first set the channel in
+"graceful" mode, then wait until all existing sessions expire or are completed. If the sessions
+are serializable and tomcat is configured in clustering mode - you can also migrate the 
+sessions to a different instance.</p>
+
+     <p>1. Edit workers2.properties. Find the channel. Change "graceful" to "1" to disable or "0"
+        to reactivate". Increment "ver".<br></br>
+      2. Access /jkstatus page. You should see the value changed in the channel and worker info.</p>
+
+     <p>When a worker is in this state, no new requests will be sent to that worker - only requests that have an 
+explicit session ID for that particular worker. It is recommended you wait for all sessions to expire 
+before stopping the tomcat instance, or you use a session migration mechanism.
+</p> 
+
+</section>
+    <section name="Adding a new tomcat instance">
+
+     <p>1. Edit workers2.properties. Add a new channel. If you want, also add a worker.ajp entry - 
+but this is optional</p>
+     <p>2. Access the /jkstatus page or triger reloading with a program. You should see the 
+new channel displayed in the status page, and requests should start going to the new tomcat instance</p>
+
+</section>
+    <section name="Advanced: reconfiguration using JMX">
+
+        <p>This is very experimental. On tomcat side, you must enable the JMX proxy. This is done by
+setting "modjk.webServerHost" and "modjk.webServerPort" in jk2.properties to point to the web server 
+port that contains /jkstatus. ( recent versions of jk and mod_jk are required ). You can also add mx4j-tools.jar to 
+server/lib and set "mx.enable=true" in jk2.properties to enable the console, or use your favorite JMX
+console or tools. You could also select http and/or jrmp protocol, with mx.httpPort, mx.httpHost, mxjrmpPort
+and mx.jrmpPort</p>
+   
+         <p>When tomcat starts up, it'll connect to the web server and create JMX mbean proxies for each
+mod_jk component. The data will be refreshed when JMX operations are performed - a set or invoke will
+allways refresh, while get will use cached values and refresh only periodically ( 5 sec default ).</p>
+
+<p>Every time  a change is made, the config file will be written ( for persistence and to allow other 
+processes to get the same change ). The scoreboard will be changed, and then all other server processes will 
+act just like in the case of a file change. All comments will be lost - you should use "info" attributes
+in components and set "disabled" to 1 if you want to temporary disable some components.</p>   
+
+
+</section>
+    <section name="Native server configuration">
+
+<p>For Apache2 you can also use httpd.conf instead or in addition to workers2.properties.
+Other servers may support similar configuration - for example using registry or their native
+formats. This configuration mode is less tested - but provides some
+unique advantages (and disadvantages )</p>
+
+<p>I'll describe the apache2 specifics, since this is the only one implemented. We use 2 directives - JkSet
+is a top-level directive is used to set global config options, and JkUriSet is used to set options
+ for Location sections</p>
+
+<p>JkSet takes 2 parameters, the property name ( including component name ) and the value. (Note:
+probably we should change it to 3 params, and separate the component name from property )</p>
+
+<p>Each Location that has a JkUriSet will automatically create a jk2 [uri] object,
+using the Location path and the vhost. All JkUriSet directives will set attributes
+in this [uri] object, exactly like properties in a ini file section</p>
+
+<p>You can mix workers2.properties and JkUriSet - for example workers and global options
+can be set in worker2.properties, but all uri properties in httpd.conf. Some people 
+might preffer to have only one config file and use httpd.conf for all configuration.</p>
+
+<p>The biggest benefit is that Apache2 mapping is used instead of jk2 to detect the
+requests that need to be sent to tomcat. Apache2 has been optimized and tuned to
+server huge number of servers and uris - if you have only few the diference may be
+hard to notice. Some people preffer to use the httpd.conf format and some tools 
+could be better used in this mode.</p>
+
+<p>One major problem is that reconfiguration is not supported if httpd.conf is used. 
+You can still enable/disable/add workers if you use workers2.properties, and 
+you could add or change uri properties in that file. </p>
+
+</section>
+    <section name="Config generators">
+ 
+<p>There is work in progress to support automatic generation of the config file. The code is
+included in org.apache.jk.config and consist of a number of ant tasks ( that work from CLI as well)
+ that process web.xml files and generate worker2.properties or server-specific config files</p>
+
+</section>
+
+
+</document>
diff --git a/connectors/jk/xdocs/jk2/configwebcom.xml b/connectors/jk/xdocs/jk2/configwebcom.xml
new file mode 100644
index 0000000..b82f9d3
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/configwebcom.xml
@@ -0,0 +1,765 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+
+<properties>
+  <title>Components</title>
+
+  <author email="cmanolache@yahoo.com">Costin Manolache</author>
+  <author email="jfrederic.clere@fujitsu-siemens.com">Jean-Frederic Clere</author>
+  <author email="yoavs@apache.org">Yoav Shapira</author>
+
+  <date>$Date$</date>
+</properties>
+
+<section name="Intro">
+  <p>
+    Each component instance has a name, that is used for configuration and at runtime.
+    Each component has a number of configurable properties. The following rules are used:
+    <ul>
+      <li>The name is composed from the type and a local part, separated with a ':' ( example: channel.unixsocket:/tmp/jk.socket ) </li>
+      <li>The 'type' consist of '.' and ascii characters.  It is mapped to a JMX 'domain'.  </li>
+      <li>The local part consists of ascii characters and .:/; 
+
+        <p>
+          Note that '=,' are not currently allowed - a future version may support the jmx syntax by using quotes to separate the local part from the property and value ( in .properties mode we must use '=' to separate the value from type, local name and property name ).
+        </p>
+      </li>
+      <li>The property is a simple name, with no dots. </li>
+      <li>A simple form of substitution is used in values, where $(property) will be replaced with a previously defined setting. If the property has ':' in it, it'll take the value from the object, if not it'll take the value from a global map.
+      </li>
+    </ul>
+  </p>
+</section>
+
+<section name="Common properties">
+  <p>
+    Common properties for all components
+  </p>
+  <p>
+    <table>
+      <tr>
+        <th>Property name</th>
+        <th>Default</th>
+        <th>Description</th>
+      </tr>
+      <tr>
+        <td>disabled</td>
+        <td>0 (false)</td>
+        <td>"disabled" state for the component, 1=true 0=false</td>
+      </tr>
+      <tr>
+        <td>debug</td>
+        <td>0 (false)</td>
+        <td>Debug level for the component, 0=disabled, 1..10 enabled. Higher levels
+            generate more debug information.</td>
+      </tr>
+      <tr>
+        <td>ver (short for, and formerly known as, version)</td>
+        <td>0</td>
+        <td>'Generation' of the component config. Important for runtime reconfiguration.
+            If you edit the config file or set the shmem properties, you need to also
+            upgrade the version of the modified component. The config layer will detect
+            the change and call the setter method.</td>
+      </tr>
+    </table>
+  </p>
+</section>
+
+        <section name="workerEnv">
+            <p>This component represent the core jk2, it has the default logger for all other components. Is the central controller, it controls global properties
+and  provides access to all other objects</p>
+            <p>
+                <table>
+                    <tr>
+                        <th>Property name</th>
+                        <th>Default</th>
+                        <th>Description</th>
+                    </tr>
+                    <tr>
+                        <td>logger</td>
+                        <td>logger</td>
+                        <td>Default loger used by jk2 components, can be changed in the config file, normally it defaults to "logger" the Alias for the default logger for the Server/platform.</td>
+                    </tr>
+                    <tr>
+                        <td>sslEnable</td>
+                        <td>1 (true)</td>
+                        <td>Enable handling of SSL</td>
+                    </tr>
+                    <tr>
+                        <td>timing</td>
+                        <td>0</td>
+                        <td>Will jk2 get request timing (needs APR?)</td>
+                    </tr>
+                    <tr>
+                        <td>forwardKeySize</td>
+                        <td>not set</td>
+                        <td>Enable filling of javax.servlet.request.key_size</td>
+                    </tr>
+                    <tr>
+                        <td>forwardURICompat</td>
+                        <td>set</td>
+                        <td>Pass the URI untouched.</td>
+                    </tr>
+                    <tr>
+                        <td>forwardURICompatUnparsed</td>
+                        <td>not set</td>
+                        <td>Parse the URI until the '?'.</td>
+                    </tr>
+                    <tr>
+                        <td>forwardURIEscaped</td>
+                        <td>not set</td>
+                        <td>Pass the URI escaped.</td>
+                    </tr>
+                    <tr>
+                        <td>noRecoveryIfRequestSent</td>
+                        <td>set</td>
+                        <td>No recovery in LB mode if a Tomcat allready received the request.</td>
+                    </tr>
+                    <tr>
+                        <td>noRecoveryIfHeaderSent</td>
+                        <td>set</td>
+                        <td>No recovery in LB mode if a Tomcat allready start to send reply to client.</td>
+                    </tr>
+                </table>
+            </p>
+            <p>Only one of the forwardURI option could be used it replaces the default value.</p>
+        </section>
+        <section name="config">
+            <p>The config component, hold the detail of the conifg system, such config file name, create global defines</p>
+            <p>
+                <table>
+                    <tr>
+                       <th>Property name</th>
+                       <th>Default</th>
+                       <th>Description</th>
+                    </tr>
+                    <tr>
+                        <td>file</td>
+                        <td>${serverRoot}/conf/workers2.properties</td>
+                        <td>Location of the workers2.properties file</td>
+                    </tr>
+                    <tr>
+                        <td>debug</td>
+                        <td>0</td>
+                        <td>Set the debug level of the config component</td>
+                    </tr>
+                    <tr>
+                        <td>debugEnv</td>
+                        <td>0</td>
+                        <td>Set the debug level of the hidden env component </td>
+                    </tr>
+                </table>
+            </p>
+        </section>
+        <section name="uriMap"/>
+        <section name="shm">
+            <p>Shared memory descriptor</p>
+            <p>
+                <table>
+                    <tr>
+                        <th>Property name</th>
+                        <th>Default</th>
+                        <th>Description</th>
+                    </tr>
+                    <tr>
+                        <td>file</td>
+                        <td>No default value</td>
+                        <td>Name of the file that will be mmapped to use as shared memory, If set to 'anonymous' use the anonymous shered memory</td>
+                    </tr>
+                    <tr>
+                        <td>size</td>
+                        <td>No default value</td>
+                        <td>Deprecated. Size of the file.</td>
+                    </tr>
+                    <tr>
+                        <td>slots</td>
+                        <td>256</td>
+                        <td>Number of shared memory slots. Set to the number of child processes</td>
+                    </tr>
+                    <tr>
+                        <td>useMemory</td>
+                        <td>0</td>
+                        <td>Use process memory instead of shared memory. Useful for single child mpm's</td>
+                    </tr>
+                </table>
+            </p>
+        </section>
+        <section name="uri">
+            <p>A uri stores a pattern that is used
+ to match requests to workers, and asociated properties</p>
+            <p>If the uri name doesn't have a slash then it is considered as a virtual host
+            directive. Uri name can have a virtual host name and(or) port associated with. Format
+            of such a name is <b>hostname</b> or <b>hostname:port</b> where hostname
+            is virtual server name and the port is vitual server port number. The port number
+            is used only for the non default server ports.</p>
+            <p>
+            Special case is a default server named as <b>[uri:*]</b> that is used when the virtual
+            host cannot be found inside the configuration. All the uri directives not containing
+            host name belongs to this default server making global mappings.
+            </p>
+            <p>
+            Addition wild char scheme id <b>[uri:*:port]</b> that is used when you wish to
+            match any virtual host having specified (non-default) port number, like [uri:*:443].
+            This will map all the virtual hosts no mather what is their name but that have port number 443.            
+            </p>
+            <p>
+            The order how the host names are resolved is :
+            <ul>
+                <li>Exact host name and optional non default port number</li>
+                <li>Alias matching host name and port number</li>
+                <li>*:port if the port is other then default</li>
+                <li>Default server</li>
+            </ul>
+            </p>
+            <p>
+                <table>                
+                    <tr>
+                        <th>Property name</th>
+                        <th>Default</th>
+                        <th>Description</th>
+                    </tr>
+                    <tr>
+                        <td>group</td>
+                        <td>lb:lb (The default loadbalancer)</td>
+                        <td>Name of the tomcat group or worker that will process the request corresponding to the uri. This used
+                            to be called 'worker'</td>
+                    </tr>
+                    <tr>
+                        <td>context</td>
+                        <td/>
+                        <td>the context path for this uri component (webapp style).</td>
+                    </tr>
+                    <tr>
+                        <td>servlet</td>
+                        <td/>
+                        <td>Servlet path for this mapping</td>
+                    </tr>
+                    <tr>
+                        <td>alias</td>
+                        <td/>
+                        <td>server name alias. This setting should only be used for 
+                            host uris like [uri:myHost:myPort] ( i.e. no /) </td>
+                    </tr>
+                </table>
+            </p>
+        </section>
+        <section name="vm">
+            <p>Represents the JVM when used as inprocess container
+            </p>
+            <p>
+                <table>
+                    <tr>
+                        <th>Property name</th>
+                        <th>Default</th>
+                        <th>Description</th>
+                    </tr>
+                    <tr>
+                        <td>JVM</td>
+                        <td>(Autoguess)</td>
+                        <td>JVM to use for this vm</td>
+                    </tr>
+                    <tr>
+                        <td>OPT</td>
+                        <td/>
+                        <td>Option to pass to this vm, this is a multivalued property</td>
+                    </tr>
+                    <tr>
+                        <td>classpath</td>
+                        <td/>
+                        <td>-Djava.class.path 0ption to pass to this vm, this is a multivalued property</td>
+                    </tr>
+                </table>
+            </p>
+        </section>
+        <section name="channels">
+            <p>A channel represents a transport protocol, connecting 2
+sides  for RPC communication. The most common and standard is the tcp socket.
+Other  important  channels are unix socket and jni</p>
+            <subsection name="channel.un">
+                <p>
+    AF_UNIX socket. Only on UNIX like platform. These sockets are faster
+    than "normal" sockets but they are limited to the machine. 
+</p>
+                <p>
+                    <table>
+                        <tr>
+                            <th>Property name</th>
+                            <th>Default</th>
+                            <th>Description</th>
+                        </tr>
+                        <tr>
+                            <td>file</td>
+                            <td>Name of socket</td>
+                            <td>Name of the socket file (It is created by the Tomcat ChannelUn)</td>
+                        </tr>
+                    </table>
+                </p>
+            </subsection>
+            <subsection name="channel.socket">
+                <p>
+    Defines a communication transport to a remote Servlet Engine. </p>
+
+<p>The name of the channels should be: channel.socket:HOST:PORT, where HOST and PORT are the 
+tomcat Ajp location.  You could use other names and explicitely set HOST and PORT, but this
+is discouraged. In most cases, you don't need to set any other config - just add a line like
+[channel.socket:localhost:8009] and all other things will have good defaults. 
+</p>
+<p>
+NB: Starting with JK2 2.0.4, APR is mandatory and the channel.socket use APR (previously
+called channel.apr).
+</p>
+<p>
+Tomcat Engine must be set with jvmRoute="HOST:PORT", to match the default tomcatId of the channel.
+</p>
+                <p>
+                    <table>
+                        <tr>
+                            <th>Property name</th>
+                            <th>Default</th>
+                            <th>Description</th>
+                        </tr>
+                        <tr>
+                            <td>port</td>
+                            <td>extracted from the component name</td>
+                            <td>Port where Tomcat is listening. It is automatically extracted from the name - you shouldn't have to specify it explicitely.</td>
+                        </tr>
+                        <tr>
+                            <td>host</td>
+                            <td>extracted from the component name</td>
+                            <td>Remote host. You should use the name, no need to override it</td>
+                        </tr>
+                        <tr>
+                            <td>graceful</td>
+                            <td>0</td>
+                            <td>If 1, only requests for existing sessions will be forwarded</td>
+                        </tr>
+                        <tr>
+                            <td>keepalive</td>
+                            <td>0</td>
+                            <td>? </td>
+                        </tr>
+                        <tr>
+                            <td>timeout</td>
+                            <td>0 (infinite)</td>
+                            <td>Socket timeout for sending and receiving</td>
+                        </tr>
+                        <tr>
+                            <td>ndelay</td>
+                            <td>0</td>
+                            <td>If set to 1 Disables the Nagle algorithm for send coalescing</td>
+                        </tr>
+                        <tr>
+                            <td>lb_factor</td>
+                            <td>1</td>
+                            <td>Load balancing factor to use. The lower the lb_factor the more often that tomcat will get requests but see
+                                "level" below.</td>
+                        </tr>
+                        <tr>
+                            <td>level</td>
+                            <td>1</td>
+                            <td>Worker Priority.  Valid values are 0-3.  The functioning workers with the lowest level
+                                will be checked for the lowest lb_value, and if found will be run.  The upper level workers are
+                                only checked upon failure of all workers on ALL of the levels below them.  This is very 
+                                useful for implementing failover withing a cluster.  You could set the tomcat server local 
+                                on the same machine as the apache instance to level 0 and all of the other workers to level 1.
+                                This would cause apache to only use the external tomcats when the local tomcat is down.</td>
+                        </tr>
+                        <tr>
+                            <td>group</td>
+                            <td>lb</td>
+                            <td>loadbalanced groups to which this channel and the associated worker will be added, multivalued. You need to set it only if you have an advanced setup with multiple clusters.</td>
+                        </tr>
+                        <tr>
+                            <td>tomcatId</td>
+                            <td>Automatically set to the localname ( host:port )</td>
+                            <td>Must match the JVM route on tomcat the server.xml Engine element, for load balancing</td>
+                        </tr>
+                        <tr>
+                            <td>route</td>
+                            <td>Automatically set to the localname ( host:port )</td>
+                            <td>Same as tomcatId</td>
+                        </tr>                        
+                    </table>
+                </p>
+            </subsection>
+            <subsection name="channel.jni">
+                <p>The jni channel, used if tomcat is started inprocess</p>
+            </subsection>
+        </section>
+        <section name="workers">
+            <p>
+             For the moment 4 worker types are supported: worker.jni,ajp13,status,lb.
+            </p>
+            <subsection name="worker.jni">
+                <p>worker used in inprocess, holds the details of the Tomcat class to startup, and parameters to pass</p>
+                <p>There are two predefined jni workers <b>onStartup</b> and <b>onShutdown</b>. Those two workers are executed
+                during startup and shutdown phase of the connector. Both must exists in the configuration to be able to start
+                and shutdown Tomcat.
+                </p>
+                <p>
+                    <table>
+                        <tr>
+                            <th>Property name</th>
+                            <th>Default</th>
+                            <th>Description</th>
+                        </tr>
+                        <tr>
+                            <td>class</td>
+                            <td>org/apache/jk/apr/TomcatStarter</td>
+                            <td>class that holds the main method called to start tomcat</td>
+                        </tr>
+                        <tr>
+                            <td>ARG</td>
+                            <td/>
+                            <td>Arguments to pass to main method when called</td>
+                        </tr>
+                        <tr>
+                            <td>stdout</td>
+                            <td>NULL</td>
+                            <td>file to redirect Standard output from the java process</td>
+                        </tr>
+                        <tr>
+                            <td>stderr</td>
+                            <td>NULL</td>
+                            <td>file to redirect Standard output from the java process </td>
+                        </tr>
+                    </table>
+                </p>
+            </subsection>
+            <subsection name="ajp13">
+                <p>Default worker.  If a property is in both the worker and the channel, you only need to define it in one place.
+                    The channel passes down the properties to its worker.  (at least in the ajp13-channel.socket linkage)</p>
+                <p>
+                    <table>
+                        <tr>
+                            <th>Property name</th>
+                            <th>Default</th>
+                            <th>Description</th>
+                        </tr>
+                        <tr>
+                            <td>secretkey</td>
+                            <td>NULL</td>
+                            <td>
+                                <b>Magic:</b> The secret key will be set automatically on the associated
+    worker.
+  </td>
+                        </tr>
+                        <tr>
+                            <td>tomcatId</td>
+                            <td>Automatically set to the localname ( host:port )</td>
+                            <td>Must match the JVM route on the tomcat server.xml Engine element, for load balancing</td>
+                        </tr>
+                        <tr>
+                            <td>route</td>
+                            <td>Automatically set to the localname ( host:port )</td>
+                            <td>Same as tomcatId</td>
+                        </tr>
+                        <tr>
+                            <td>group</td>
+                            <td>lb</td>
+                            <td>loadbalanced groups to which this channel and the associated worker will be added, multivalued. You need to set it only if you have an advanced setup with multiple clusters.</td>
+                        </tr>
+                        <tr>
+                            <td>lb_factor</td>
+                            <td>1</td>
+                            <td>Load balancing factor to use. The lower the lb_factor the more often that tomcat will get requests but see
+                                "level" below.</td>
+                        </tr>
+                        <tr>
+                            <td>level</td>
+                            <td>1</td>
+                            <td>Worker Priority.  Valid values are 0-3.  The functioning workers with the lowest level
+                                will be checked for the lowest lb_value, and if found will be run.  The upper level workers are
+                                only checked upon failure of all workers on ALL of the levels below them.  This is very 
+                                useful for implementing failover withing a cluster.  You could set the tomcat server local 
+                                on the same machine as the apache instance to level 0 and all of the other workers to level 1.
+                                This would cause apache to only use the external tomcats when the local tomcat is down.</td>
+                        </tr>
+                        <tr>
+                            <td>channel</td>
+                            <td/>
+                            <td>Communication channel used by the worker.  Use the full name of the channel (everything between the []'s).</td>
+                        </tr>
+                        <tr>
+                            <td>max_connections</td>
+                            <td>0 (unlimited)</td>
+                            <td>Maximum number of currently used endpoints.
+                                If the specified number is reached then the load balancer has the chance
+                                to try another worker. This is very useful in situations when having multiple
+                                servers and you wish to finer grade the lb_factor.
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>connectTimeout</td>
+                            <td>0 (no timeout)</td>
+                            <td>With such timeout set, the web-server will send a CPING request just after physical connect to the remote Tomcat 
+                                and will wait for a CPONG reply for the connectTimeout milliseconds, a guarantee that the remote Tomcat is not hang.
+                                Side effect, this round trip add a little delay at connection time and require a recent AJP13 implementation, 
+                                with support for CPING/CPONG command.
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>replyTimeout</td>
+                            <td>0 (no timeout)</td>
+                            <td>With such timeout set, the web-server will wait for Tomcat reply to a forwarded request for replyTimeout milliseconds.
+                                Another guarantee that the remote Tomcat is not hang.
+                                Warning, if you have 'normal' long running processes on Tomcat side, you shouldn't use this feature to avoid
+                                invalid errors reports or set the timeout accordingly.
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>prepostTimeout</td>
+                            <td>0 (no timeout)</td>
+                            <td>With such timeout set, the web-server will send a CPING request just before forwarding the request to the remote Tomcat 
+                                and will wait for a CPONG reply for the prepostTimeout milliseconds, a guarantee that the remote Tomcat is not hang.
+                                Side effect, this round trip add a little delay in forwarding request and require a recent AJP13 implementation, 
+                                with support for CPING/CPONG command.
+                            </td>
+                        </tr>
+                    </table>
+                </p>
+            </subsection>
+            <subsection name="status">
+                <p>An optional worker that outputs (HTML) pages with useful information to monitor JK2.</p>
+                <p>To create the worker, add a [status:] or [status:'a name'] section.</p>
+                <p>The status worker needs no 'local' name, although one can be used if desired.</p>
+                <p>To access the worker, add a [uri] section with the group= assigned to this worker.</p>
+                <p>The status worker has the following additional properties:
+                    <table>
+                        <tr>
+                            <th>Property name</th>
+                            <th>Default</th>
+                            <th>Description</th>
+                        </tr>
+                        <tr>
+                            <td>styleMode</td>
+                            <td>0 (None)</td>
+                            <td>Defines if or how the status worker will apply a Style Sheet to returned pages.</td>
+                        </tr>
+                        <tr>
+                            <td>stylePath</td>
+                            <td>NULL</td>
+                            <td>For styleMode=2 or 3, where to find the Style Sheet file.</td>
+                        </tr>
+                    </table>
+                </p>
+                <p>The syleMode setting accepts the following values:
+                    <table>
+                        <tr>
+                            <th>styleMode value</th>
+                            <th>Description</th>
+                        </tr>
+                        <tr>
+                            <td>0</td>
+                            <td>No Style settings are used. (Default)</td>
+                        </tr>
+                        <tr>
+                            <td>1</td>
+                            <td>Use the built-in Default Style Sheet. It is presented as an 'Internal Style Sheet' to HTML pages.</td>
+                        </tr>
+                        <tr>
+                            <td>2</td>
+                            <td>Use stylePath as a URI context to an external Style Sheet file to be returned by the Web Server.</td>
+                        </tr>
+                        <tr>
+                            <td>3</td>
+                            <td>Use stylePath as a file-system reference to a Style Sheet file. It is presented as an 'Internal Style
+                                Sheet' to HTML pages.</td>
+                        </tr>
+                    </table>
+                </p>
+                <p>If the stylePath has not been set, styleMode=2 and 3 are ignored.</p>
+                <p>The sylePath setting accepts the following values:
+                    <table>
+                        <tr>
+                            <th>styleMode value</th>
+                            <th>stylePath Description</th>
+                        </tr>
+                        <tr>
+                            <td>2</td>
+                            <td>A URI context to a file (e.g  /styles/jkstatus.css). It must begin with '/' and include the file name.</td>
+                        </tr>
+                        <tr>
+                            <td>3</td>
+                            <td>A file-system path (suitable for the OS in use) plus file name. Also ${serverRoot}/conf/'file name'
+                                allows the file to be located in Apache's conf directory.</td>
+                        </tr>
+                    </table>
+                </p>
+                <p>If the Style file cannot be found, Mode 2 and 3 record Apache log entries.</p>
+                <p>For styleMode=2 and 3, changes in the Style Sheet are effective on the next access to the status worker.</p>
+            </subsection>
+            <subsection name="lb">
+                <p>Loadbalanced worker</p>
+                <p>
+                    <table>
+                        <tr>
+                            <th>Property name</th>
+                            <th>Default</th>
+                            <th>Description</th>
+                        </tr>
+                        <tr>
+                            <td>worker</td>
+                            <td/>
+                            <td/>
+                        </tr>
+                        <tr>
+                            <td>noErrorHeader</td>
+                            <td>1 (true)</td>
+                            <td>If set, jk2 won't touch the headers in case of error and will let for example Apache present the ErrorDocument via mod_alias. 
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>noWorkerMsg</td>
+                            <td/>
+                            <td/>
+                        </tr>
+                        <tr>
+                            <td>noWorkerCode</td>
+                            <td>503</td>
+                            <td/>
+                        </tr>
+                        <tr>
+                            <td>hwBalanceErr</td>
+                            <td/>
+                            <td/>
+                        </tr>
+                        <tr>
+                            <td>timeout</td>
+                            <td>0 (disabled)</td>
+                            <td>If all the workers are in the error state, probably by Tomcat
+refusing any new connections due to the overload, you can set the timeout forcing lb to wait that some
+worker becomes available, instead of immediately returning error to the client. This is very useful in
+situations with high peek load. The timeout should be set to the maximum application call time, but
+not less then 1 second.
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>attempts</td>
+                            <td>3</td>
+                            <td>Number of attempts that lb will try on each worker before
+                            giving up.
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>recovery</td>
+                            <td>60 (seconds)</td>
+                            <td>Time to wait before retrying to see if the worker came out
+                            of the error state.
+                            </td>
+                        </tr>                        
+                        <tr>
+                            <td>stickySession</td>
+                            <td>1 (true)</td>
+                            <td>Sessions stick to the same worker, 1=true 0=false
+                            </td>
+                        </tr>
+                    </table>
+                </p>
+            </subsection>
+        </section>
+        <section name="loggers">
+            <p>Any connector based on jk2, at least has a default logger, that can be reached using the "logger" alias, the logger used is the more appropiate for the plataform/server combination, Apache2 under in any platform has logger.apache2 as default, IIS on his only platform uses logger.win32, and Any apache 1 install uses logger.file as default.., the config file lets you change that defaults, you can end using logger.file in IIs i.e</p>
+            <p>The properties shared by all loggers are:
+<table>
+                    <tr>
+                        <th>Property name</th>
+                        <th>Default</th>
+                        <th>Description</th>
+                    </tr>
+                    <tr>
+                        <td>level</td>
+                        <td>INFO</td>
+                        <td>Text of the log level. Strings supported: EMERG, ERROR, INFO, DEBUG</td>
+                    </tr>
+                </table>
+            </p>
+            <subsection name="logger.file">
+                <p>
+                    <table>
+                        <tr>
+                            <th>Property name</th>
+                            <th>Default</th>
+                            <th>Description</th>
+                        </tr>
+                        <tr>
+                            <td>file</td>
+                            <td>${serverRoot}/logs/jk2.log</td>
+                            <td>
+    Log file.  XXX you may be able to change this at runtime,
+               to implement rolling.
+  </td>
+                        </tr>
+                    </table>
+                </p>
+            </subsection>
+            <subsection name="logger.win32">
+                <p>logger used in the IIS server by default, it ends at native Application Event Log.</p>
+            </subsection>
+            <subsection name="logger.apache2">
+                <p>Logger used in Apache2 servers, it normally in ends in error.log </p>
+            </subsection>
+        </section>
+    <section name="How Load Balancing Works">
+        <p>The lb_factor and level properties combine to deliver a flexible static load balancing solution.
+            The level property is used to create up to four pools over workers in descending priority and lb_factor
+            is used to weight the workers within a pool.  The lower the level the more likely the worker is to be used
+            and the lower the lb_factor the more likely the worker is to be used.  Here is how the algorithm is
+            currently implemented:</p>
+        <p>
+(Assume that every worker's lb_value is set to their lb_factor)<br/>
+<br/><source>
+if (loadbalancer has a route) and (stickysession=1){<br/>
+    worker= loadbanlancer.getWorkerForRoute(route)<br/>
+    if worker.hasRedirect<br/>
+        redirect=worker.redirect<br/>
+    else if !worker.hasError<br/>
+        return worker<br/>
+    }<br/>
+<br/>
+selectedWorker=null<br/>
+foreach lb_level {<br/>
+    foreach worker in the level {<br/>
+        if worker.isNotWorking<br/>
+            continue<br/>
+        if selectedWorker == null<br/>
+            selectedWorker = worker<br/>
+            continue<br/>
+        if worker.lb_value &lt; selectedWorker.lb_value<br/>
+            selectedWorker = worker<br/>
+            continue<br/>
+    }<br/>
+    if selectedWorker !=null<br/>
+        break<br/>
+}<br/>
+<br/>
+Reenable workers in error state if the timeout has passed()<br/>
+<br/>
+if selectedWorker !=null {<br/>
+    selectedWorker.lb_value += selectedWorker.lb_factor<br/>
+    if selectedWorker.lb_value > 255 {<br/>
+        foreach worker in load_balancer.workers[selectedWorker.level]<br/>
+            worker.lb_value=worker.lb_factor<br/>
+        }<br/>
+    }<br/>
+    return selectedWorker<br/>
+}<br/></source>
+</p>
+            
+    </section>
+</document>
diff --git a/connectors/jk/xdocs/jk2/configwebex.xml b/connectors/jk/xdocs/jk2/configwebex.xml
new file mode 100644
index 0000000..34dda31
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/configwebex.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>Examples</title>
+<author email="cmanolache@yahoo.com">Costin Manolache</author>
+<author email="jfrederic.clere@fujitsu-siemens.com">Jean-Frederic Clere</author>
+<date>$Date$</date>
+</properties>
+    <section name="Sockets">
+        <p>
+The examples below are working when the Tomcat is configured according the 
+examples described in the configtc file.
+</p>
+        <subsection name="/example using normal socket">
+            <p> 
+Map /examples to the Tomcat /examples context using a normal socket. Note the 
+IP instead localhost (The JVM listens on the IPV4 address not no the IPV6).
+</p>
+            <p>
+                <source>
+[shm]
+file=${serverRoot}/logs/shm.file
+size=1048576
+
+# Example socket channel, override port and host.
+[channel.socket:localhost:8019]
+port=8019
+host=127.0.0.1
+
+# define the worker
+[ajp13:localhost:8019]
+channel=channel.socket:localhost:8019
+
+# Uri mapping
+[uri:/examples/*]
+worker=ajp13:localhost:8019
+</source>
+            </p>
+        </subsection>
+        <subsection name="/jkstatus">
+            <p>
+Map /jkstatus to the status worker.
+</p>
+            <p>
+                <source>
+[shm]
+file=${serverRoot}/logs/shm.file
+size=1048576
+
+# define the worker
+[status:status]
+
+# Uri mapping
+[uri:/jkstatus/*]
+worker=status:status
+</source>
+            </p>
+        </subsection>
+        <subsection name="/example using AF_UNIX socket">
+            <p>
+Map /examples to the Tomcat /examples context using a AF_UNIX socket.
+Socket file is create by the Tomcat becarefull when the Web Server runs in
+a different user than the Tomcat with the permission of the socket file:
+<source>
+apache20@jfcexpert:~/apache> ls -l /home1/jakarta/jakarta-tomcat-4.1/dist/work/jk2.socket
+srw-rw----    1 jakarta  jakarta         0 Jun 20 08:27 /home1/jakarta/jakarta-tomcat-4.1/dist/work/jk2.socket
+</source>
+Here the Tomcat user and the Web Server user must be in the same group.
+</p>
+            <p>
+                <source>
+[shm]
+file=${serverRoot}/logs/shm.file
+size=1048576
+
+# Example unixsocket channel.
+[channel.un:unixsocket]
+file=/home1/jakarta/jakarta-tomcat-4.1/dist/work/jk2.socket
+
+# define the worker
+[ajp13:unixsocket]
+channel=channel.un:unixsocket
+
+# Uri mapping
+[uri:/examples/*]
+worker=ajp13:unixsocket
+</source>
+            </p>
+        </subsection>
+    </section>
+    <section name="JNI">
+    </section>
+</document>
diff --git a/connectors/jk/xdocs/jk2/davhowto.xml b/connectors/jk/xdocs/jk2/davhowto.xml
new file mode 100644
index 0000000..dbdad64
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/davhowto.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<document>
+<properties>
+<title>Apache 2.x/mod-dav - Tomcat/jk2 - HOWTO</title>
+<author email="jfrederic.clere@fujitsu-siemens.com">Jean-Frederic Clere</author>
+<date>Wed Mar  3 10:31:06 CET 2004</date>
+</properties>
+<section name="Purpose">
+<p>
+Use mod_dav to modify JSP pages.
+</p>
+</section>
+
+<section name="extract of httpd.conf">
+<p>
+The Alias is just for confort ;-)
+<source>
+  Alias /examples/jsp-source /home/jfclere/jakarta-tomcat-4.1.24/webapps/examples/jsp
+  &lt;Location /examples/jsp-source&gt;
+    Dav On
+ 
+    AuthType Basic
+    AuthName DAV
+    AuthUserFile user.passwd
+ 
+    &lt;LimitExcept GET OPTIONS&gt;
+      require user admin
+    &lt;/LimitExcept&gt;
+  &lt;/Location&gt;
+
+  &lt;LocationMatch "/*.jsp"&gt;
+    JkUriSet worker ajp13:localhost:8009
+  &lt;/LocationMatch&gt;
+
+</source>
+The LocationMatch only maps the *.jsp files.
+To have also the images it is possible to the DefaultServlet by mapping /examples.
+<source>
+  &lt;Location /examples&gt;
+    JkUriSet worker ajp13:localhost:8009
+  &lt;/Location&gt;
+</source>
+Or to get the images served by httpd and not by Tomcat.
+<source>
+  Alias /examples/images /home/jfclere/jakarta-tomcat-4.1.24/webapps/examples/images
+</source>
+<source>
+</source>
+</p>
+</section>
+<section name="extract of workers2.properties">
+<p>
+The worker ajp13:localhost:8009 of the JkUriSet Directive has to be defined
+in workers2.properties.
+<source>
+# Example socket channel, override port and host.
+[channel.socket:localhost:8009]
+port=8009
+host=127.0.0.1
+ 
+# define the worker
+[ajp13:localhost:8009]
+channel=channel.socket:localhost:8009
+</source>
+</p>
+</section>
+</document>
diff --git a/connectors/jk/xdocs/jk2/installhowto.xml b/connectors/jk/xdocs/jk2/installhowto.xml
new file mode 100644
index 0000000..ad4da2f
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/installhowto.xml
@@ -0,0 +1,262 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+
+<properties>
+  <title>Installation of jk2 in the Web Server</title>
+
+  <author email="jfrederic.clere@fujitsu-siemens.com">Jean-Frederic Clere</author>
+  <author email="andy@tagish.com">Andy Armstrng</author>
+  <author email="yoavs@apache.org">Yoav Shapira</author>
+
+  <date>$Date$</date>
+</properties>
+
+  <section name="Installation">
+    <p>
+      The actual build mechanism creates the dso files in the
+      build/jk2/${servername} subdirectory of the jakarta-tomcat-connectors/jk.
+      When configure --with-jni is used 2 dso files are created.
+      These files have to be copied in the right location of the web server
+      installation.
+    </p>
+    <p>
+      JNI support in JK2 require APR, which is provded in Apache 2.0, and is
+      available for many platforms, so Apache 1.3, IIS and NES/iPlanet should be
+      able to use it
+    </p>
+    <subsection name="Apache 1.3">
+      <p>
+        In the following example Apache-1.3 is installed in
+        /home/apache13/ and the commands are executed in
+        the jakarta-tomcat-connectors directory.
+      </p>
+      <p>
+        Apache 1.3 require APR, and if APR has been built with pthread support
+        and your Apache 1.3 wasn't (general case in Unix platforms) you should :
+        <ul>
+        <li>
+        Rebuild Apache 1.3 with pthread support (recommanded)
+        </li>
+        <li>
+        Add the LoadFile /usr/lib/pthread.so in beginning of your httpd.conf
+        </li>
+        </ul>
+      </p>
+      <p>
+        You should also ensure that the linker will be able to locate the apr shared libraries,
+        <code>libapr.so</code>, and <code>libaprutil.so</code>, make sure they have been installed
+        in linker search locations.
+      </p>
+      <screen>
+        <note>Copy the dso files in the modules location:</note>
+        <type>cp jk/build/jk2/apache13/mod_jk2.so /home/apache13/modules</type>
+        <note>Copy jkjni.so if you're using JNI</note>
+        <type>cp jk/build/jk2/apache13/jkjni.so /home/apache13/modules</type>
+        <note>You may have to add pthread library in the httpd.conf:</note>
+        <read>LoadFile /usr/lib/pthread.so</read>
+        <note>Add mod_jk2 loading in the httpd.conf</note>
+        <read>LoadModule jk2_module modules/mod_jk2.so</read>
+      </screen>
+    </subsection>
+    <subsection name="Apache 2">
+      <p>
+        In the following example Apache-2.0 is installed in
+        /home/apache20/apache40 and the commands are executed in
+        the jakarta-tomcat-connectors directory.
+      </p>
+      <screen>
+        <note>Copy the dso files in the modules location:</note>
+        <type>cp jk/build/jk2/apache2/mod_jk2.so /home/apache20/apache40/modules</type>
+        <note>Copy jkjni.so if you're using JNI</note>
+        <type>cp jk/build/jk2/apache2/jkjni.so /home/apache20/apache40/modules</type>
+        <note>Add mod_jk2 loading in the httpd.conf:</note>
+        <read>LoadModule jk2_module modules/mod_jk2.so</read>
+      </screen>
+    </subsection>
+    <subsection name="IIS">
+        <p>A pre-built version of the ISAPI redirector server plugin, isapi_redirector2.dll,
+        is available under the win32/i386 directory of jakarta-tomcat-connectors distribution.
+        You can also build a copy locally from the source present in jakarta-tomcat-connectors
+        distribution.<br />The Tomcat redirector requires three entities:
+            <ul>
+            <li>
+            <b>isapi_redirector2.dll</b> - The IIS server plugin, either obtain a pre-built DLL or build it yourself (see the build section).
+            </li><li>
+            <b>workers2.properties</b> - A file that describes the host(s) and port(s) used by the workers (Tomcat processes). 
+                A sample workers2.properties can be found under the conf directory.
+            </li><li>
+            <b>jk2.properties</b> - A configuration file used by mod_jk2 on the Tomcat side.
+            </li>
+            </ul>
+        </p>
+        <p>
+            <ol><li>In the registry, create a new registry key named
+            <b>"HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\2.0"</b>
+            </li><li>
+            Add a string value with the name <b>serverRoot</b> and a value which is a full path
+            to your Tomcat installation (for example <b>c:\jakarta-tomcat</b>).  If Tomcat is installed
+            on a different server, this entry must point to the location (directory) where the
+            workers2.properties file resides.
+            </li><li>
+            Add a string value with the name <b>extensionUri</b> and a value of <b>/jakarta/isapi_redirector2.dll</b>
+            </li><li>
+            Add a string value with the name <b>workersFile</b> and a value which is the full path 
+            to your workers2.properties file (for example <b>c:\jakarta-tomcat\conf\workers2.properties</b>)
+            </li><li>
+            Add a string value with the name <b>logLevel</b> and a value for your log level 
+            (can be DEBUG, INFO or ERROR).
+            </li><li>Using the IIS management console, add a new virtual directory to your IIS web site.
+            The name of the virtual directory must be jakarta. 
+            Its physical path should be the directory where you placed isapi_redirector2.dll 
+            While creating this new virtual directory assign it with execute access.
+            </li><li>
+            Using the IIS management console, add isapi_redirector2.dll as a filter in your IIS web site. 
+            The name of the filter should reflect its task (I use the name jakarta), 
+            its executable must full path to the isapi_redirector2.dll. 
+            </li>            
+            </ol>        
+        </p>
+        <p>Install using provided script <b>install4iis.js</b><br/>
+        This script creates the virtual directory and installs ISAPI filter for Default webserver<br/>
+        <i>C:\Program Files\Apache Software Foundation\Tomcat 5.0</i>
+        <screen>
+        <note>Open the command prompt and cd to bin folder</note>
+        <type>cd C:\Program Files\Apache Software Foundation\Tomcat 5.0\bin</type>
+        <note>Copy the isapi_redirector2.dll and install4iis.js to that folder</note>
+        <type>cscript install4iis.js</type>
+        </screen>
+        </p>
+        <p>        
+        You may modify the default installation using various command line options.
+        To see what the options are type <b>csrcipt install4iis.js -h</b>        
+        </p>
+        <p>
+        That's all, you should now start Tomcat and ask IIS to serve you the /examples context. 
+        Try <a href="http://localhost/examples/jsp/index.html">http://localhost/examples/jsp/index.html</a> for example and 
+        execute some of the JSP examples. 
+        </p>        
+    </subsection>
+    <subsection name="Lotus Domino">
+	<p><i>At the time of writing these instruction are applicable to Windows only.
+	As soon as there are versions of the Domino redirector available for other
+	platforms these instructions will be updated.</i></p>
+	
+	<p>If necessary build dsapi_redirector2.dll as per the instructions in the
+	jk/native2/server/dsapi directory. Copy the DLL into the Domino program
+	directory (this is the directory, which may be called something like
+	C:\Lotus\Domino, that contains a file called nlnotes.exe). Shortly we will
+	tell Domino where to find this file, but before we do that we need to make
+	some registry entries. The simplest way is to edit the supplied file
+	dsapi_redirector2.reg, which initially will look like this</p>
+	
+	<screen>
+	    <read>Windows Registry Editor Version 5.00</read>
+  	    <read></read>
+	    <read>[HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Dsapi Redirector\2.0]</read>
+	    <read>"serverRoot"="D:\\Works\\Tomcat\\jakarta-tomcat-4.1.27"</read>
+	    <read>"workersFile"="conf\\workers2.properties"</read>
+	    <read>"tomcatStart"="bin\\startup.bat"</read>
+	    <read>"tomcatStop"="bin\\shutdown.bat"</read>
+	    <read>"tomcatTimeout"="30000"</read>
+	</screen>
+
+	<p>Change serverRoot to reflect the location of your Tomcat installation. The
+	other filename parameters can either be relative to serverRoot or absolute
+	paths. Once edited double click on dsapi_redirector2.reg to enter it into the
+	registry.</p>
+
+	<p><b>Starting Tomcat:</b> The last three registry entries above provide
+	commands that the redirector DLL will use to start and stop Tomcat when the
+	Domino http server starts and stops respectively. If you don't require this
+	behaviour these two lines can be omitted (or deleted if you've already placed
+	them in the registry).</p>
+
+	<p><b>The Workers file:</b> If necessary take the sample workers2.properties
+	file from jakarta- tomcat- connectors\jk\conf and place it in the location
+	specified in the registry settings above (typically the conf directory of your
+	Tomcat installation). Edit the file to suit your Tomcat setup.</p>
+
+	<p><b>Configuring Domino:</b> Finally we need to configure Domino to use the
+	DSAPI extension DLL. For those who are unfamiliar with Domino server
+	configuration most of a server's configurable behavior is dictated by a
+	document called the "server document" in a database called the "Public Name
+	and Address Book" or "NAB" for short (N.B. Lotus have renamed the NAB to
+	"Domino Directory" from Domino 5 onwards). Each Domino server will have a NAB
+	(called names.nsf) and each NAB will have a number of server documents
+	including one for the current server. If you have not previously configured a
+	Domino server you may need to refer to the supplied documentation, or you may
+	need to pass this document to your tame Domino administrator.</p>
+
+	<p>Assuming you know your way around a Domino server document what we're going
+	to do is actually quite simple. Open the server document for this server,
+	place it in Edit mode, then locate the DSAPI section and the 'DSAPI filter
+	file names' field on the Internet Protocols tab, HTTP sub- tab. Add
+	"dsapi_redirector2.dll" to the DSAPI field, then save and close the
+	document.</p>
+
+	<p><b>Restart Domino:</b> In order to get these settings to take effect and
+	make sure that you haven't disrupted anything else you should now restart the
+	Domino server. If the server is running as a service and you have changed any
+	relevant system variables (JAVA_HOME, TOMCAT_HOME, CLASSPATH) since the last
+	time you restarted the computer you should do a complete restart now because
+	updates to system variables are not seen by services until after a reboot. If
+	all goes well you should see something like this on the server console when
+	the web server starts up.</p>
+
+	<screen>
+	    <read>14/11/2003 13:02:18   Attempting to start Tomcat: D:\...\startup.bat</read>
+	    <read>Using CATALINA_BASE:   D:\Works\Tomcat\jakarta-tomcat-4.1.27</read>
+	    <read>Using CATALINA_HOME:   D:\Works\Tomcat\jakarta-tomcat-4.1.27</read>
+	    <read>Using CATALINA_TMPDIR: D:\Works\Tomcat\jakarta-tomcat-4.1.27\temp</read>
+	    <read>Using JAVA_HOME:       C:\JBuilder8\jdk1.4</read>
+	    <read>14/11/2003 13:02:18   Apache Tomcat Interceptor (Jakarta/DSAPI/2.0.0) loaded</read>
+	    <read>14/11/2003 13:02:19   HTTP Web Server started</read>
+	</screen>
+
+	<p>At about the same time Tomcat should open in a new window (assuming you
+	enabled the autostart option in the registry settings). You should now be able
+	to visit a URL that is handled by Tomcat. Something like</p>
+
+	<p><code>http://name-of-server/servlet/SnoopServlet</code></p>
+
+	<p>may be available, depending on how Tomcat is configured. If that all works
+	you're done.</p>
+
+	<p><b>Mailing Lists:</b> There are two mailing lists dedicated to the Domino
+	Tomcat redirector:</p>
+
+	<p><a href="http://nomen.tagish.co.uk/mailman/listinfo/domino-tomcat-l">domino-tomcat-l</a></p>
+
+	<p><b>domino-tomcat-l</b> is a general discussion list for issues with the
+	redirector and also wider Tomcat/Domino integration issues such as calling the
+	Domino Java API from a Tomcat servlet. This list is fairly low volume so
+	please subscribe if you're actively using the redirector. If you have an issue
+	with the redirector please post it to the list where it will be seen not only
+	by the author but by other users who may be able to help with any problems.</p>
+
+	<p><a href="http://nomen.tagish.co.uk/mailman/listinfo/domino-tomcat-announce-l">domino-tomcat-announce-l</a></p>
+
+	<p><b>domino-tomcat-announce-l</b> is for announcements about the Domino Tomcat
+	redirector. Mainly this list will be used for new releases but serious
+	bugs will also be posted to it. This list will be very low volume; not more than
+	a few posts per month. For this reason if you'd like to keep track of new redirector versions
+	please subscribe to this list.</p>
+  
+    </subsection>
+  </section>
+</document>
diff --git a/connectors/jk/xdocs/jk2/vhosthowto.xml b/connectors/jk/xdocs/jk2/vhosthowto.xml
new file mode 100644
index 0000000..4f0eeb4
--- /dev/null
+++ b/connectors/jk/xdocs/jk2/vhosthowto.xml
@@ -0,0 +1,610 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>Apache 2.0.43 - Tomcat 4.1.12 - jk2 - virtual host HOWTO</title>
+<author email="unicoletti at prometeo.it">Umberto Nicoletti</author>
+<date>Tue 22 Oct 2002 11:58:28 AM GMT-5</date>
+</properties>
+<section name="Scenario">
+<ul>
+<li>RedHat Linux 7.2</li>
+<li>Latest 1.4.x Sun JDK</li>
+<li>Tomcat 4.1.12 binary</li>
+<li>Apache 2.0.43 built from source</li>
+<li>jk2 connector binary from jakarta.apache.org</li>
+</ul>
+</section>
+
+<section name="Requirements">
+<p>
+Deploy three (in my case) web applications under three different virtual hosts,
+making the default vhost
+respond to any name and to the bare IP address.
+</p>
+</section>
+
+<section name="Installing JDK">
+	<p>Note: download the jdk, not just the jre!</p>
+<screen>
+<note>
+Uncompress the jdk somewhere in the filesystem.
+I chose /usr/local/:
+</note>
+<type>
+ll /usr/local/
+</type>
+<read>
+drwxr-xr-x    9 root     root         4096 Oct 18 16:37 j2sdk1.4.1_01
+</read>
+<read>
+lrwxrwxrwx    1 root     root           14 Oct 18 16:38 java -> j2sdk1.4.1_01/
+</read>
+</screen>
+<p>
+make a symlink named java to j2sdk1.4.1_01/ so that you can
+easily switch back and forth
+between different jvms. We will use the same trick for apache and tomcat afterwards.
+</p>
+<p>
+Now tell your bash shell where to find java binaries: create a file named java.sh in
+/etc/profile.d with the following content:
+</p>
+<screen>
+<type>
+cat /etc/profile.d/java.sh 
+</type>type>
+<read># set java environment</read>
+<read></read>
+<read>export JAVA_HOME=/usr/local/java</read>
+<read>export PATH=$PATH:$JAVA_HOME/bin</read>
+<read></read>
+<read>export CLASSPATH=$JAVA_HOME/lib</read>
+</screen>
+<p>
+do a chmod:
+</p>
+<screen>
+<note>
+Make java.sh readable and executable by anyone:
+</note>
+<type>
+#chmod 755 /etc/profile.d/java.sh
+</type>
+</screen>
+Now open a new shell and try this:
+<screen>
+<type>
+which java
+</type>
+<read>
+/usr/local/java/bin/java
+</read>
+</screen>
+<p>
+You should get the answer given above. If not chek your environment and make
+sure that java.sh is executed
+when opening a new shell.
+Try to run a java program or the following: java -version.
+</p>
+<p>
+If you don't like this way of installing java please ignore it.
+
+Make sure everything is ok and then jump to the next step.
+</p>
+</section>
+
+<section name="Installing Apache">
+<p>
+Download the latest release, uncompress it, cd into the newly created directory
+and run the following:
+</p>
+<screen>
+<type>
+./configure -prefix=/usr/local/apache2.0.43 --sysconfdir=/etc/apache --localstatedir=/var --enable-so
+</type>
+</screen>
+<p>
+Of course you can customize the installation specifying other modules to enable
+or whatever you like.
+Just don't forget to ENABLE-SO, because that's what you need to load the
+apache-tomcat connector.
+</p>
+<p>
+Run make and make install. Create the log directories and others (you can skip
+this if you know how
+to configure where apache puts its log files -> edit httpd.conf):
+</p>
+<screen>
+<type>
+#mkdir /var/logs
+</type>
+<type>
+#mkdir /usr/local/apache2.0.43/conf
+</type>
+<type>
+#mkdir /usr/local/apache2.0.43/logs
+</type>
+</screen>
+<p>
+Create the symlink /usr/local/apache to /usr/local/apache2.0.43 and test your
+installation
+by executing:
+</p>
+<screen>
+<type>
+#/usr/local/apache/bin/apachectl start
+</type>
+</screen>
+<p>
+Open a browser and point it to the linux box: you should get a page telling you
+that the apache installation
+was successful.
+If that doesn't happen check the logs and troubleshoot: common errors in this configuration
+are that some directory holding log or configuration files is missing or maybe you have another web
+server listening on port 80.
+</p>
+</section>
+
+<section name="Installing Tomcat">
+<p>
+Uncompress the tomcat binaries in a directory of your choice. In this howto we
+will use /opt.
+Create a symlink named jakarta to the newly created directory so that you have
+something like the following:
+</p>
+<screen>
+<type>
+ll /opt/
+</type>
+<read>total 4</read>
+<read>lrwxrwxrwx    1 root     root           31 Oct 18 16:38 jakarta ->jakarta-tomcat-4.1.12-LE-jdk14/</read>
+<read>drwxr-xr-x   12 root     root         4096 Oct 18 18:10 jakarta-tomcat-4.1.12-LE-jdk14</read>
+<note>
+Start tomcat by running:
+</note>
+<type>
+/opt/jakarta/bin/startup.sh
+</type>
+</screen>
+<p>After a
+few seconds point your browser at the IP of
+the linux box on port 8080 and you should see the tomcat welcome page.
+If not check the catalina.out log file in /opt/jakarta/logs and fix all errors
+until Tomcat comes up.
+</p>
+</section>
+
+<section name="Configuring Tomcat to listen to Apache ajp13 requests">
+<p>
+Here is a sample server.xml file. Please note that the location of directories
+and log files is absolutely
+arbitrary and you have to edit it to make it suit your needs.
+<source>
+&lt;!-- Umberto Server Configuration File --&gt;
+
+&lt;Server port="8005" shutdown="SHUTDOWN" debug="0"&gt;
+  &lt;!-- Define an Apache-Connector Service --&gt;
+
+  &lt;Service name="Tomcat-Apache"&gt;
+ 
+   &lt;!-- Define a Coyote/JK2 AJP 1.3 Connector on port 8009 --&gt;
+    &lt;Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
+               port="8009" minProcessors="5" maxProcessors="75"
+               enableLookups="true" redirectPort="8443"
+               acceptCount="10" debug="0" connectionTimeout="20000"
+               useURIValidationHack="false"
+               protocolHandlerClassName="org.apache.jk.server.JkCoyoteHandler"/&gt;
+
+	&lt;Engine name="Apache" defaultHost="www.home.net" debug="0"&gt;
+
+      &lt;Logger className="org.apache.catalina.logger.FileLogger"
+              prefix="apache_log." suffix=".txt"
+              timestamp="true"/&gt;
+	  &lt;!-- Access log processes all requests for this virtual host. --&gt;
+      &lt;Valve className="org.apache.catalina.valves.AccessLogValve"
+                 directory="logs"  prefix="localhost_access_log." suffix=".txt"
+                 pattern="common" resolveHosts="false"/&gt;
+
+	&lt;Host name="www.home.net" debug="0"
+appBase="/opt/jakarta-tomcat-4.1.12-LE-jdk14/webapps/struts-example" 
+       unpackWARs="true" autoDeploy="true"&gt;
+		&lt;Alias&gt;localhost&lt;/Alias&gt;
+		&lt;Alias&gt;www&lt;/Alias&gt;
+		&lt;Alias&gt;10.0.0.10&lt;/Alias&gt;
+
+
+		&lt;Context path="" docBase="" debug="1"/&gt;
+
+		&lt;Valve className="org.apache.catalina.valves.AccessLogValve"
+                 directory="logs"  prefix="home_access_log." suffix=".txt"
+                 pattern="common" resolveHosts="false"/&gt;
+	&lt;/Host&gt;
+
+	&lt;Host name="www.customer1.it" debug="0"
+appBase="/opt/jakarta-tomcat-4.1.12-LE-jdk14/webapps/struts-blank" 
+       unpackWARs="true" autoDeploy="true"&gt;
+
+		&lt;Context path="" docBase="" debug="1"/&gt;
+
+		&lt;Valve className="org.apache.catalina.valves.AccessLogValve"
+                 directory="logs"  prefix="cust1_access_log." suffix=".txt"
+                 pattern="common" resolveHosts="false"/&gt;
+	&lt;/Host&gt;
+
+	&lt;Host name="www.customer2.net" debug="0"
+appBase="/opt/jakarta-tomcat-4.1.12-LE-jdk14/webapps/root" 
+       unpackWARs="true" autoDeploy="true"&gt;
+
+		&lt;Context path="" docBase="" debug="1"/&gt;
+
+		&lt;Valve className="org.apache.catalina.valves.AccessLogValve"
+                 directory="logs"  prefix="cust2_log." suffix=".txt"
+                 pattern="common" resolveHosts="false"/&gt;
+	&lt;/Host&gt;
+
+    &lt;/Engine&gt;
+
+  &lt;/Service&gt;
+
+&lt;/Server&gt;
+</source>
+
+This is a very minimalistic conf file, because we have taken away the HTTP1.1
+connector that allows us to talk directly to Tomcat.
+It might not be good for development, but it should be good for production.
+If you feel like you need also the Tomcat Standalone service then copy and paste
+it from your original server.xml file
+(you did back it up, didn't you?).
+<br/>
+Try to start tomcat again and check catalina.out to see if everything is up and
+running. If it complains about
+missing apr stuff try to edit /opt/jakarta/conf/jk2.properties and make it look so:
+
+<source>
+# list of needed handlers.
+handler.list=channelSocket,request
+# Override the default port for the channelSocket
+channelSocket.port=8009
+</source>
+
+If everything is ok move on to next section.
+</p>
+</section>
+
+<section name="Configuring Apache virtual hosting">
+<p>
+rtfm at <a
+href="http://httpd.apache.org/docs-2.0/vhosts/">http://httpd.apache.org/docs-2.0/vhosts/</a>
+In the appendix you can find the httpd.conf file I used to write and test this
+HOWTO.
+</p>
+</section>
+
+<section name="Configuring Apache to talk to Tomcat">
+<p>
+Download the jk2 shared library for you version of apache and copy it in
+/usr/local/apache/modules
+(create the  directory if necessary). If you can't find a suitable version of
+jk2 ask it to the tomcat-user mailing list
+or download the source and build it yourself (this is another HOWTO).
+</p>
+<p>
+Create, if you haven't already, the /usr/local/apache/conf directory and create
+a file named
+workers2.properties with this content in it:
+
+<source>
+# only at beginnin. In production uncomment it out
+[logger.apache2]
+level=DEBUG
+
+[shm]
+file=/usr/local/apache/logs/shm.file
+size=1048576
+
+# Example socket channel, override port and host.
+[channel.socket:localhost:8009]
+port=8009
+host=127.0.0.1
+
+# define the worker
+[ajp13:localhost:8009]
+channel=channel.socket:localhost:8009
+
+# Uri mapping
+[uri:10.0.0.10/*.jsp]
+worker=ajp13:localhost:8009
+
+[uri:www.home.net/*.jsp]
+worker=ajp13:localhost:8009
+
+[uri:www.customer1.it/*.jsp]
+worker=ajp13:localhost:8009
+
+[uri:www.customer2.net/*.jsp]
+worker=ajp13:localhost:8009
+</source>
+
+Edit the file, change ip addresses and names to suit your needs and save it.
+</p>
+<p>
+Edit http.conf and add the following line in the Modules section:
+
+<source>
+LoadModule jk2_module modules/mod_jk2.so
+</source>
+
+Save http.conf and try to start apache. It should now load the jk2 connector and
+the configuration
+from workers2.properties.
+Check the error log to make sure everything is ok.
+</p>
+<p>
+Start tomcat and try to load a HTML page in your browser: apache should return
+the page
+without problems.
+Now try with a jsp page: it should display after a little.
+<br/>
+If you get errors check that the path and host names (double check also the
+configuration of DNS
+with your network administrator) are ok, the directories are readable by both
+Tomcat and Apache.
+Again look into the log files.
+</p>
+<p>
+If everything works go to next section.
+</p>
+</section>
+
+<section name="The last trick">
+<p>
+Now ask your network administrator to set up an alias for your brand new server
+(use jspsrc if
+you like to stick to this howto).
+If you don't have easy access to dns try to edit your hosts file (on the client
+where you open the browser)
+and add a line as follows:
+
+<source>
+10.0.0.10		jspsrc
+</source>
+
+where 10.0.0.10 is the ip of your server. Open your browser and type this in
+your location bar:
+
+<source>
+http://jspsrc
+</source>
+
+and navigate to a jsp page. You should get the source of the jsp page into your
+browser!
+</p>
+<p>
+This is clearly a security problem, if not a major annoyance.
+</p>
+<p>
+What's wrong with the setup we came up so far? The problem is (or should be)
+that the ajp13
+connector can't find a virtual host that matches the jspsrc uri.
+What we need to do is set up the default virtual host so that ALL *.jsp requests
+get handled by tomcat.
+</p>
+<p>
+How do we do it?
+</p>
+<p>
+Read on if you want to know how.
+</p>
+</section>
+
+<section name="JK directives in httpd.conf">
+<p>
+In addition to the workers2.properties you can put Jk diretives directly into
+the httpd.conf file (just as you did
+with jk and webapp).
+Edit the default virtual host section in httpd.conf and add the following lines
+in the end, before
+<code>&lt;/VirtualHost&gt;</code>:
+
+<source>
+    &lt;Location "/*.jsp"&gt;
+        JkUriSet worker ajp13:localhost:8009 
+    &lt;/Location&gt;
+</source>
+
+Restart Apache and test the jspsrc url again.
+</p>
+<p>
+The jsp source should not be displayed anymore.
+</p>
+</section>
+
+<section name="Notes">
+<p>
+I think a better approach would be to remove all uri directives from
+workers2.properties
+and to put them in http.conf as we did in the previous section for the defualt
+virtual host.
+Experiment and let me know.
+</p>
+</section>
+
+<section name="APPENDIX A: httpd.conf">
+<p>
+<source>
+#
+# Umberto Nicoletti, 18/10/2002
+#
+
+### Section 1: Global Environment
+
+ServerRoot "/usr/local/apache"
+ErrorLog logs/error_log
+
+&lt;IfModule !mpm_winnt.c&gt;
+&lt;IfModule !mpm_netware.c&gt;
+#LockFile logs/accept.lock
+&lt;/IfModule&gt;
+&lt;/IfModule&gt;
+
+# ScoreBoardFile: File used to store internal server process information.
+&lt;IfModule !mpm_netware.c&gt;
+&lt;IfModule !perchild.c&gt;
+#ScoreBoardFile logs/apache_runtime_status
+&lt;/IfModule&gt;
+&lt;/IfModule&gt;
+
+&lt;IfModule !mpm_netware.c&gt;
+PidFile logs/httpd.pid
+&lt;/IfModule&gt;
+
+Timeout 300
+
+KeepAlive On
+MaxKeepAliveRequests 100
+KeepAliveTimeout 15
+
+&lt;IfModule prefork.c&gt;
+StartServers         5
+MinSpareServers      5
+MaxSpareServers     10
+MaxClients         150
+MaxRequestsPerChild  0
+&lt;/IfModule&gt;
+
+&lt;IfModule worker.c&gt;
+StartServers         2
+MaxClients         150
+MinSpareThreads     25
+MaxSpareThreads     75 
+ThreadsPerChild     25
+MaxRequestsPerChild  0
+&lt;/IfModule&gt;
+
+&lt;IfModule perchild.c&gt;
+NumServers           5
+StartThreads         5
+MinSpareThreads      5
+MaxSpareThreads     10
+MaxThreadsPerChild  20
+MaxRequestsPerChild  0
+&lt;/IfModule&gt;
+
+# listen on all ports
+Listen 80
+
+#
+# Dynamic Shared Object (DSO) Support
+#
+LoadModule jk2_module modules/mod_jk2.so
+
+### Section 2: 'Main' server configuration
+
+&lt;IfModule !mpm_winnt.c&gt;
+&lt;IfModule !mpm_netware.c&gt;
+#
+# If you wish httpd to run as a different user or group, you must run
+# httpd as root initially and it will switch.  
+#
+# User/Group: The name (or #number) of the user/group to run httpd as.
+#  . On SCO (ODT 3) use "User nouser" and "Group nogroup".
+#  . On HPUX you may not be able to use shared memory as nobody, and the
+#    suggested workaround is to create a user www and use that user.
+#  NOTE that some kernels refuse to setgid(Group) or semctl(IPC_SET)
+#  when the value of (unsigned)Group is above 60000; 
+#  don't use Group #-1 on these systems!
+#
+User nobody
+Group #-1
+&lt;/IfModule&gt;
+&lt;/IfModule&gt;
+
+ServerAdmin whatever@you.want
+ServerName www.home.net
+UseCanonicalName Off
+
+#
+# The following directives define some format nicknames for use with
+# a CustomLog directive (see below).
+#
+LogFormat "%h %l %u %t \"%r\" %&gt;s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
+LogFormat "%h %l %u %t \"%r\" %&gt;s %b" common
+LogFormat "%{Referer}i -&gt; %U" referer
+LogFormat "%{User-agent}i" agent
+
+LogLevel debug
+CustomLog logs/access.log common
+
+DocumentRoot "/opt/jakarta-tomcat-4.1.12-LE-jdk14/webapps/struts-example"
+
+&lt;Directory /opt/jakarta-tomcat-4.1.12-LE-jdk14/webapps/struts-example&gt;
+    Options None
+    AllowOverride None
+&lt;/Directory&gt;
+
+DirectoryIndex index.html index.jsp
+
+&lt;Directory /&gt;
+    Options None
+    AllowOverride None
+&lt;/Directory&gt;
+
+&lt;Files ~ "^\.ht"&gt;
+    Order allow,deny
+    Deny from all
+&lt;/Files&gt;
+
+&lt;Location /WEB-INF/&gt;
+    Order Allow,Deny
+&lt;/Location&gt;
+
+NameVirtualHost *
+
+&lt;VirtualHost *&gt;
+    ServerName www.home.net
+	ServerAlias www
+	ServerAlias localhost
+    ServerAdmin sysmaster@arpa.veneto.it
+    DocumentRoot /opt/jakarta-tomcat-4.1.12-LE-jdk14/webapps/struts-example
+
+    ErrorLog logs/home.net-errorlog
+	CustomLog logs/home.net-access.log common
+
+    &lt;Location "/*.jsp"&gt;
+        JkUriSet worker ajp13:localhost:8009 
+    &lt;/Location&gt;
+&lt;/VirtualHost&gt;
+
+&lt;VirtualHost *&gt;
+    ServerName www.customer1.it
+    ServerAdmin sysmaster@arpa.veneto.it
+    DocumentRoot /opt/jakarta-tomcat-4.1.12-LE-jdk14/webapps/struts-blank
+    ErrorLog logs/cust1-errorlog
+&lt;/VirtualHost&gt;
+
+&lt;VirtualHost *&gt;
+    ServerName www.customer2.net
+    ServerAdmin sysmaster@arpa.veneto.it
+    DocumentRoot /opt/jakarta-tomcat-4.1.12-LE-jdk14/webapps/root
+    ErrorLog logs/cust2-errorlog
+&lt;/VirtualHost&gt;
+</source>
+</p>
+
+</section>
+</document>
diff --git a/connectors/jk/xdocs/menu.idx.in b/connectors/jk/xdocs/menu.idx.in
new file mode 100644
index 0000000..6bad65c
--- /dev/null
+++ b/connectors/jk/xdocs/menu.idx.in
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<!DOCTYPE index [
+<!ENTITY JK SYSTEM  "menu.jk.idx">
+<!ENTITY JK2 SYSTEM "menu.jk2.idx">
+]>
+<!--
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+
+<index>
+  <section name="Presentation">
+  <document href="index.xml"/>
+  </section>
+  
+  <section name="Commons">
+  <document href="common/AJPv13.xml"/>
+  <document href="common/AJPv13-extensions-proposal.xml"/>
+  <document href="common/doccontrib.xml"/>
+  <document href="common/tools.xml"/>
+  <document href="faq.xml"/>
+  </section>
+  
+@JK@
+@JK2@
+  
+</index>
diff --git a/connectors/jk/xdocs/menu.jk.idx b/connectors/jk/xdocs/menu.jk.idx
new file mode 100644
index 0000000..c093ec7
--- /dev/null
+++ b/connectors/jk/xdocs/menu.jk.idx
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<section name="JK">
+<document href="jk/quickhowto.xml"/>
+<document href="jk/aphowto.xml"/>
+<document href="jk/domhowto.xml"/>
+<document href="jk/iishowto.xml"/>
+<document href="jk/neshowto.xml"/>
+<document href="jk/workershowto.xml"/>
+</section>
diff --git a/connectors/jk/xdocs/menu.jk2.idx b/connectors/jk/xdocs/menu.jk2.idx
new file mode 100644
index 0000000..32015cf
--- /dev/null
+++ b/connectors/jk/xdocs/menu.jk2.idx
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<section name="JK2">
+</section>
+<section name="Configuration in the Tomcat">
+    <document href="jk2/configtc.xml"/>
+    <document href="jk2/configtccom.xml"/>
+    <document href="jk2/configtcex.xml"/>
+</section>
+<section name="Configuration in the Web Server">
+    <document href="jk2/configweb.xml"/>
+    <document href="jk2/configwebcom.xml"/>
+    <document href="jk2/configwebex.xml"/>
+</section>
+<section name="Installation">
+    <document href="jk2/installhowto.xml"/>
+</section>
+<section name="Howto">
+    <document href="jk2/confighowto.xml"/>
+    <document href="jk2/vhosthowto.xml"/>
+    <document href="jk2/davhowto.xml"/>
+</section>
diff --git a/connectors/jk/xdocs/proxy.xml b/connectors/jk/xdocs/proxy.xml
new file mode 100755
index 0000000..7ed6a8a
--- /dev/null
+++ b/connectors/jk/xdocs/proxy.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<document>
+<copyright>
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+</copyright>
+<properties>
+<title>Using proxies with Tomcat</title>
+<date>$Date$</date>
+</properties>
+
+<section name="Http proxy">
+<p>
+It easy to use the standard Http proxy of Apache when single Tomcat is connected to Apache.
+<source>
+&lt;Location /examples/&gt;
+ProxyPass http://localhost:8080/examples/
+ProxyPassReverse http://localhost:8080/examples/
+&lt;/Location&gt;
+</source>
+</p>
+</section>
+
+<section name="Ajp proxy">
+<p>
+The Ajp proxy is a new module based on the standard Http proxy it uses AJP instead of HTTP.
+<source>
+&lt;Location /examples/&gt;
+   ProxyPass ajp://localhost:8009/examples/
+&lt;/Location&gt;
+</source>
+</p>
+</section>
+
+</document>
diff --git a/connectors/jk/xdocs/style.css.in b/connectors/jk/xdocs/style.css.in
new file mode 100644
index 0000000..12da3b2
--- /dev/null
+++ b/connectors/jk/xdocs/style.css.in
@@ -0,0 +1,269 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+body {
+  margin: 0;
+  font-family: "verdana", "tahoma", "arial", "helvetica", sans-serif;
+  font-size: 9pt;
+}
+
+td.logo {
+  background-color: @body-bg@;
+  border-style: none;
+}
+
+td.nil {
+  font-size: 1px;
+}
+
+td.head {
+  background-color: #999999;
+  border-style: solid;
+  border-width: 1px;
+  border-color: #cccccc #666666 #666666 #cccccc;
+  font-weight: bold;
+  font-size: smaller;
+  color: #cccccc;
+  padding: 2px;
+  text-align: right;
+}
+
+a.head:link {
+  text-decoration: none;
+  color: #cccccc;
+}
+
+a.head:visited {
+  text-decoration: none;
+  color: #cccccc;
+}
+
+a.head:active {
+  text-decoration: underline;
+  color: #ffffff;
+}
+
+a.head:hover {
+  text-decoration: underline;
+  color: #ffffff;
+}
+
+table.menu {
+  background-color: #cccccc;
+  border-style: solid;
+  border-width: 1px;
+  border-color: #cccccc #999999 #999999 #999999;
+  font-weight: bold;
+  font-size: smaller;
+  color: #000000;
+  padding: 2px;
+  text-align: left;
+  margin-left: 5px;
+}
+
+a.menu:link {
+  text-decoration: none;
+  color: #333333;
+}
+
+a.menu:visited {
+  text-decoration: none;
+  color: #333333;
+}
+
+a.menu:active {
+  text-decoration: underline;
+  color: #000000;
+}
+
+a.menu:hover {
+  text-decoration: underline;
+  color: #000000;
+}
+
+td.body {
+  background-color: #ffffff;
+  border-style: none;
+  padding: 4px;
+  text-align: justify;
+}
+
+td.section {
+  background-color: @banner-bg@;
+  border-style: none;
+  font-weight: bold;
+  font-size: bigger;
+  color: @banner-fg@;
+  padding: 1px;
+  text-align: left;
+}
+
+td.subsection {
+  background-color: @banner-bg@;
+  border-style: none;
+  font-weight: bold;
+  font-size: smaller;
+  color: @banner-fg@;
+  padding: 0px;
+  text-align: left;
+}
+
+p.section {
+  background-color: #ffffff;
+  border-style: none;
+  color: #000000;
+  margin-left: 20px;
+  margin-right: 10px;
+  text-align: justify;
+}
+
+pre.section {
+   margin-left: 20px;
+}
+
+td.section {
+  background-color: @sub-banner-bg@;
+  border-style: none;
+  font-weight: bold;
+  font-size: bigger;
+  color: @sub-banner-fg@;
+  padding: 0px;
+  text-align: left;
+}
+
+p.todo {
+  background-color: #ffffff;
+  border-style: none;
+  color: #000000;
+  margin-left: 20px;
+  margin-right: 10px;
+  text-align: justify;
+  font-size: smaller;
+}
+
+p.screen {
+  background-color: #ffffff;
+  border-style: none;
+  color: #000000;
+  margin-left: 10px;
+  margin-right: 0px;
+  text-align: left;
+}
+
+code.screen {
+  background-color: #cccccc;
+  border-style: none;
+  color: #000000;
+  margin-left: 10px;
+  margin-right: 0px;
+  text-align: left;
+}
+
+p.screendos {
+  background-color: #000000;
+  border-style: none;
+  color: #c0c0c0;
+  margin-left: 10px;
+  margin-right: 0px;
+  text-align: left;
+}
+
+code.screendos {
+  background-color: #000000;
+  border-style: none;
+  color: #c0c0c0;
+  margin-left: 10px;
+  margin-right: 0px;
+  text-align: left;
+}
+
+p.screen5250 {
+  background-color: #000000;
+  border-style: none;
+  color: #00ff00;
+  margin-left: 10px;
+  margin-right: 0px;
+  text-align: left;
+}
+
+code.screen5250 {
+  background-color: #000000;
+  border-style: none;
+  color: #00ff00;
+  margin-left: 10px;
+  margin-right: 0px;
+  text-align: left;
+}
+
+div.screen {
+  margin: 10px 0px 10px 20px;
+  font-size: smaller;
+  color: #666666;
+}
+
+div.screendos {
+  margin: 10px 0px 10px 20px;
+  font-size: smaller;
+  color: #ffffff;
+}
+
+div.screen5250 {
+  margin: 10px 0px 10px 20px;
+  font-size: smaller;
+  color: #66ff66;
+}
+
+em.screen {
+  font-weight: normal;
+  font-style: normal;
+  color: #666666;
+}
+
+em.screendos {
+  font-weight: normal;
+  font-style: normal;
+  color: #c0c0c0;
+}
+
+em.screen5250 {
+  font-weight: normal;
+  font-style: normal;
+  color: #00ff00;
+}
+
+b.screen {
+  font-weight: normal;
+  font-style: normal;
+  color: #0000ff;
+}
+
+b.screendos {
+  font-weight: normal;
+  font-style: normal;
+  color: #c0c0c0;
+}
+
+b.screen5250 {
+  font-weight: normal;
+  font-style: normal;
+  color: #00ff00;
+}
+
+b.code {
+  font-weight: normal;
+  font-style: normal;
+  color: @source-color@;
+}
+
diff --git a/connectors/jk/xdocs/style.xsl.in b/connectors/jk/xdocs/style.xsl.in
new file mode 100644
index 0000000..71f58e8
--- /dev/null
+++ b/connectors/jk/xdocs/style.xsl.in
@@ -0,0 +1,863 @@
+<?xml version="1.0"?>
+<!--
+   Copyright 1999-2004 The Apache Software Foundation
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+ 
+       http://www.apache.org/licenses/LICENSE-2.0
+ 
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+
+<xsl:stylesheet version="1.0"
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  xmlns="http://www.w3.org/TR/xhtml1/strict">
+
+  <!--
+    Let's start by declaring HOW this stylesheet must behave.
+  -->
+  <xsl:output method="html" indent="yes"
+    doctype-public="-//W3C//DTD HTML 4.01//EN"
+    doctype-system="http://www.w3.org/TR/html4/strict.dtd"/>
+
+  <!-- Define default values for parameters -->
+  <xsl:param name="styles" select="''"/>
+  <xsl:param name="images" select="'../images'"/>
+  <xsl:param name="homedoc" select="'../'"/>
+  <xsl:param name="project-menu"     select="'menu'"/>
+  <xsl:param name="relative-path"    select="'.'"/>
+  <xsl:param name="printer-location" select="'printer/'"/>
+
+  <!-- Defined variables (non-overrideable) -->
+  <xsl:variable name="body-bg"       select="'@body-bg@'"/>
+  <xsl:variable name="body-fg"       select="'@body-fg@'"/>
+  <xsl:variable name="body-link"     select="'@body-link@'"/>
+  <xsl:variable name="banner-bg"     select="'@banner-bg@'"/>
+  <xsl:variable name="banner-fg"     select="'@banner-fg@'"/>
+  <xsl:variable name="sub-banner-bg"     select="'@sub-banner-bg@'"/>
+  <xsl:variable name="sub-banner-fg"     select="'@sub-banner-fg@'"/>
+  <xsl:variable name="table-th-bg"   select="'@table-th-bg@'"/>
+  <xsl:variable name="table-td-bg"   select="'@table-td-bg@'"/>
+  <xsl:variable name="gen-dev-doc"   select="'@gen-dev-doc@'"/>
+
+  <!--
+    Match the ROOT of the source document and process its "document" element.
+  -->
+  <xsl:template match="/">
+    <xsl:apply-templates select="document"/>
+  </xsl:template>
+
+  <!--
+    Match the roote "document" element, let's prepare the layout of the whole
+    page.
+  -->
+  <xsl:template match="document">
+    <html>
+
+      <!--
+        This is the page header, we want a title from this document title
+        the <meta> copyright statement and all authors in "meta" headers.
+      -->
+      <head>
+        <title>
+          <xsl:value-of select="properties/title"/>
+        </title>
+        <xsl:apply-templates select="copyright" />
+        <meta name="copyright" content="1999-2004 The Apache Software Foundation"/>
+        <xsl:for-each select="properties/date">
+          <xsl:variable name="date">
+            <xsl:value-of select="."/>
+          </xsl:variable>
+          <meta name="last-changed" content="{$date}"/>
+        </xsl:for-each>
+         <xsl:for-each select="properties/author">
+          <xsl:variable name="name">
+            <xsl:value-of select="."/>
+          </xsl:variable>
+          <xsl:variable name="email">
+            <xsl:value-of select="@email"/>
+          </xsl:variable>
+          <meta name="author" content="{$name}"/>
+          <meta name="email" content="{$email}"/>
+        </xsl:for-each>
+        <link rel="stylesheet" type="text/css" href="{$styles}/style.css"/>
+        <link rel="shortcut icon" href="{$images}/tomcat.ico"/>
+      </head>
+
+      <!--
+        This describes the layout of the page
+      -->
+      <body bgcolor="{$body-bg}" text="{$body-fg}" alink="{$body-link}"
+            vlink="{$body-link}" link="{$body-link}">
+        <a name="TOP"/>
+
+        <table border="0" cellspacing="0" cellpadding="0" width="100%">
+          <!--
+            An empty row (thank you stupid IE).
+          -->
+          <tr height="1">
+            <td width="150" bgcolor="{$body-bg}" height="1" class="nil">
+              <img src="{$images}/pixel.gif" border="0" width="150" height="1" vspace="0" hspace="0"/>
+            </td>
+            <td width="*" bgcolor="{$body-bg}" height="1" class="nil">
+              <img src="{$images}/pixel.gif" border="0" width="370" height="1" vspace="0" hspace="0"/>
+            </td>
+          </tr>
+
+          <!--
+            Our first row contains the Jakarta and the JK logos.
+          -->
+          <tr>
+            <td bgcolor="{$body-bg}" class="logo" colspan="2" width="*">
+              <table border="0" cellspacing="0" cellpadding="0" width="100%">
+                <tr>
+                  <td align="left">
+                    <img src="{$images}/jakarta.gif" border="0" width="505" height="48" align="left"/>
+                  </td>
+                  <td align="right">
+                    <img src="{$images}/mod_jk.jpg" border="0" align="right"/>
+                  </td>
+                </tr>
+              </table>
+            </td>
+          </tr>
+
+          <!--
+            A Turbine-style bar with links to the ASF, Jakarta and Tomcat.
+          -->
+          <tr>
+            <td bgcolor="#999999" class="head" align="right" width="*" colspan="2">
+              <nobr>
+                <a class="head" href="http://www.apache.org/">
+                  <xsl:text>Apache Software Foundation</xsl:text>
+                </a> |
+                <a class="head" href="http://jakarta.apache.org/">
+                  <xsl:text>Jakarta Project</xsl:text>
+                </a> |
+                <a class="head" href="http://jakarta.apache.org/tomcat/">
+                  <xsl:text>Apache Tomcat</xsl:text>
+                </a>
+              </nobr>
+            </td>
+          </tr>
+
+          <!--
+            Sidebar menu in a nested table and main content.
+          -->
+          <tr>
+            <xsl:choose>
+            <xsl:when test="$project-menu = 'menu'">
+            <td bgcolor="#ffffff" width="150" valign="top">
+
+              <!--
+                This is the sidebar menu, we have links to all documents specified
+                in "menu.idx", and if this is the current document, we go deeper
+                and write an index of the sections as well.
+              -->
+              <table border="0" width="150" cellspacing="0" cellpadding="0" class="menu">
+                <!-- Empty row, thanks IE -->
+                <tr height="1">
+                  <td width="10" bgcolor="#cccccc" height="1" class="nil">
+                    <img src="{$images}/pixel.gif" border="0" width="10" height="1" vspace="0" hspace="0"/>
+                  </td>
+                  <td width="140" bgcolor="#cccccc" height="1" class="nil">
+                    <img src="{$images}/pixel.gif" border="0" width="140" height="1" vspace="0" hspace="0"/>
+                  </td>
+                </tr>
+
+                <!--
+                  All the files we want to have processed in the final pages are
+                  stored (in order) in a file called "menu.idx". We set a variable
+                  name with the current URL, and then we process each "document"
+                  within the index.
+                -->
+                <xsl:variable name="root" select="/"/>
+                <xsl:for-each select="document('menu.idx')/index/section">
+                  <tr height="6">
+                  <td bgcolor="#d0d0d0" width="150" colspan="2">
+                  <xsl:value-of select="@name"/>
+                  </td>
+                  </tr>
+	                <xsl:for-each select="./document">
+	                  <xsl:variable name="href" select="@href"/>
+	                  <tr>
+	                    <td bgcolor="#cccccc" width="150" colspan="2">
+	                      <nobr>
+	                        <a class="menu">
+	                          <xsl:call-template name="converturi">
+	                            <xsl:with-param name="href" select="@href"/>
+	                          </xsl:call-template>
+	                        </a>
+	                      </nobr>
+	                    </td>
+	                  </tr>
+	                  <tr height="2">
+	                  </tr>
+
+	                  <!--
+	                    Slightly more complicated, we use the document-location function
+	                    and compare against it to see whether we are in the same file or
+	                    not. If we actually are, we expand to the "section" level.
+	                  -->
+	                  <xsl:if test="$root/document/properties/title = document(@href)/document/properties/title">
+	                    <xsl:for-each select="document(@href)/document/section">
+	                      <tr>
+	                        <td bgcolor="#cccccc" width="10"/>
+	                        <td bgcolor="#cccccc" width="140">
+	                          <a class="menu" href="#{@name}">
+	                            <xsl:value-of select="@name"/>
+	                          </a>
+	                        </td>
+	                       </tr>
+	                       <tr height="1">
+	                      </tr>
+	                    </xsl:for-each>
+	                  </xsl:if>
+                  </xsl:for-each>
+                  <tr height="6"/>
+                </xsl:for-each>
+
+	            <xsl:if test="$gen-dev-doc = true">
+                <!--
+                  The last thing to put down in the index are the API docs,
+                  both for C and for Java
+                -->
+                <tr height="6">
+                <td bgcolor="#d0d0d0" width="150" colspan="2">
+                Developpers
+                </td>
+                </tr>
+                <tr>
+                  <td bgcolor="#cccccc" width="150" colspan="2">
+                    <nobr>
+                      <a class="menu" href="./api-java/index.html">Java API Documentation</a>
+                    </nobr>
+                  </td>
+                </tr>
+                <tr>
+                  <td bgcolor="#cccccc" width="150" colspan="2">
+                    <nobr>
+                      <a class="menu" href="./api-c/">C API Documentation</a>
+                    </nobr>
+                  </td>
+                </tr>
+	            </xsl:if>
+
+              </table>
+            </td>
+            </xsl:when>
+            </xsl:choose>
+
+            <!--
+              Done with the sidebar, now, do we want some content as well or WHAT?
+            -->
+            <td bgcolor="#ffffff" width="*" valign="top" class="body">
+              <!--
+              Add printer friendly link.
+              -->
+              <table border="0" width="100%" cellspacing="4">
+                <tr>
+                  <td align="left" valign="top" nowrap="true">
+                    <!-- Not yet available in jk docs
+                    <h1><xsl:value-of select="project/title"/></h1>
+                    -->
+                    <h2><xsl:value-of select="properties/title"/></h2>
+                  </td>
+                  <td align="right" valign="top" nowrap="true">
+                    <!-- Add the printer friendly link for docs with a menu -->
+                    <xsl:if test="$project-menu = 'menu'">
+                      <xsl:call-template name="addprint">
+                      </xsl:call-template>
+                    </xsl:if>
+                    <xsl:if test="$project-menu != 'menu'">
+                      <img src="{$images}/void.gif" width="1" height="1" vspace="0" hspace="0" border="0"/>
+                    </xsl:if>
+                  </td>
+                </tr>
+              </table>
+
+              <xsl:apply-templates select="section"/>
+            </td>
+          </tr>
+        </table>
+      </body>
+    </html>
+  </xsl:template>
+
+  <!--
+    Match the "author" tag only in mode "header" (meaning that we have to
+    process it for the HTML <head> element.
+  -->
+  <xsl:template match="author" mode="header">
+    <meta name="author" content="{text()}"/>
+    <meta name="email" content="{@email}"/>
+  </xsl:template>
+
+  <!--
+    Present a canonical representation of an author.
+  -->
+  <xsl:template match="author">
+    <a href="mailto:{@email}"><xsl:value-of select="text()"/></a>
+  </xsl:template>
+
+  <xsl:template match="section">
+    <a name="{@name}">
+      <table border="0" cellspacing="0" cellpadding="0" width="100%">
+        <tr>
+          <td bgcolor="{$banner-bg}" class="section" valign="top" align="left">
+            <img src="{$images}/corner.gif" valign="top" align="left" hspace="0" vspace="0" border="0"/>
+              <xsl:if test="string-length(description/text()) = 0">
+                <xsl:value-of select="@name"/>
+              </xsl:if>
+              <xsl:value-of select="description/text()"/>
+          </td>
+        </tr>
+      </table>
+    </a>
+    <xsl:apply-templates select="subsection|p|ul|ol|img|screen|screendos|screen5250|todo"/>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="subsection">
+    <a name="sub_{@name}">
+      <table border="0" cellspacing="0" cellpadding="0" width="100%">
+        <tr>
+          <td bgcolor="{$sub-banner-bg}" class="subsection" valign="top" align="left">
+            <img src="{$images}/corner.gif" valign="top" align="left" hspace="0" vspace="0" border="0"/>
+              <xsl:if test="string-length(description/text()) = 0">
+                <xsl:value-of select="@name"/>
+              </xsl:if>
+              <xsl:value-of select="description/text()"/>
+          </td>
+        </tr>
+      </table>
+    </a>
+    <xsl:apply-templates select="subsection|p|ul|ol|img|screen|screendos|screen5250|todo"/>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="todo">
+    <p class="todo">
+      This paragraph has not been written yet, but <b>you</b> can contribute to it.
+      <xsl:if test="string-length(@note) > 0">
+        The original author left a note attached to this TO-DO item:
+        <b><xsl:value-of select="@note"/></b>
+      </xsl:if>
+    </p>
+  </xsl:template>
+
+  <xsl:template match="p">
+    <p class="section"><xsl:apply-templates select="author|code|source|screen|screendos|screen5250|table|ul|ol|br|b|a|text()"/></p>
+  </xsl:template>
+
+  <xsl:template match="b">
+    <b><font color="#333333"><xsl:apply-templates select="text()"/></font></b>
+  </xsl:template>
+
+  <xsl:template match="br">
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="img">
+    <p>
+      <div align="center">
+        <xsl:value-of select="@alt"/><br/>
+        <img src="{@src}" alt="{@alt}" vspace="0" hspace="0" border="0"/>
+      </div>
+    </p>
+  </xsl:template>
+
+  <xsl:template match="ul">
+    <ul><xsl:apply-templates select="li|ul|ol"/></ul>
+  </xsl:template>
+
+  <xsl:template match="ol">
+    <ol><xsl:apply-templates select="li|ul|ol"/></ol>
+  </xsl:template>
+
+  <xsl:template match="li">
+    <li><xsl:apply-templates select="br|b|a|ul|ol|text()"/></li>
+  </xsl:template>
+
+  <!-- JFC added -->
+  <xsl:template match="table">
+    <table border="0"><xsl:apply-templates select="tr"/></table>
+  </xsl:template>
+
+  <xsl:template match="tr">
+    <tr><xsl:apply-templates select="th|td|td15|td13|td6|td5|td3|td2"/></tr>
+  </xsl:template>
+
+  <xsl:template match="th">
+    <td bgcolor="{$table-th-bg}" valign="top" align="middle">
+        <xsl:if test="@colspan">
+            <xsl:attribute name="colspan">
+            <xsl:value-of select="@colspan"/>
+            </xsl:attribute>
+        </xsl:if>
+        <xsl:apply-templates select="b|a|text()"/>
+    </td>
+  </xsl:template>
+
+  <xsl:template match="td">
+    <td bgcolor="{$table-td-bg}" valign="top" align="left">
+        <xsl:if test="@colspan">
+            <xsl:attribute name="colspan">
+            <xsl:value-of select="@colspan"/>
+            </xsl:attribute>
+        </xsl:if>
+        <xsl:apply-templates select="b|a|text()"/>
+    </td>
+  </xsl:template>
+
+  <xsl:template match="source">
+    <pre class="section"><xsl:apply-templates select="text()"/></pre>
+  </xsl:template>
+
+  <xsl:template match="code">
+    <b class="code"><xsl:apply-templates select="text()"/></b>
+  </xsl:template>
+  <!-- end JFC -->
+
+  <xsl:template match="screen">
+    <p class="screen">
+      <div align="center">
+        <table width="80%" border="1" cellspacing="0" cellpadding="2" bgcolor="#cccccc">
+          <tr>
+            <td bgcolor="#cccccc" align="left">
+              <xsl:apply-templates select="note|wait|type|typenext|read"/>
+            </td>
+          </tr>
+        </table>
+      </div>
+    </p>
+  </xsl:template>
+  
+  <xsl:template match="screendos">
+    <p class="screendos">
+      <div align="center">
+        <table width="80%" border="1" cellspacing="0" cellpadding="2" bgcolor="#000000">
+          <tr>
+            <td bgcolor="#000000" align="left">
+              <xsl:apply-templates select="notedos|waitdos|typedos|typedosnext|readdos"/>
+            </td>
+          </tr>
+        </table>
+      </div>
+    </p>
+  </xsl:template>
+  
+  <xsl:template match="screen5250">
+    <p class="screen5250">
+      <div align="center">
+        <table width="80%" border="1" cellspacing="0" cellpadding="2" bgcolor="#000000">
+          <tr>
+            <td bgcolor="#000000" align="left">
+              <xsl:apply-templates select="note5250|wait5250|type5250|type5250next|read5250"/>
+            </td>
+          </tr>
+        </table>
+      </div>
+    </p>
+  </xsl:template>
+  
+  <!-- Unix look -->
+
+  <xsl:template match="note">
+    <div class="screen">
+      <xsl:value-of select="text()"/>
+    </div>
+  </xsl:template>
+
+  <xsl:template match="wait">
+    <div class="screen">[...]</div>
+  </xsl:template>
+
+  <xsl:template match="type">
+    <code>
+      <nobr>
+        <em class="screen">
+          <xsl:text>[user@host] ~</xsl:text>
+          <xsl:if test="string-length(@dir) > 0">
+            <xsl:text>/</xsl:text>
+            <xsl:value-of select="@dir"/>
+          </xsl:if>
+          <xsl:text> $ </xsl:text>
+        </em>
+        <xsl:if test="string-length(text()) > 0">
+          <b class="screen"><xsl:value-of select="text()"/></b>
+        </xsl:if>
+      </nobr>
+    </code>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="typenext">
+    <code>
+      <nobr>
+        <em class="screen">        
+          <xsl:text> </xsl:text>
+        </em>
+        <xsl:if test="string-length(text()) > 0">
+          <b class="screen"><xsl:value-of select="text()"/></b>
+        </xsl:if>
+      </nobr>
+    </code>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="read">
+    <code class="screen">
+      <nobr>
+        <xsl:apply-templates select="text()|enter"/>
+      </nobr>
+    </code>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="enter">
+    <b class="screen"><xsl:value-of select="text()"/></b>
+  </xsl:template>
+
+  <!-- DOS/Windows look -->
+
+  <xsl:template match="notedos">
+    <div class="screendos">
+      <xsl:value-of select="text()"/>
+    </div>
+  </xsl:template>
+
+  <xsl:template match="waitdos">
+    <div class="screendos">[...]</div>
+  </xsl:template>
+
+  <xsl:template match="readdos">
+    <code class="screendos">
+      <nobr>
+        <xsl:apply-templates select="text()|enterdos"/>
+      </nobr>
+    </code>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="typedos">
+    <code>
+      <nobr>
+        <em class="screendos">
+          <xsl:text>c:\</xsl:text>
+          <xsl:if test="string-length(@dir) > 0">
+            <xsl:text>/</xsl:text>
+            <xsl:value-of select="@dir"/>
+          </xsl:if>
+          <xsl:text>></xsl:text>
+        </em>
+        <xsl:if test="string-length(text()) > 0">
+          <b class="screendos"><xsl:value-of select="text()"/></b>
+        </xsl:if>
+      </nobr>
+    </code>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="typedosnext">
+    <code>
+      <nobr>
+        <em class="screendos">        
+          <xsl:text> </xsl:text>
+        </em>
+        <xsl:if test="string-length(text()) > 0">
+          <b class="screendos"><xsl:value-of select="text()"/></b>
+        </xsl:if>
+      </nobr>
+    </code>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="enterdos">
+    <b class="screendos"><xsl:value-of select="text()"/></b>
+  </xsl:template>
+
+  <!-- iSeries/5250 look -->
+
+  <xsl:template match="note5250">
+    <div class="screen5250">
+      <xsl:value-of select="text()"/>
+    </div>
+  </xsl:template>
+
+  <xsl:template match="wait5250">
+    <div class="screen5250">[...]</div>
+  </xsl:template>
+
+  <xsl:template match="read5250">
+    <code class="screen5250">
+      <nobr>
+        <xsl:apply-templates select="text()|enter5250"/>
+      </nobr>
+    </code>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="type5250">
+    <code>
+      <nobr>
+        <em class="screen5250">
+          <xsl:text>===></xsl:text>
+        </em>
+        <xsl:if test="string-length(text()) > 0">
+          <b class="screen5250"><xsl:value-of select="text()"/></b>
+        </xsl:if>
+      </nobr>
+    </code>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="type5250next">
+    <code>
+      <nobr>
+        <em class="screen5250">        
+          <xsl:text> </xsl:text>
+        </em>
+        <xsl:if test="string-length(text()) > 0">
+          <b class="screen5250"><xsl:value-of select="text()"/></b>
+        </xsl:if>
+      </nobr>
+    </code>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="enter5250">
+    <b class="screen5250"><xsl:value-of select="text()"/></b>
+  </xsl:template>
+
+  <xsl:template match="a">
+    <b>
+      <a>
+        <xsl:call-template name="converturi">
+          <xsl:with-param name="href" select="@href"/>
+          <xsl:with-param name="text" select="text()"/>
+          <xsl:with-param name="attr" select="'href'"/>
+        </xsl:call-template>
+      </a>
+    </b>
+  </xsl:template>
+
+  <!--
+    Convert the name of the matching "href" attribute (if needed) from
+    "file.xml#anchor" to "file.html#anchor", and insert the title of
+    the target document as the only text child of the resulting html
+    <a /> tag. (Of course, don't convert fully qualified URIs).
+  -->
+  <xsl:template name="converturi">
+    <xsl:param name="attr" select="'href'"/>
+    <xsl:param name="href" select="''"/>
+    <xsl:param name="text" select="''"/>
+
+    <xsl:choose>
+    
+      <!--
+        If the "href" parameter contains ":" this is most definitely an URL,
+        therefore we need to quote it "as is" without translating its value.
+        The text is either supplied, or it's the value of the URL itself
+        (without the trailing anchor, if any).
+      -->
+      <xsl:when test="contains($href,':')">
+        <xsl:attribute name="{$attr}"><xsl:value-of select="$href"/></xsl:attribute>
+        <xsl:if test="string-length($text) = 0">
+          <xsl:choose>
+            <xsl:when test="contains($href,'#')">
+              <xsl:value-of select="substring-before($href,'#')"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="$href"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:if>
+        <xsl:value-of select="$text"/>
+      </xsl:when>
+
+      <!--
+        Nope, we don't have a full URL, therefore we interpret this as a
+        relative hyperlink to another document. We need to translate its
+        name from "*.xml" to "*.html" (because this is how we convert the
+        names) and the text included in this will be the title of the
+        target document.
+      -->
+      <xsl:otherwise>
+        <!--
+          The "file" variable contains the part of the "href" before
+          the "#" character. Yes, the "file" name.
+        -->
+        <xsl:variable name="file">
+          <xsl:choose>
+            <xsl:when test="contains($href,'#')">
+              <xsl:value-of select="substring-before($href,'#')" />
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="$href" />
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+
+        <!--
+          Like "file" the "anchor" variable contains the part of the "href"
+          after the "#" character.
+        -->
+        <xsl:variable name="anchor">
+          <xsl:if test="contains($href,'#')">
+            <xsl:value-of select="'#'" />
+            <xsl:value-of select="substring-after($href,'#')" />
+          </xsl:if>
+        </xsl:variable>
+
+        <!--
+          Good, now we check if "file" ends in ".xml", if so, we replace that
+          with ".html", otherwise we keep its original value, then we add the
+          anchor we gathered before. We call this "target".
+        -->
+        <xsl:variable name="target">
+          <xsl:value-of select="$homedoc"/>
+          <xsl:if test="string-length($file) > 0">
+            <xsl:choose>
+              <xsl:when test="substring($file,string-length($file)-3) = '.xml'">
+                <xsl:value-of select="substring($file,1,string-length($file)-3)"/>
+                <xsl:value-of select="'html'"/>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:value-of select="$file"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:if>
+          <xsl:value-of select="$anchor"/>
+        </xsl:variable>
+        
+        <!--
+          Now, we want to set the attribute to contain the "target" variable.
+        -->
+        <xsl:attribute name="{$attr}">
+          <xsl:value-of select="$target"/>
+        </xsl:attribute>
+
+        <!--
+          To finish we want to set the body of this element: if we have "text"
+          the body of the element will be just that, otherwise, it will be
+          the "target" value (the translated href) if there was no text,
+          or the "title" of the target document if we actually translated
+          something
+        -->
+        <xsl:if test="string-length($text) = 0">
+          <xsl:choose>
+            <xsl:when test="$target = $href">
+              <xsl:value-of select="$file"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="document($file)/document/properties/title"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:if>
+        <xsl:value-of select="$text"/>
+
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <!--
+    Build the name of the location of the printer friendly page
+  -->
+  <xsl:template name="printeruri">
+    <xsl:param name="attr" select="'href'"/>
+    <xsl:param name="href" select="''"/>
+
+    <!-- Extract the filename -->
+    <xsl:variable name="file">
+      <xsl:choose>
+        <xsl:when test="contains($href,'/')">
+          <xsl:value-of select="substring-after($href,'/')" />
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$href" />
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:variable name="filename">
+      <xsl:choose>
+        <xsl:when test="substring($file,string-length($file)-3) = '.xml'">
+          <xsl:value-of select="substring($file,1,string-length($file)-3)"/>
+          <xsl:value-of select="'html'"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$file"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+
+    <!-- Extract the directory name -->
+    <xsl:variable name="dirname">
+      <xsl:choose>
+        <xsl:when test="contains($href,'/')">
+          <xsl:value-of select="substring-before($href,'/')" />
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="''" />
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <!-- Build the target name -->
+    <xsl:variable name="target">
+      <xsl:value-of select="$homedoc"/>
+      <xsl:choose>
+        <xsl:when test="string-length($dirname) = 0">
+          <xsl:value-of select="$printer-location"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$dirname"/>
+          <xsl:value-of select="'/'"/>
+          <xsl:value-of select="$printer-location"/>
+        </xsl:otherwise>
+      </xsl:choose>
+      <xsl:value-of select="$filename"/>
+    </xsl:variable>
+
+    <xsl:attribute name="{$attr}">
+      <xsl:value-of select="$target"/>
+    </xsl:attribute>
+  </xsl:template>
+
+  <!--
+    Get the file (via the menu.idx) and build the printer friendly link
+  -->
+  <xsl:template name="addprint">
+    <xsl:variable name="root" select="/"/> 
+    <xsl:for-each select="document('menu.idx')/index/section">
+      <xsl:for-each select="./document">
+        <xsl:variable name="href" select="@href"/>
+        <xsl:if test="$root/document/properties/title = document(@href)/document/properties/title">
+
+          <small>
+            <a>
+              <xsl:call-template name="printeruri">
+                <xsl:with-param name="href" select="$href"/>
+              </xsl:call-template>
+              <img src="{$images}/printer.gif" border="0" alt="Printer Friendly Version"/>
+              <br />print-friendly<br />version
+            </a>
+          </small>
+
+        </xsl:if>
+      </xsl:for-each>
+    </xsl:for-each>
+  </xsl:template>
+
+  <xsl:template match="copyright">
+    <xsl:comment>
+      <xsl:apply-templates select="text()" />
+    </xsl:comment>
+  </xsl:template>
+  
+</xsl:stylesheet>
diff --git a/connectors/naming/build.xml b/connectors/naming/build.xml
new file mode 100644
index 0000000..02420c3
--- /dev/null
+++ b/connectors/naming/build.xml
@@ -0,0 +1,96 @@
+<project name="jtc-naming" default="main" basedir=".">
+
+    <!-- ===================== Initialize Property Values ================ -->
+    <property file="build.properties"/>
+    <property file="${user.home}/build.properties"/>
+    <property file="${user.home}/.build.properties"/>
+
+    <!-- ====================  ==================== -->
+    <property name="naming.build.dir" location="target" />
+        
+    <property name="commons.src" location="../../jakarta-commons" />
+
+    <property name="jndi.home" location="${base.path}/jndi1.2.1" />
+    <property name="commons-logging.jar" location="${commons.src}/logging/dist/commons-logging.jar" />
+    <property name="tomcat-util.jar" location="../util/build/lib/tomcat-util.jar" />
+    <property name="commons-collections.jar" location="${commons.src}/collections/dist/commons-collections.jar" />
+
+    <path id="build-main.classpath">
+        <pathelement location="${commons-logging.jar}"/>
+        <pathelement location="${commons-collections.jar}"/>
+        <pathelement location="${tomcat-util.jar}"/>
+        <pathelement location="${naming.build.dir}/classes"/>
+
+        <pathelement location="${jndi.home}/lib/dns.jar"/>
+        <pathelement location="${jndi.home}/lib/dsml.jar"/>
+        <pathelement location="${jndi.home}/lib/fscontext.jar"/>
+        <pathelement location="${jndi.home}/lib/cosnaming.jar"/>
+        <pathelement location="${jndi.home}/lib/providerutil.jar"/>
+        <pathelement location="${jndi.home}/lib/ldap.jar"/>
+        <pathelement location="${jndi.home}/lib/ldapbp.jar"/>
+        <pathelement location="${jndi.home}/lib/ldapsec.jar"/>
+
+        <pathelement location="${jndi.home}/lib/jndibrowser.jar"/>
+    </path>
+
+ 
+    <target name="main"  >
+        <mkdir dir="${naming.build.dir}/classes"/>
+        <javac srcdir="src"
+               destdir="${naming.build.dir}/classes"
+               deprecation="${compile.deprecation}"
+               debug="${compile.debug}"
+               optimize="${optimize}"
+               verbose="off" >
+            <exclude name="org/apache/ajp/tomcat4/**" unless="tomcat40.detect"/>
+	    <classpath refid="build-main.classpath"/>
+	</javac>
+        <copy todir="${naming.build.dir}/classes" >
+            <fileset dir="src" includes="**/*.properties"/>
+        </copy>
+    </target>
+
+  <target name="test"  >
+    <classloader classpathRef="build-main.classpath" />
+
+    <taskdef name="jndiSet" classname="org.apache.naming.ant.JndiSet"/>
+    <taskdef name="jndiProperties" classname="org.apache.naming.ant.JndiProperties"/>
+    <taskdef name="jndiFileCtx" classname="org.apache.naming.modules.fs.FileDirContext"/>
+
+    <!-- Enable ${jndi:...} dynamic properties -->
+    <jndiProperties/>
+
+    <!-- memory context -->
+    <jndiSet context="/foo" value="bar" />
+    <echo message="Value: ${jndi:/foo}"/>
+
+
+    <property name="dirname" location="." />
+
+    <!-- Create a JNDI context for the current dir -->
+    <jndiFileCtx docBase="${dirname}" id="docBaseId" />
+    
+    <!-- Bind the file context to /currentDir -->
+    <jndiSet context="/currentDir" refId="docBaseId" />
+
+    <echo message="Value: ${jndi:/currentDir/build.xml}"/>
+    <echo message="Value: ${jndi:fs:/tmp}"/>
+  </target>
+
+
+  <target name="browser">
+    <java classpathref="build-main.classpath" 
+          classname="examples.browser.Browser" fork="true">
+      <sysproperty key="java.naming.dns.url" value="dns://10.0.0.1/covalent.net"/>
+      
+      <!-- LABEL|java.naming.factory.initial|java.naming.provider.url|root|auth( none, simple, etc ) |princ|pass -->
+      <arg value="LDAP|com.sun.jndi.ldap.LdapCtxFactory|ldap://localhost:389|dc=wyn,dc=org|simple|cn=Manager,dc=wyn,dc=org|secret" />
+      <!-- arg value="File|com.sun.jndi.fscontext.RefFSContextFactory|file:/" /-->
+      <arg value="DSML|com.sun.jndi.dsml.DsmlCtxFactory|file:/tmp/test.dsml.xml" />
+      <arg value="DNS|com.sun.jndi.dns.DnsContextFactory|dns://127.0.0.1/localhost" />
+      <arg value="FS|org.apache.naming.modules.fs.fsURLContextFactory|fs:/" />
+      <arg value="Mem|org.apache.naming.modules.memory.memoryURLContextFactory|memory:/" />
+    </java>
+  </target>
+
+</project>
diff --git a/connectors/naming/src/jndi.properties b/connectors/naming/src/jndi.properties
new file mode 100644
index 0000000..df838bd
--- /dev/null
+++ b/connectors/naming/src/jndi.properties
@@ -0,0 +1,6 @@
+#java.naming.factory.object=
+#java.naming.factory.state=
+#java.naming.factory.control=
+
+java.naming.factory.initial=org.apache.naming.modules.memory.memoryURLContextFactory
+java.naming.factory.url.pkgs=org.apache.naming.modules
diff --git a/connectors/naming/src/org/apache/naming/ant/JndiEnv.java b/connectors/naming/src/org/apache/naming/ant/JndiEnv.java
new file mode 100644
index 0000000..adb3063
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/ant/JndiEnv.java
@@ -0,0 +1,126 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.ant;
+
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.naming.Context;
+
+import org.apache.tools.ant.Task;
+
+/**
+ *  Task to set up JNDI properties ( the hashtable that is passed to InitialContext )
+ *  It has explicit attributes for common properties, and supports generic name/value
+ *  elements.
+ * 
+ * @author  Costin Manolache
+ */
+public class JndiEnv extends Task  {
+ 
+    Hashtable env = new Hashtable();
+    String url;
+    boolean topLevel=true;
+    
+    public JndiEnv() {
+    }
+    
+    public JndiEnv(boolean child) {
+        topLevel=false;
+    }
+    
+    public String getProviderUrl() {
+        return (String) env.get(Context.PROVIDER_URL);
+    }
+    
+    public void setProviderUrl(String providerUrl) {
+        env.put(Context.PROVIDER_URL, providerUrl);
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getInitialFactory() {
+        return (String) env.get(Context.INITIAL_CONTEXT_FACTORY);
+    }
+
+    public void setInitialFactory(String initialFactory) {
+        env.put(Context.INITIAL_CONTEXT_FACTORY, initialFactory);
+    }
+
+    public String getAuthoritative() {
+        return (String) env.get(Context.AUTHORITATIVE);
+    }
+
+    public void setAuthoritative(String authoritative) {
+        env.put(Context.AUTHORITATIVE, authoritative);
+    }
+
+    public String getObjectFactories() {
+        return (String) env.get(Context.OBJECT_FACTORIES);
+    }
+
+    public void setObjectFactories(String objectFactories) {
+        env.put(Context.OBJECT_FACTORIES, objectFactories);
+    }
+
+    public String getUrlPkgPrefixes() {
+        return (String) env.get(Context.URL_PKG_PREFIXES);
+    }
+
+    public void setUrlPkgPrefixes(String urlPkgPrefixes) {
+        env.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+    }
+
+    public void execute() {
+        if( nvEntries!=null ) {
+            for( int i=0; i<nvEntries.size(); i++ ) {
+                NameValue nv=(NameValue)nvEntries.elementAt(i);
+                env.put( nv.name, nv.value);
+            }
+        }
+        // If this is a standalone task - add a ref in the project.
+        if(topLevel)
+            project.addReference( "globalJndiEnv", this );
+    }
+
+    Vector nvEntries;
+    
+    public NameValue addEnv() {
+        if( nvEntries==null ) nvEntries=new Vector();
+        NameValue nv=new NameValue();
+        nvEntries.addElement( nv );
+        return nv;
+    }
+    
+    public static class NameValue {
+        String name;
+        String value;
+        
+        public void setName(String name) {
+            this.name=name;
+        }
+        public void setValue(String value) {
+            this.value=value;
+        }
+    }
+}
diff --git a/connectors/naming/src/org/apache/naming/ant/JndiProperties.java b/connectors/naming/src/org/apache/naming/ant/JndiProperties.java
new file mode 100644
index 0000000..ca26d29
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/ant/JndiProperties.java
@@ -0,0 +1,97 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.ant;
+
+import javax.naming.InitialContext;
+
+import org.apache.tools.ant.PropertyHelper;
+import org.apache.tools.ant.Task;
+
+
+/**
+ *  Dynamic properties from a JNDI context. Use ${jndi:NAME} syntax.
+ *  You may need to use <jndiEnv> to set up jndi properties and drivers,
+ *  and eventually different context-specific tasks.
+ *
+ * @author Costin Manolache
+ */
+public class JndiProperties extends Task {
+    public static String PREFIX="jndi:";
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( JndiProperties.class );
+    private JndiHelper helper=new JndiHelper();
+
+    public JndiProperties() {
+        initNaming();
+    }
+
+    static boolean initialized=false;
+    static void initNaming() {
+        if( initialized ) return;
+        initialized=true;
+        Thread.currentThread().setContextClassLoader( JndiProperties.class.getClassLoader() );
+//         System.setProperty( "java.naming.factory.initial", "org.apache.naming.memory.MemoryInitialContextFactory" );
+    }
+    
+    class JndiHelper extends PropertyHelper {
+        public boolean setPropertyHook( String ns, String name, Object v, boolean inh,
+                                        boolean user, boolean isNew)
+        {
+            if( ! name.startsWith(PREFIX) ) {
+                // pass to next
+                return super.setPropertyHook(ns, name, v, inh, user, isNew);
+            }
+            name=name.substring( PREFIX.length() );
+
+            // XXX later
+
+            return true;
+        }
+
+        public Object getPropertyHook( String ns, String name , boolean user) {
+            if( ! name.startsWith(PREFIX) ) {
+                // pass to next
+                return super.getPropertyHook(ns, name, user);
+            }
+
+            Object o=null;
+            name=name.substring( PREFIX.length() );
+            try {
+                InitialContext ic=new InitialContext();
+                // XXX lookup attribute in DirContext ?
+                o=ic.lookup( name );
+                if( log.isDebugEnabled() ) log.debug( "getProperty jndi: " + name +  " " + o);
+            } catch( Exception ex ) {
+                log.error("getProperty " + name , ex);
+                o=null;
+            }
+            return o;
+        }
+
+    }
+
+
+    public void execute() {
+        PropertyHelper phelper=PropertyHelper.getPropertyHelper( project );
+        helper.setProject( project );
+        helper.setNext( phelper.getNext() );
+        phelper.setNext( helper );
+
+        project.addReference( "jndiProperties", this );
+    }
+    
+}
diff --git a/connectors/naming/src/org/apache/naming/ant/JndiSet.java b/connectors/naming/src/org/apache/naming/ant/JndiSet.java
new file mode 100644
index 0000000..8b26e8f
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/ant/JndiSet.java
@@ -0,0 +1,86 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.ant;
+
+import javax.naming.InitialContext;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskAdapter;
+
+
+/**
+ * Set a JNDI attribute or context.
+ *
+ * @author Costin Manolache
+ */
+public class JndiSet extends Task  {
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( JndiSet.class );
+    String refId;
+    String value;
+    
+    String contextName;
+    String attributeName;
+    
+    public JndiSet() {
+    }
+
+    /** Will bind the referenced object
+     */
+    public void setRefId( String refId ) {
+        this.refId=refId;
+    }
+
+    /** bind/set this value
+     */
+    public void setValue( String val ) {
+        this.value=val;
+    }
+    
+    /** The context name that will be set.
+     */
+    public void setContext( String ctx ) {
+        this.contextName=ctx;
+    }
+
+    public void setAttribute( String att ) {
+        this.attributeName=att;
+    }
+    
+    
+    public void execute() {
+        try {
+            InitialContext ic=new InitialContext();
+            Object o=null;
+            
+            if( refId != null ) {
+                o=project.getReference( refId );
+                if( o instanceof TaskAdapter )
+                    o=((TaskAdapter)o).getProxy();
+            }
+            if( o==null )
+                o=value;
+            // Add other cases.
+            log.info( "Binding " + contextName + " " + o );
+            ic.bind( contextName, o );
+
+        } catch( Exception ex ) {
+            ex.printStackTrace();
+        }
+    }
+    
+}
diff --git a/connectors/naming/src/org/apache/naming/core/BaseContext.java b/connectors/naming/src/org/apache/naming/core/BaseContext.java
new file mode 100644
index 0000000..2f4b737
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/core/BaseContext.java
@@ -0,0 +1,619 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.core;
+
+import java.util.Hashtable;
+
+import javax.naming.CompositeName;
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NameParser;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NotContextException;
+import javax.naming.OperationNotSupportedException;
+import javax.naming.directory.SearchControls;
+
+// Based on a merge of various catalina naming contexts
+// Name is used - it provide better oportunities for reuse and optimizations
+
+/**
+ * Base Context implementation. Use it if the source doesn't support attributes.
+ *
+ * Implements all JNDI methods with reasonable defaults or UnsuportedOperation.
+ *
+ * IMPORTANT: all contexts should use setters/getters for configuration, instead
+ * of the Hashtable. The default constructor will use introspection to configure
+ * and may provide ( via a hook ? ) JMX management on all contexts.
+ *
+ * All methods use Name variant. They should expect an arbitrary implementation, but
+ * it's recommended to check if ServerName is used - and take advantage of the
+ * specific features ( MessageBytes, etc ).
+ *
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ */
+public class BaseContext extends BaseNaming implements Context {
+
+    public BaseContext() {
+        super();
+    }
+
+    public BaseContext(Hashtable env) {
+        super(env);
+    }
+
+
+    // -------------------- Context impl --------------------
+
+    /**
+     * Retrieves the named object. If name is empty, returns a new instance
+     * of this context (which represents the same naming context as this
+     * context, but its environment may be modified independently and it may
+     * be accessed concurrently).
+     *
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public Object lookup(Name name)
+            throws NamingException {
+        return lookup(name, true);
+    }
+
+    /**
+     * Retrieves the named object.
+     *
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public Object lookup(String name)
+            throws NamingException {
+        return lookup(string2Name(name), true);
+    }
+
+
+    /**
+     * Binds a name to an object. All intermediate contexts and the target
+     * context (that named by all but terminal atomic component of the name)
+     * must already exist.
+     *
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception javax.naming.NameAlreadyBoundException if name is already bound
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public void bind(Name name, Object obj)
+            throws NamingException {
+        bind(name, obj, null, false);
+    }
+
+
+    /**
+     * Binds a name to an object.
+     *
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception javax.naming.NameAlreadyBoundException if name is already bound
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public void bind(String name, Object obj)
+            throws NamingException {
+        bind(string2Name(name), obj, null, false);
+    }
+
+
+    /**
+     * Binds a name to an object, overwriting any existing binding. All
+     * intermediate contexts and the target context (that named by all but
+     * terminal atomic component of the name) must already exist.
+     * <p>
+     * If the object is a DirContext, any existing attributes associated with
+     * the name are replaced with those of the object. Otherwise, any
+     * existing attributes associated with the name remain unchanged.
+     *
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public void rebind(Name name, Object obj)
+            throws NamingException {
+        bind(name, obj, null, true);
+    }
+
+
+    /**
+     * Binds a name to an object, overwriting any existing binding.
+     *
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public void rebind(String name, Object obj)
+            throws NamingException {
+        bind(string2Name(name), obj, null, true);
+    }
+
+
+    /**
+     * Unbinds the named object. Removes the terminal atomic name in name
+     * from the target context--that named by all but the terminal atomic
+     * part of name.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic
+     * name is not bound in the target context, but throws
+     * NameNotFoundException if any of the intermediate contexts do not exist.
+     *
+     * @param name the name to bind; may not be empty
+     * @exception javax.naming.NameNotFoundException if an intermediate context does not
+     * exist
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public void unbind(Name name)
+            throws NamingException {
+        unbind(name, false);
+    }
+
+    public void unbind(String name)
+            throws NamingException {
+        unbind(string2Name(name), false);
+    }
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the
+     * old name. Both names are relative to this context. Any attributes
+     * associated with the old name become associated with the new name.
+     * Intermediate contexts of the old name are not changed.
+     *
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception javax.naming.NameAlreadyBoundException if newName is already bound
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public void rename(String oldName, String newName)
+            throws NamingException {
+        rename(string2Name(oldName), string2Name(newName));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class
+     * names of objects bound to them. The contents of any subcontexts are
+     * not included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on
+     * an enumeration previously returned is undefined.
+     *
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(String name)
+            throws NamingException {
+        return list(string2Name(name));
+    }
+
+    public NamingEnumeration list(Name name)
+            throws NamingException {
+        return new NamingContextEnumeration(getChildren(), this, false);
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the
+     * objects bound to them. The contents of any subcontexts are not
+     * included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on
+     * an enumeration previously returned is undefined.
+     *
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context.
+     * Each element of the enumeration is of type Binding.
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(Name name)
+            throws NamingException {
+        return new NamingContextEnumeration(getChildren(), this, true);
+    }
+
+    public NamingEnumeration listBindings(String name)
+            throws NamingException {
+        return listBindings(string2Name(name));
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace. Any
+     * attributes associated with the name are also removed. Intermediate
+     * contexts are not destroyed.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic
+     * name is not bound in the target context, but throws
+     * NameNotFoundException if any of the intermediate contexts do not exist.
+     *
+     * In a federated naming system, a context from one naming system may be
+     * bound to a name in another. One can subsequently look up and perform
+     * operations on the foreign context using a composite name. However, an
+     * attempt destroy the context using this composite name will fail with
+     * NotContextException, because the foreign context is not a "subcontext"
+     * of the context in which it is bound. Instead, use unbind() to remove
+     * the binding of the foreign context. Destroying the foreign context
+     * requires that the destroySubcontext() be performed on a context from
+     * the foreign context's "native" naming system.
+     *
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception javax.naming.NameNotFoundException if an intermediate context does not
+     * exist
+     * @exception javax.naming.NotContextException if the name is bound but does not name
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(Name name)
+            throws NamingException {
+        unbind(name, true);
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace.
+     *
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception javax.naming.NameNotFoundException if an intermediate context does not
+     * exist
+     * @exception javax.naming.NotContextException if the name is bound but does not name
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(String name)
+            throws NamingException {
+        unbind(string2Name(name), true);
+    }
+
+
+    /**
+     * Creates and binds a new context. Creates a new context with the given
+     * name and binds it in the target context (that named by all but
+     * terminal atomic component of the name). All intermediate contexts and
+     * the target context must already exist.
+     *
+     * @param name the name of the context to create; may not be empty
+     * @return the newly created context
+     * @exception javax.naming.NameAlreadyBoundException if name is already bound
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public Context createSubcontext(Name name)
+            throws NamingException {
+        return createSubcontext(name, null);
+    }
+
+    public Context createSubcontext(String name)
+            throws NamingException {
+        return createSubcontext(string2Name(name), null);
+    }
+
+    public void rename(Name oldName, Name newName)
+            throws NamingException
+        {
+            // Override if needed
+            Object value = lookup(oldName, false);
+            bind(newName, value, null, false);
+            unbind(oldName, true);
+
+        }
+
+    /**
+     * Retrieves the named object, following links except for the terminal
+     * atomic component of the name. If the object bound to name is not a
+     * link, returns the object itself.
+     *
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link
+     * (if any).
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public Object lookupLink(Name name)
+            throws NamingException {
+        return lookup(name, false);
+    }
+
+
+    /**
+     * Retrieves the named object, following links except for the terminal
+     * atomic component of the name.
+     *
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link
+     * (if any).
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public Object lookupLink(String name)
+            throws NamingException {
+        return lookupLink(string2Name(name));
+    }
+
+
+    /**
+     * Retrieves the parser associated with the named context. In a
+     * federation of namespaces, different naming systems will parse names
+     * differently. This method allows an application to get a parser for
+     * parsing names into their atomic components using the naming convention
+     * of a particular naming system. Within any single naming system,
+     * NameParser objects returned by this method must be equal (using the
+     * equals() test).
+     *
+     * @param name the name of the context from which to get the parser
+     * @return a name parser that can parse compound names into their atomic
+     * components
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public NameParser getNameParser(Name name)
+            throws NamingException {
+
+        while ((!name.isEmpty()) && (name.get(0).length() == 0))
+            name = name.getSuffix(1);
+        if (name.isEmpty())
+            return nameParser;
+
+        if (name.size() > 1) {
+            Object obj = lookup(name.get(0));
+            if (obj instanceof Context) {
+                return ((Context) obj).getNameParser(name.getSuffix(1));
+            } else {
+                throw new NotContextException(name.toString());
+            }
+        }
+
+        return nameParser;
+
+    }
+
+
+    /**
+     * Retrieves the parser associated with the named context.
+     *
+     * @param name the name of the context from which to get the parser
+     * @return a name parser that can parse compound names into their atomic
+     * components
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public NameParser getNameParser(String name)
+            throws NamingException {
+        return getNameParser(new CompositeName(name));
+    }
+
+    /**
+     * Composes the name of this context with a name relative to this context.
+     * <p>
+     * Given a name (name) relative to this context, and the name (prefix)
+     * of this context relative to one of its ancestors, this method returns
+     * the composition of the two names using the syntax appropriate for the
+     * naming system(s) involved. That is, if name names an object relative
+     * to this context, the result is the name of the same object, but
+     * relative to the ancestor context. None of the names may be null.
+     *
+     * @param name a name relative to this context
+     * @param prefix the name of this context relative to one of its ancestors
+     * @return the composition of prefix and name
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public Name composeName(Name name, Name prefix)
+            throws NamingException {
+        prefix = (Name) name.clone();
+        return prefix.addAll(name);
+    }
+
+
+    /**
+     * Composes the name of this context with a name relative to this context.
+     *
+     * @param name a name relative to this context
+     * @param prefix the name of this context relative to one of its ancestors
+     * @return the composition of prefix and name
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public String composeName(String name, String prefix)
+            throws NamingException {
+        return prefix + "/" + name;
+    }
+
+
+    /**
+     * Adds a new environment property to the environment of this context. If
+     * the property already exists, its value is overwritten.
+     *
+     * @param propName the name of the environment property to add; may not
+     * be null
+     * @param propVal the value of the property to add; may not be null
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public Object addToEnvironment(String propName, Object propVal)
+            throws NamingException {
+        return env.put(propName, propVal);
+    }
+
+
+    /**
+     * Removes an environment property from the environment of this context.
+     *
+     * @param propName the name of the environment property to remove;
+     * may not be null
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public Object removeFromEnvironment(String propName)
+            throws NamingException {
+        return env.remove(propName);
+    }
+
+
+    /**
+     * Retrieves the environment in effect for this context. See class
+     * description for more details on environment properties.
+     * The caller should not make any changes to the object returned: their
+     * effect on the context is undefined. The environment of this context
+     * may be changed using addToEnvironment() and removeFromEnvironment().
+     *
+     * @return the environment of this context; never null
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public Hashtable getEnvironment()
+            throws NamingException {
+        return env;
+    }
+
+
+    /**
+     * Closes this context. This method releases this context's resources
+     * immediately, instead of waiting for them to be released automatically
+     * by the garbage collector.
+     * This method is idempotent: invoking it on a context that has already
+     * been closed has no effect. Invoking any other method on a closed
+     * context is not allowed, and results in undefined behaviour.
+     *
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public void close()
+            throws NamingException {
+        // We don't own the env., but the clone
+        env.clear();
+    }
+
+
+    /**
+     * Retrieves the full name of this context within its own namespace.
+     * <p>
+     * Many naming services have a notion of a "full name" for objects in
+     * their respective namespaces. For example, an LDAP entry has a
+     * distinguished name, and a DNS record has a fully qualified name. This
+     * method allows the client application to retrieve this name. The string
+     * returned by this method is not a JNDI composite name and should not be
+     * passed directly to context methods. In naming systems for which the
+     * notion of full name does not make sense,
+     * OperationNotSupportedException is thrown.
+     *
+     * @return this context's name in its own namespace; never null
+     * @exception javax.naming.OperationNotSupportedException if the naming system does
+     * not have the notion of a full name
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public String getNameInNamespace()
+            throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+    /**
+     * Searches in the named context or object for entries that satisfy the
+     * given search filter. Performs the search as specified by the search
+     * controls.
+     *
+     * @param name the name of the context or object to search
+     * @param filter the filter expression to use for the search; may not be
+     * null
+     * @param cons the search controls that control the search. If null,
+     * the default search controls are used (equivalent to
+     * (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisfy
+     * the filter; never null
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search
+            (Name name, String filter, SearchControls cons)
+            throws NamingException {
+        return search(name.toString(), filter, cons);
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the
+     * given search filter. Performs the search as specified by the search
+     * controls.
+     *
+     * @param name the name of the context or object to search
+     * @param filter the filter expression to use for the search; may not be
+     * null
+     * @param cons the search controls that control the search. If null,
+     * the default search controls are used (equivalent to
+     * (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisfy
+     * the filter; never null
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, String filter,
+                                    SearchControls cons)
+            throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the
+     * given search filter. Performs the search as specified by the search
+     * controls.
+     *
+     * @param name the name of the context or object to search
+     * @param filterExpr the filter expression to use for the search.
+     * The expression may contain variables of the form "{i}" where i is a
+     * nonnegative integer. May not be null.
+     * @param filterArgs the array of arguments to substitute for the
+     * variables in filterExpr. The value of filterArgs[i] will replace each
+     * occurrence of "{i}". If null, equivalent to an empty array.
+     * @param cons the search controls that control the search. If null, the
+     * default search controls are used (equivalent to (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisy the
+     * filter; never null
+     * @exception java.lang.ArrayIndexOutOfBoundsException if filterExpr contains {i}
+     * expressions where i is outside the bounds of the array filterArgs
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(Name name, String filterExpr,
+                                    Object[] filterArgs, SearchControls cons)
+            throws NamingException {
+        return search(name.toString(), filterExpr, filterArgs, cons);
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the
+     * given search filter. Performs the search as specified by the search
+     * controls.
+     *
+     * @param name the name of the context or object to search
+     * @param filterExpr the filter expression to use for the search.
+     * The expression may contain variables of the form "{i}" where i is a
+     * nonnegative integer. May not be null.
+     * @param filterArgs the array of arguments to substitute for the
+     * variables in filterExpr. The value of filterArgs[i] will replace each
+     * occurrence of "{i}". If null, equivalent to an empty array.
+     * @param cons the search controls that control the search. If null, the
+     * default search controls are used (equivalent to (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisy the
+     * filter; never null
+     * @exception java.lang.ArrayIndexOutOfBoundsException if filterExpr contains {i}
+     * expressions where i is outside the bounds of the array filterArgs
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, String filterExpr,
+                                    Object[] filterArgs,
+                                    SearchControls cons)
+            throws NamingException {
+        throw new OperationNotSupportedException();
+    }
+
+
+}
+
diff --git a/connectors/naming/src/org/apache/naming/core/BaseDirContext.java b/connectors/naming/src/org/apache/naming/core/BaseDirContext.java
new file mode 100644
index 0000000..3f126f4
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/core/BaseDirContext.java
@@ -0,0 +1,599 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.core;
+
+import java.util.Hashtable;
+
+import javax.naming.Name;
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.OperationNotSupportedException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+
+
+//import org.apache.naming.core.NameParserImpl;
+
+// Based on a merge of various catalina naming contexts
+// Name is used - it provide better oportunities for reuse and optimizations
+
+/**
+ * Base Directory Context implementation. All j-t-c/naming contexts should
+ * extend it.
+ *
+ * Implements all JNDI methods - if you just extend it you'll get UnsuportedOperation.
+ * XXX Should it also act as introspector proxy or should we use a separate context ?
+ * The intention is to allow use 'introspection magic' and bean-like DirContexts.
+ *
+ * IMPORTANT: all contexts should use setters/getters for configuration, instead
+ * of the Hashtable. The default constructor will use introspection to configure
+ * and may provide ( via a hook ? ) JMX management on all contexts.
+ *
+ * You must extend and override few methods. Of course, you can also override any other
+ * method and provide a more optimal implementation, but in most cases you only
+ * need the minimal set.
+ *
+ * All methods use Name variant. They should expect an arbitrary implementation, but
+ * it's recommended to check if ServerName is used - and take advantage of the
+ * specific features ( MessageBytes, etc ).
+ *
+ * <ul>
+ *   <li>
+ * </ul>
+ *
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ */
+public class BaseDirContext extends BaseContext implements DirContext {
+
+    public BaseDirContext()
+    {
+        super();
+    }
+
+    public BaseDirContext(Hashtable env)
+    {
+        super(env);
+    }
+
+
+    // ----------------------------------------------------- DirContext Methods
+
+    /**
+     * Retrieves all of the attributes associated with a named object.
+     *
+     * @return the set of attributes associated with name.
+     * Returns an empty attribute set if name has no attributes; never null.
+     * @param name the name of the object from which to retrieve attributes
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(Name name)
+            throws NamingException
+    {
+        return getAttributes(name, null);
+    }
+
+
+    /**
+     * Retrieves all of the attributes associated with a named object.
+     *
+     * @return the set of attributes associated with name
+     * @param name the name of the object from which to retrieve attributes
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(String name)
+            throws NamingException
+    {
+        return getAttributes(string2Name(name));
+    }
+
+    /**
+     * Retrieves selected attributes associated with a named object.
+     * See the class description regarding attribute models, attribute type
+     * names, and operational attributes.
+     *
+     * @return the requested attributes; never null
+     * @param name the name of the object from which to retrieve attributes
+     * @param attrIds the identifiers of the attributes to retrieve. null
+     * indicates that all attributes should be retrieved; an empty array
+     * indicates that none should be retrieved
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(String name, String[] attrIds)
+            throws NamingException
+    {
+        return getAttributes(string2Name(name), attrIds);
+    }
+
+    public Attributes getAttributes(Name name, String[] attrIds)
+            throws NamingException
+    {
+        if( attrIds==null ) {
+            attrIds= super.getAttributeNames(name);
+        }
+        Attributes res=new ServerAttributes();
+        if( attrIds==null ) return res;
+
+        for( int i=0; i<attrIds.length; i++ ) {
+            Object val=super.getAttribute(name, attrIds[i]);
+            res.put( attrIds[i], val );
+        }
+        return res;
+    }
+
+
+    /**
+     * Modifies the attributes associated with a named object. The order of
+     * the modifications is not specified. Where possible, the modifications
+     * are performed atomically.
+     *
+     * @param name the name of the object whose attributes will be updated
+     * @param mod_op the modification operation, one of: ADD_ATTRIBUTE,
+     * REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE
+     * @param attrs the attributes to be used for the modification; may not
+     * be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(Name name, int mod_op, Attributes attrs)
+            throws NamingException
+    {
+        NamingEnumeration enum=attrs.getAll();
+        while( enum.hasMoreElements() ) {
+            Attribute att=(Attribute)enum.nextElement();
+            switch( mod_op ) {
+            case ADD_ATTRIBUTE:
+            case REPLACE_ATTRIBUTE:
+                for( int i=0; i< att.size(); i++ ) {
+                    super.setAttribute(name, att.getID(), att.get(i));
+                }
+                break;
+            case REMOVE_ATTRIBUTE:
+                break;
+            }
+        }
+    }
+
+    public void modifyAttributes(String name, int mod_op, Attributes attrs)
+            throws NamingException
+    {
+        modifyAttributes(string2Name(name), mod_op, attrs);
+    }
+
+
+    /**
+     * Modifies the attributes associated with a named object using an an
+     * ordered list of modifications. The modifications are performed in the
+     * order specified. Each modification specifies a modification operation
+     * code and an attribute on which to operate. Where possible, the
+     * modifications are performed atomically.
+     *
+     * @param name the name of the object whose attributes will be updated
+     * @param mods an ordered sequence of modifications to be performed; may
+     * not be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(Name name, ModificationItem[] mods)
+            throws NamingException
+    {
+        if (mods==null) return;
+        for (int i=0; i<mods.length; i++) {
+
+            switch( mods[i].getModificationOp() ) {
+            case ADD_ATTRIBUTE:
+            case REPLACE_ATTRIBUTE:
+            case REMOVE_ATTRIBUTE:
+            };
+        }
+    }
+
+
+    public void modifyAttributes(String name, ModificationItem[] mods)
+            throws NamingException
+    {
+        modifyAttributes(string2Name(name), mods);
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes. If attrs
+     * is null, the resulting binding will have the attributes associated
+     * with obj if obj is a DirContext, and no attributes otherwise. If attrs
+     * is non-null, the resulting binding will have attrs as its attributes;
+     * any attributes associated with obj are ignored.
+     *
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception javax.naming.NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if some "mandatory" attributes
+     * of the binding are not supplied
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public void bind(Name name, Object obj, Attributes attrs)
+            throws NamingException
+    {
+        super.bind( name, obj );
+
+        NamingEnumeration enum=attrs.getAll();
+        while( enum.hasMoreElements() ) {
+            Attribute att=(Attribute)enum.nextElement();
+
+            Object val=getAttribute(name, att.getID() );
+            if( val != null ) {
+                throw new NameAlreadyBoundException(name.toString() + " : " +
+                        att.getID());
+            }
+
+            int size=att.size();
+            for( int i=0; i<size; i++ ) {
+                // probably need some addAttribute
+                setAttribute( name, att.getID(), att.get(i));
+            }
+        }
+    }
+
+    public void bind( String name, Object obj, Attributes attrs )
+            throws NamingException
+    {
+        bind(string2Name(name), obj, attrs);
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes,
+     * overwriting any existing binding. If attrs is null and obj is a
+     * DirContext, the attributes from obj are used. If attrs is null and obj
+     * is not a DirContext, any existing attributes associated with the object
+     * already bound in the directory remain unchanged. If attrs is non-null,
+     * any existing attributes associated with the object already bound in
+     * the directory are removed and attrs is associated with the named
+     * object. If obj is a DirContext and attrs is non-null, the attributes
+     * of obj are ignored.
+     *
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception InvalidAttributesException if some "mandatory" attributes
+     * of the binding are not supplied
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public void rebind(Name name, Object obj, Attributes attrs)
+            throws NamingException
+    {
+        bind(name, obj, attrs, true);
+    }
+
+    public void rebind(String name, Object obj, Attributes attrs)
+            throws NamingException
+    {
+        bind(string2Name(name), obj, attrs, true);
+    }
+
+
+    /**
+     * Creates and binds a new context, along with associated attributes.
+     * This method creates a new subcontext with the given name, binds it in
+     * the target context (that named by all but terminal atomic component of
+     * the name), and associates the supplied attributes with the newly
+     * created object. All intermediate and target contexts must already
+     * exist. If attrs is null, this method is equivalent to
+     * Context.createSubcontext().
+     *
+     * @param name the name of the context to create; may not be empty
+     * @param attrs the attributes to associate with the newly created context
+     * @return the newly created context
+     * @exception javax.naming.NameAlreadyBoundException if the name is already bound
+     * @exception InvalidAttributesException if attrs does not contain all
+     * the mandatory attributes required for creation
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public DirContext createSubcontext(String name, Attributes attrs)
+            throws NamingException
+    {
+        return createSubcontext(string2Name(name), attrs);
+    }
+
+
+    /**
+     * Retrieves the schema associated with the named object. The schema
+     * describes rules regarding the structure of the namespace and the
+     * attributes stored within it. The schema specifies what types of
+     * objects can be added to the directory and where they can be added;
+     * what mandatory and optional attributes an object can have. The range
+     * of support for schemas is directory-specific.
+     *
+     * @param name the name of the object whose schema is to be retrieved
+     * @return the schema associated with the context; never null
+     * @exception javax.naming.OperationNotSupportedException if schema not supported
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public DirContext getSchema(Name name)
+            throws NamingException
+    {
+        return getSchema(name.toString());
+    }
+
+
+    /**
+     * Retrieves the schema associated with the named object.
+     *
+     * @param name the name of the object whose schema is to be retrieved
+     * @return the schema associated with the context; never null
+     * @exception javax.naming.OperationNotSupportedException if schema not supported
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public DirContext getSchema(String name)
+            throws NamingException
+    {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Retrieves a context containing the schema objects of the named
+     * object's class definitions.
+     *
+     * @param name the name of the object whose object class definition is to
+     * be retrieved
+     * @return the DirContext containing the named object's class
+     * definitions; never null
+     * @exception javax.naming.OperationNotSupportedException if schema not supported
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public DirContext getSchemaClassDefinition(Name name)
+            throws NamingException
+    {
+        return getSchemaClassDefinition(name.toString());
+    }
+
+
+    /**
+     * Retrieves a context containing the schema objects of the named
+     * object's class definitions.
+     *
+     * @param name the name of the object whose object class definition is to
+     * be retrieved
+     * @return the DirContext containing the named object's class
+     * definitions; never null
+     * @exception javax.naming.OperationNotSupportedException if schema not supported
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public DirContext getSchemaClassDefinition(String name)
+            throws NamingException
+    {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set
+     * of attributes, and retrieves selected attributes. The search is
+     * performed using the default SearchControls settings.
+     *
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or
+     * null, all objects in the target context are returned.
+     * @param attributesToReturn the attributes to return. null indicates
+     * that all attributes are to be returned; an empty array indicates that
+     * none are to be returned.
+     * @return a non-null enumeration of SearchResult objects. Each
+     * SearchResult contains the attributes identified by attributesToReturn
+     * and the name of the corresponding object, named relative to the
+     * context named by name.
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(Name name, Attributes matchingAttributes,
+                                    String[] attributesToReturn)
+            throws NamingException
+    {
+        return search(name.toString(), matchingAttributes, attributesToReturn);
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set
+     * of attributes, and retrieves selected attributes.
+     *
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or
+     * null, all objects in the target context are returned.
+     * @param attributesToReturn the attributes to return. null indicates
+     * that all attributes are to be returned; an empty array indicates that
+     * none are to be returned.
+     * @return a non-null enumeration of SearchResult objects. Each
+     * SearchResult contains the attributes identified by attributesToReturn
+     * and the name of the corresponding object, named relative to the
+     * context named by name.
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, Attributes matchingAttributes,
+                                    String[] attributesToReturn)
+            throws NamingException
+    {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set
+     * of attributes. This method returns all the attributes of such objects.
+     * It is equivalent to supplying null as the atributesToReturn parameter
+     * to the method search(Name, Attributes, String[]).
+     *
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or
+     * null, all objects in the target context are returned.
+     * @return a non-null enumeration of SearchResult objects. Each
+     * SearchResult contains the attributes identified by attributesToReturn
+     * and the name of the corresponding object, named relative to the
+     * context named by name.
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(Name name, Attributes matchingAttributes)
+            throws NamingException
+    {
+        return search(name.toString(), matchingAttributes);
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set
+     * of attributes.
+     *
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or
+     * null, all objects in the target context are returned.
+     * @return a non-null enumeration of SearchResult objects. Each
+     * SearchResult contains the attributes identified by attributesToReturn
+     * and the name of the corresponding object, named relative to the
+     * context named by name.
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, Attributes matchingAttributes)
+            throws NamingException
+    {
+        throw new OperationNotSupportedException();
+    }
+
+    /**
+     * Searches in the named context or object for entries that satisfy the
+     * given search filter. Performs the search as specified by the search
+     * controls.
+     *
+     * @param name the name of the context or object to search
+     * @param filter the filter expression to use for the search; may not be
+     * null
+     * @param cons the search controls that control the search. If null,
+     * the default search controls are used (equivalent to
+     * (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisfy
+     * the filter; never null
+     * @exception InvalidSearchFilterException if the search filter specified
+     * is not supported or understood by the underlying directory
+     * @exception InvalidSearchControlsException if the search controls
+     * contain invalid settings
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(Name name, String filter,
+                                    SearchControls cons)
+            throws NamingException
+    {
+        return search(name.toString(), filter, cons);
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the
+     * given search filter. Performs the search as specified by the search
+     * controls.
+     *
+     * @param name the name of the context or object to search
+     * @param filter the filter expression to use for the search; may not be
+     * null
+     * @param cons the search controls that control the search. If null,
+     * the default search controls are used (equivalent to
+     * (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisfy
+     * the filter; never null
+     * @exception InvalidSearchFilterException if the search filter
+     * specified is not supported or understood by the underlying directory
+     * @exception InvalidSearchControlsException if the search controls
+     * contain invalid settings
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, String filter,
+                                    SearchControls cons)
+            throws NamingException
+    {
+        throw new OperationNotSupportedException();
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the
+     * given search filter. Performs the search as specified by the search
+     * controls.
+     *
+     * @param name the name of the context or object to search
+     * @param filterExpr the filter expression to use for the search.
+     * The expression may contain variables of the form "{i}" where i is a
+     * nonnegative integer. May not be null.
+     * @param filterArgs the array of arguments to substitute for the
+     * variables in filterExpr. The value of filterArgs[i] will replace each
+     * occurrence of "{i}". If null, equivalent to an empty array.
+     * @param cons the search controls that control the search. If null, the
+     * default search controls are used (equivalent to (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisy the
+     * filter; never null
+     * @exception java.lang.ArrayIndexOutOfBoundsException if filterExpr contains {i}
+     * expressions where i is outside the bounds of the array filterArgs
+     * @exception InvalidSearchControlsException if cons contains invalid
+     * settings
+     * @exception InvalidSearchFilterException if filterExpr with filterArgs
+     * represents an invalid search filter
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(Name name, String filterExpr,
+                                    Object[] filterArgs, SearchControls cons)
+            throws NamingException
+    {
+        return search(name.toString(), filterExpr, filterArgs, cons);
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the
+     * given search filter. Performs the search as specified by the search
+     * controls.
+     *
+     * @param name the name of the context or object to search
+     * @param filterExpr the filter expression to use for the search.
+     * The expression may contain variables of the form "{i}" where i is a
+     * nonnegative integer. May not be null.
+     * @param filterArgs the array of arguments to substitute for the
+     * variables in filterExpr. The value of filterArgs[i] will replace each
+     * occurrence of "{i}". If null, equivalent to an empty array.
+     * @param cons the search controls that control the search. If null, the
+     * default search controls are used (equivalent to (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisy the
+     * filter; never null
+     * @exception java.lang.ArrayIndexOutOfBoundsException if filterExpr contains {i}
+     * expressions where i is outside the bounds of the array filterArgs
+     * @exception InvalidSearchControlsException if cons contains invalid
+     * settings
+     * @exception InvalidSearchFilterException if filterExpr with filterArgs
+     * represents an invalid search filter
+     * @exception javax.naming.NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, String filterExpr,
+                                    Object[] filterArgs,
+                                    SearchControls cons)
+            throws NamingException
+    {
+        throw new OperationNotSupportedException();
+    }
+
+}
+
diff --git a/connectors/naming/src/org/apache/naming/core/BaseNaming.java b/connectors/naming/src/org/apache/naming/core/BaseNaming.java
new file mode 100644
index 0000000..2587303
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/core/BaseNaming.java
@@ -0,0 +1,309 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.core;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.naming.CompositeName;
+import javax.naming.InvalidNameException;
+import javax.naming.Name;
+import javax.naming.NameParser;
+import javax.naming.NamingException;
+import javax.naming.OperationNotSupportedException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+import org.apache.tomcat.util.IntrospectionUtils;
+
+// Based on a merge of various catalina naming contexts
+// Name is used - it provide better oportunities for reuse and optimizations
+
+/**
+ *  This is the base class for our naming operations, for easy reading.
+ *
+ *  <p>Creating a new context:
+ *  <ul>
+ *  <li>Create a new class, extending BaseContext or BaseDirContext ( second
+ *      if you want to support attributes ).
+ *  <li>Add setters for configuration. The setters will be called autmatically,
+ *      like in ant, from the initial env settings.
+ *  <li>Override methods that are defined in BaseNaming. Default behavior
+ *      is provided for all.
+ *  <li>If performance is a concern or have special behavior - override Context and
+ *      DirContext methods. That shouldn't be needed in most cases.
+ *  </ul>
+ *
+ *  This class is designed to minimize the ammount of code that is required to
+ * create a new context. The usual DirContext interface has way too many methods,
+ *  so implementation requires a lot of typing.
+ *
+ *  Our contexts are mostly wrappers for files or in memory structures. That means
+ *  some operations are cheaper, and we're far from the features that would be
+ *  exposed for an LDAP or real Directory server.
+ *
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ */
+public class BaseNaming {
+
+    /**
+     * Builds a base directory context.
+     */
+    public BaseNaming() {
+        this.env=new Hashtable();
+    }
+
+    /**
+     * Builds a base directory context using the given environment.
+     */
+    public BaseNaming(Hashtable env) {
+        this.env=new Hashtable();
+        if (env != null ) {
+            Enumeration envEntries = env.keys();
+            while (envEntries.hasMoreElements()) {
+                String entryName = (String) envEntries.nextElement();
+                Object entryValue=env.get(entryName);
+                this.env.put(entryName, entryValue);
+                try {
+                    // XXX We need a mechanism to select properties for
+                    // this task. Maybe all contexts should use as property prefix the
+                    // class name ? Or base class name ? 
+                    IntrospectionUtils.setAttribute( this, entryName, entryValue );
+                } catch(Exception ex ) {
+                    System.out.println("Unsuported property " + entryName + " " + ex.getMessage());
+                }
+            }
+        }
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Environment. All context config info.
+     */
+    protected Hashtable env;
+
+    /**
+     * Default name parser for this context.
+     * XXX This should be combined with the Tomcat mapper, and
+     * optimized for MessageBytes.
+     */
+    protected final NameParser nameParser = new NameParserImpl();
+
+    /** Prefix used for URL-based namming lookup. It must be removed
+     *  from all names.
+     *  Deprecated ? Do we need it ?
+     */
+    protected String urlPrefix="";
+
+    // ------------------------------------------------------------- Properties
+    // Common properties, apply to all jtc naming contexts.
+    
+    // XXX Introspection should be used to turn the Hashtable attributes
+    // into setters.
+    public void setURLPrefix( String s ) {
+        urlPrefix=s;
+    }
+
+    private boolean cached;
+    private int cacheTTL;
+    private int cacheObjectMaxSize;
+
+    public boolean isCached() {
+        return cached;
+    }
+
+    public void setCached(boolean cached) {
+        this.cached = cached;
+    }
+
+    public int getCacheTTL() {
+        return cacheTTL;
+    }
+
+    public void setCacheTTL(int cacheTTL) {
+        this.cacheTTL = cacheTTL;
+    }
+
+    public int getCacheObjectMaxSize() {
+        return cacheObjectMaxSize;
+    }
+
+    public void setCacheObjectMaxSize(int cacheObjectMaxSize) {
+        this.cacheObjectMaxSize = cacheObjectMaxSize;
+    }
+
+    // -------------------- Not so Abstract methods --------------------
+    // This is what a subclass should implement.
+
+    // XXX Base resolveLinks() method ?? And then use lookup without resolveLinks flag
+    
+    /** The lookup method. This is the main method you should implement.
+     *
+     * @param name
+     * @param resolveLinks If false, this is a lookupLink call.
+     */
+    public Object lookup(Name name, boolean resolveLinks)
+        throws NamingException
+    {
+        throw new OperationNotSupportedException();
+    }
+
+    /** The setter method. Implement it if the context is read/write.
+     *
+     * @param name 
+     * @param obj The object to be bound.
+     * @param attrs Attributes - if this is a dir context, null otherwise
+     * @param rebind What to do if the name is already bound. 
+     *     XXX can be further simplified - do a lookup and implement it. 
+     */
+    public void bind(Name name, Object obj, Attributes attrs, boolean rebind )
+        throws NamingException
+    {
+        throw new OperationNotSupportedException();
+    }
+
+    /** Remove a binding. XXX do we need the isContext case ?
+     */
+    public void unbind(Name name, boolean isContext)
+        throws NamingException
+    {
+        throw new OperationNotSupportedException();
+    }
+
+    /* XXX There are 2 ways to list the childs: array ( size/elementAt ) or
+       iterator/Enumeration. Since the JNDI interface uses iteration -
+       that's what we should use.
+       */
+
+    /** Return the child elements, if any.
+     *
+     * This is a String or Name or Binding or NameClassPari enumeration -
+     *  the Context implementation will wrap it as a NamingEnumeration and
+     *  construct the right information.
+     *
+     * XXX name is all we need - all other info can be extracted - with some
+     * penalty. It's easy to do some instanceof tricks to avoid it, if possible,
+     * but the goal is to make it easy to write contexts, and name should be
+     * enough.
+     */
+    public Enumeration getChildren() throws NamingException {
+        return null;
+    }
+
+    public DirContext createSubcontext(Name name, Attributes attrs)
+        throws NamingException
+    {
+        // XXX We can implement a decent default using bind and the current class.
+        throw new OperationNotSupportedException();
+    }
+
+    /** Implement for directories
+     *
+     */
+    public Object getAttribute( Name name, String attName )
+        throws NamingException
+    {
+        throw new OperationNotSupportedException();
+    }
+
+    public void setAttribute( Name name, String attName, Object value )
+        throws NamingException
+    {
+        throw new OperationNotSupportedException();
+    }
+
+    public String[] getAttributeNames(Name name )
+        throws NamingException
+    {
+        throw new OperationNotSupportedException();
+    }
+
+
+    // -------------------- Utils --------------------
+    // XXX Implement this
+
+    /**
+     * Returns true if writing is allowed on this context.
+     */
+    protected boolean isWritable(Name name) {
+        return true;
+        //return ContextAccessController.isWritable(name);
+    }
+
+
+    /**
+     * Throws a naming exception is Context is not writable.
+     */
+    protected void checkWritable(Name n)
+        throws NamingException
+    {
+        if (!isWritable(n))
+            throw new NamingException("read only");
+    }
+
+    protected Name string2Name(String s ) throws InvalidNameException {
+        // XXX uniq
+        //        try {
+            return new CompositeName( s );
+//         } catch( InvalidNameException ex ) {
+//             ex.printStackTrace();
+//             return null;
+//         }
+    }
+    
+    // -------------------- Lifecycle methods ? --------------------
+
+    /**
+     * Allocate resources for this directory context.
+     */
+    public void allocate() {
+        ; // No action taken by the default implementation
+    }
+
+
+    /**
+     * Release any resources allocated for this directory context.
+     */
+    public void release() {
+        ; // No action taken by the default implementation
+    }
+
+    public void recycle() {
+        // nothing yet.
+    }
+
+    //-------------------- Helpers --------------------
+
+    /** Just a hack so that all DirContexts can be used as tasks.
+     * They'll do nothing - the setters will be called ( just like
+     * new Context(Hashtable) - since we use introspection ) and the
+     * context can be registred as a reference in the Project ns.
+     *
+     * Then other tasks could manipulate it by name.
+     *
+     * In a future version of ant we should have the 'references'
+     * pluggable and a possible impl should be JNDI.
+     *
+     * Alternative: there is a way to use tasks without this method,
+     * but for now it's simpler.
+     */
+    public void execute() {
+    }
+}
+
diff --git a/connectors/naming/src/org/apache/naming/core/ContextAccessController.java b/connectors/naming/src/org/apache/naming/core/ContextAccessController.java
new file mode 100644
index 0000000..c97700e
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/core/ContextAccessController.java
@@ -0,0 +1,124 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.core;
+
+import java.util.Hashtable;
+
+/**
+ * Handles the access control on the JNDI contexts. All
+ * contexts implementations should use this.
+ *
+ * @author Remy Maucherat
+ */
+public class ContextAccessController {
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * Catalina context names on which writing is not allowed.
+     */
+    private static Hashtable readOnlyContexts = new Hashtable();
+
+
+    /**
+     * Security tokens repository.
+     */
+    private static Hashtable securityTokens = new Hashtable();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Set a security token for a context. Can be set only once.
+     * 
+     * @param name Name of the context
+     * @param context Security token
+     */
+    public static void setSecurityToken(Object name, Object token) {
+        if ((!securityTokens.containsKey(name)) && (token != null)) {
+            securityTokens.put(name, token);
+        }
+    }
+
+
+    /**
+     * Remove a security token for a context.
+     * 
+     * @param name Name of the context
+     * @param context Security token
+     */
+    public static void unsetSecurityToken(Object name, Object token) {
+        if (checkSecurityToken(name, token)) {
+            securityTokens.remove(name);
+        }
+    }
+
+
+    /**
+     * Check a submitted security token. The submitted token must be equal to
+     * the token present in the repository. If no token is present for the 
+     * context, then returns true.
+     * 
+     * @param name Name of the context
+     * @param context Submitted security token
+     */
+    public static boolean checkSecurityToken
+        (Object name, Object token) {
+        Object refToken = securityTokens.get(name);
+        if (refToken == null)
+            return (true);
+        if ((refToken != null) && (refToken.equals(token)))
+            return (true);
+        return (false);
+    }
+
+
+    /**
+     * Allow writing to a context.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void setWritable(Object name, Object token) {
+        if (checkSecurityToken(name, token))
+            readOnlyContexts.remove(name);
+    }
+
+
+    /**
+     * Set whether or not a context is writable.
+     * 
+     * @param name Name of the context
+     */
+    public static void setReadOnly(Object name) {
+        readOnlyContexts.put(name, name);
+    }
+
+
+    /**
+     * Returns if a context is writable.
+     * 
+     * @param name Name of the context
+     */
+    public static boolean isWritable(Object name) {
+        return !(readOnlyContexts.containsKey(name));
+    }
+}
+
diff --git a/connectors/naming/src/org/apache/naming/core/DirContextHelper.java b/connectors/naming/src/org/apache/naming/core/DirContextHelper.java
new file mode 100644
index 0000000..c9220a6
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/core/DirContextHelper.java
@@ -0,0 +1,64 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.core;
+
+import javax.naming.directory.DirContext;
+
+/**
+ * Utility class providing additional operations on DirContexts.
+ * Instead of extending DirContext we use a helper who will work
+ * on any context, including 'foreign' ones ( like JNDI, etc ).
+ *
+ * Typical methods - conversions ( int, boolean), etc.
+ *
+ * This code should check if the context extend our BaseDirContext
+ * and use specific features ( like notes - to cache the conversion
+ * results for example ), but fallback for external contexts.
+ *
+ * @author Costin Manolache
+ */
+public class DirContextHelper  {
+    static DirContextHelper instance=new DirContextHelper();
+    
+    public static DirContextHelper getInstance() {
+        return instance;
+    }
+    
+    
+    /** Debugging string - the context, imediate childs and attributes
+     */
+    public String toString(DirContext ctx) {
+        return "";
+    }
+
+    public int getIntAttribute( DirContext ctx, String name ) {
+        return 0;
+    }
+
+    public long getLongAttribute( DirContext ctx, String name ) {
+        return 0;
+    }
+
+    public String getStringAttribute( DirContext ctx, String name ) {
+        return null;
+    }
+
+    public boolean getBooleanAttribute( DirContext ctx, String name ) {
+        return false;
+    }
+}
+
diff --git a/connectors/naming/src/org/apache/naming/core/JndiPermission.java b/connectors/naming/src/org/apache/naming/core/JndiPermission.java
new file mode 100644
index 0000000..3fa5f59
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/core/JndiPermission.java
@@ -0,0 +1,60 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.core;
+
+import java.security.BasicPermission;
+
+/**
+ * Java SecurityManager Permission class for JNDI name based file resources
+ * <p>
+ * The JndiPermission extends the BasicPermission.
+ * The permission name is a full or partial jndi resource name.
+ * An * can be used at the end of the name to match all named
+ * resources that start with name.  There are no actions.</p>
+ * <p>
+ * Example that grants permission to read all JNDI file based resources:
+ * <li> permission org.apache.naming.JndiPermission "*";</li>
+ * </p>
+ *
+ * @author Glenn Nielsen
+ * @version $Revision$ $Date$
+ */
+
+public final class JndiPermission extends BasicPermission {
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Creates a new JndiPermission with no actions
+     *
+     * @param String - JNDI resource path name
+     */
+    public JndiPermission(String name) {
+        super(name);
+    }
+
+    /**
+     * Creates a new JndiPermission with actions
+     *
+     * @param String - JNDI resource path name
+     * @param String - JNDI actions (none defined)
+     */
+    public JndiPermission(String name, String actions) {
+        super(name,actions);
+    }
+
+}
diff --git a/connectors/naming/src/org/apache/naming/core/NameParserImpl.java b/connectors/naming/src/org/apache/naming/core/NameParserImpl.java
new file mode 100644
index 0000000..810611e
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/core/NameParserImpl.java
@@ -0,0 +1,74 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.core;
+
+import javax.naming.NameParser;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.CompositeName;
+
+/**
+ * Parses names.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+public class NameParserImpl 
+    implements NameParser
+{
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // ----------------------------------------------------- NameParser Methods
+
+
+    /**
+     * Parses a name into its components.
+     * 
+     * @param name The non-null string name to parse
+     * @return A non-null parsed form of the name using the naming convention 
+     * of this parser.
+     */
+    public Name parse(String name)
+        throws NamingException {
+        return new CompositeName(name);
+    }
+
+//     public Name parse( String name ) throws NamingException
+//     {
+//         return new CompoundName( name, syntax );
+//     }
+  
+//   public Properties getSyntax()
+//   {
+//     return syntax;
+//   }
+  
+//   static Properties syntax = new Properties();
+  
+//   static
+//   {
+//     syntax.put("jndi.syntax.direction", "left_to_right");
+//     syntax.put("jndi.syntax.separator", "/");
+//     syntax.put("jndi.syntax.ignorecase", "false");
+//   }
+
+
+}
+
diff --git a/connectors/naming/src/org/apache/naming/core/NamingContextEnumeration.java b/connectors/naming/src/org/apache/naming/core/NamingContextEnumeration.java
new file mode 100644
index 0000000..fb0baa9
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/core/NamingContextEnumeration.java
@@ -0,0 +1,128 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.core;
+
+
+import java.util.Enumeration;
+
+import javax.naming.Binding;
+import javax.naming.Context;
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+/**
+ * Naming enumeration implementation.
+ *
+ * TODO: implement 'throw exceptions on close' part of the spec.
+ * TODO: implement recycling ( for example on close )
+ *
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ */
+public class NamingContextEnumeration 
+    implements NamingEnumeration
+{
+    /** Constructor.
+     *
+     * @param enum base enumeration. Elements can be Strings, NameClassPair,
+     * Bindings or Entries, we'll provide the wrapping if needed. For String
+     * the Class and value will be lazy-loaded.
+     *
+     * @param ctx The context where this enum belongs. Used to lazy-eval
+     * the class and value
+     *
+     * @param bindings If true, we'll wrap things as Binding ( true for
+     * listBindings, false for list ).
+     */
+    public NamingContextEnumeration( Enumeration enum, Context ctx,
+                                     boolean bindings )
+    {
+        this.ctx = ctx;
+        this.bindings=bindings;
+        this.enum = enum;
+    }
+
+    // -------------------------------------------------------------- Variables
+
+    // return bindings instead of NameClassPair
+    protected boolean bindings;
+    /**
+     * Underlying enumeration.
+     */
+    protected Enumeration enum;
+
+    protected Context ctx;
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Retrieves the next element in the enumeration.
+     */
+    public Object next()
+        throws NamingException
+    {
+        return nextElement();
+    }
+
+
+    /**
+     * Determines whether there are any more elements in the enumeration.
+     */
+    public boolean hasMore()
+        throws NamingException
+    {
+        return enum.hasMoreElements();
+    }
+
+
+    /**
+     * Closes this enumeration.
+     */
+    public void close()
+        throws NamingException
+    {
+        // XXX all exceptions should be thrown on close ( AFAIK )
+    }
+
+
+    public boolean hasMoreElements() {
+        return enum.hasMoreElements();
+    }
+
+    public Object nextElement() {
+        Object next=enum.nextElement();
+        if( next instanceof NamingEntry ) {
+            NamingEntry entry = (NamingEntry) next;
+            return new ServerBinding(entry.name, ctx, true);
+        } else if( next instanceof NameClassPair ) {
+            NameClassPair ncp=(NameClassPair)next;
+            if( bindings )
+                return new ServerBinding(ncp.getName(), ctx, true);
+            return next;
+        } else if( next instanceof Binding ) {
+            return next;
+        } else if( next instanceof String ) {
+            String name=(String)next;
+            return new ServerBinding( name, ctx, true );
+        }
+        return null;
+    }
+
+}
+
diff --git a/connectors/naming/src/org/apache/naming/core/NamingEntry.java b/connectors/naming/src/org/apache/naming/core/NamingEntry.java
new file mode 100644
index 0000000..bc318c5
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/core/NamingEntry.java
@@ -0,0 +1,91 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.core;
+
+import javax.naming.directory.Attributes;
+
+/**
+ * Represents a binding in a NamingContext. All jtc contexts should
+ * use this class to represent entries.
+ *
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ */
+public class NamingEntry {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final int ENTRY = 0;
+    public static final int LINK_REF = 1;
+    public static final int REFERENCE = 2;
+    
+    public static final int CONTEXT = 10;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public NamingEntry(String name, Object value, Attributes atts, int type) {
+        this.name = name;
+        this.value = value;
+        this.type = type;
+        this.attributes=atts;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The type instance variable is used to avoid unsing RTTI when doing
+     * lookups.
+     */
+    public int type;
+    public String name;
+    public Object value;
+
+    public Attributes attributes;
+
+    // cached values
+    private boolean hasIntValue=false;
+    private boolean hasBoolValue=false;
+    private boolean hasLongValue=false;
+    
+    private int intValue;
+    private boolean boolValue;
+    private long longValue;
+
+    // --------------------------------------------------------- Object Methods
+
+    public void recycle() {
+    }
+
+    public boolean equals(Object obj) {
+        if ((obj != null) && (obj instanceof NamingEntry)) {
+            return name.equals(((NamingEntry) obj).name);
+        } else {
+            return false;
+        }
+    }
+
+    public int hashCode() {
+        return name.hashCode();
+    }
+
+}
diff --git a/connectors/naming/src/org/apache/naming/core/ServerAttribute.java b/connectors/naming/src/org/apache/naming/core/ServerAttribute.java
new file mode 100644
index 0000000..6dd0dae
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/core/ServerAttribute.java
@@ -0,0 +1,41 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.core;
+
+import javax.naming.directory.BasicAttribute;
+
+/**
+ * Extended version for Attribute. All our dirContexts should return objects
+ * of this type. Methods that take attribute param should use this type
+ * of objects for performance.
+ *
+ * This is an extension of the 'note' in tomcat 3.3. Each attribute will have an
+ * 'ID' ( small int ) and an associated namespace. The attributes are recyclable.
+ *
+ * The attribute is designed for use in server env, where performance is important.
+ *
+ * @author Costin Manolache
+ */
+public class ServerAttribute extends BasicAttribute
+{
+    public ServerAttribute(String id) {
+        super(id);
+    }
+    
+    public void recycle() {
+    }
+}
diff --git a/connectors/naming/src/org/apache/naming/core/ServerAttributes.java b/connectors/naming/src/org/apache/naming/core/ServerAttributes.java
new file mode 100644
index 0000000..415b899
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/core/ServerAttributes.java
@@ -0,0 +1,37 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.core;
+
+import javax.naming.directory.BasicAttributes;
+
+/**
+ * Extended version for Attribute. All our dirContexts should return objects
+ * of this type. Methods that take attribute param should use this type
+ * of objects for performance.
+ *
+ * This is an extension of the 'note' in tomcat 3.3. Each attribute will have an
+ * 'ID' ( small int ) and an associated namespace. The attributes are recyclable.
+ *
+ * The attribute is designed for use in server env, where performance is important.
+ *
+ * @author Costin Manolache
+ */
+public class ServerAttributes extends BasicAttributes
+{
+    
+    // no extra methods yet.     
+}
diff --git a/connectors/naming/src/org/apache/naming/core/ServerBinding.java b/connectors/naming/src/org/apache/naming/core/ServerBinding.java
new file mode 100644
index 0000000..59da50b
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/core/ServerBinding.java
@@ -0,0 +1,108 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.core;
+
+import javax.naming.Binding;
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+/** This is used for both NameClass and Binding.
+ * Lazy eval - so the extra methods in Binding don't affect us.
+ * For most contexts we'll deal getting the class name is as expensive
+ * as getting the object. In addition most operations will only use the name.
+ *
+ */
+public class ServerBinding extends Binding {
+    public ServerBinding( String name, Context ctx, boolean isRelative ) {
+        super( name, null );
+        this.ctx=ctx;
+        this.name = name;
+        this.isRel=isRelative;
+    }
+
+    public void recycle() {
+    }
+
+    private Context ctx;
+    private Object boundObj;
+    private String name;
+    private boolean isRel = true;
+    private String className;
+
+    private void lookup() {
+        try {
+            boundObj=ctx.lookup(name);
+        } catch( NamingException ex ) {
+            ex.printStackTrace();
+        }
+    }
+
+    public String getClassName() {
+        if( className!=null ) return className;
+        if( boundObj==null ) lookup();
+
+        if( boundObj!=null )
+            className=boundObj.getClass().getName();
+        return className;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setClassName(String name) {
+        this.className = name;
+    }
+
+    public boolean isRelative() {
+        return isRel;
+    }
+
+    public void setRelative(boolean r) {
+        isRel = r;
+    }
+
+    /**
+     * Generates the string representation of this name/class pair.
+     * The string representation consists of the name and class name separated
+     * by a colon (':').
+     * The contents of this string is useful
+     * for debugging and is not meant to be interpreted programmatically.
+     *
+     * @return The string representation of this name/class pair.
+     */
+    public String toString() {
+        return  (isRelative() ? "" : "(not relative)") + getName() + ": " +
+                getClassName();
+    }
+
+
+    public Object getObject() {
+        if( boundObj!=null )
+            return boundObj;
+        lookup();
+        return boundObj;
+    }
+
+    public void setObject(Object obj) {
+        boundObj = obj;
+    }
+}
diff --git a/connectors/naming/src/org/apache/naming/core/ServerName.java b/connectors/naming/src/org/apache/naming/core/ServerName.java
new file mode 100644
index 0000000..da443f6
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/core/ServerName.java
@@ -0,0 +1,76 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.core;
+
+import javax.naming.CompositeName;
+import javax.naming.InvalidNameException;
+import javax.naming.Name;
+
+/**
+ * Implementation of Name  with support for extra information.
+ *
+ * An extra feature ( not yet implemneted ) is the support for
+ * MessageBytes. This allows tomcat to operate lookup operations
+ * on the original message, without creating Strings.
+ *
+ * Another feature is support for extra information that can be cached.
+ * This brakes a bit the JNDI requirements, as Contexts can modify the
+ * Name and add anotations. The main benefit is that after the first
+ * lookup it'll be possible to avoid some expensive operations.
+ *
+ * @author Costin Manolache
+ */
+public class ServerName extends CompositeName
+{
+    private int id=0;
+
+    public ServerName( String s ) throws InvalidNameException {
+        super(s);
+    }
+
+    /** ID is a small int, equivalent with a note.
+        The name is reused in the server environment, and various
+        components can use the id for fast access.
+
+        The id is local for a particular context.
+
+        /Servlet/ClassName -> ClassName will have an ID ( note id ) unique
+                              for the Servlet 
+    */
+    public final int getId() {
+        return id;
+    }
+
+    // XXX Should we allow setting the id, or use a counter for each prefix ? 
+    public final void setId( int id ) {
+        this.id=id;
+    }
+
+    /**
+     * Factory method to create server names. 
+     */
+    public static Name getName( String s ) throws InvalidNameException {
+        return new ServerName( s );
+    }
+
+    /**
+     * @todo: optimize parsing and cache ( reuse existing instances ).
+     */
+//     public static Name getName( MessageBytes mb ) {
+//         return new ServerName( mb.toString() );
+//     }
+}
diff --git a/connectors/naming/src/org/apache/naming/handler/jndi/DirContextURLConnection.java b/connectors/naming/src/org/apache/naming/handler/jndi/DirContextURLConnection.java
new file mode 100644
index 0000000..485a438
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/handler/jndi/DirContextURLConnection.java
@@ -0,0 +1,408 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.handler.jndi;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.Permission;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+import org.apache.naming.core.JndiPermission;
+import org.apache.naming.util.AttributeHelper;
+// import org.apache.naming.resources.Resource;
+// import org.apache.naming.resources.ResourceAttributes;
+
+/**
+ * Connection to a JNDI directory context.
+ * <p/>
+ * Note: All the object attribute names are the WebDAV names, not the HTTP 
+ * names, so this class overrides some methods from URLConnection to do the
+ * queries using the right names. Content handler is also not used; the 
+ * content is directly returned.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author Costin Manolache
+ */
+public class DirContextURLConnection 
+    extends URLConnection
+{
+    
+    
+    // ----------------------------------------------------------- Constructors
+    
+    /**
+     * @param context The base context for the dynamic resources.
+     *        For regular webapps, it should be a thread-bound context, probably
+     *        a branch under java:
+     *        For top-level apps, it can be either the InitialContext or a branch
+     *        that is used for all content.
+     *
+     * The choice of the context affects the base of the URLs.
+     */
+    public DirContextURLConnection(DirContext context, URL url) {
+        super(url);
+        if (context == null)
+            throw new IllegalArgumentException
+                ("Directory context can't be null");
+        if (System.getSecurityManager() != null) {
+            this.permission = new JndiPermission(url.toString());
+	}
+        this.context = context;
+    }
+    
+    
+    // ----------------------------------------------------- Instance Variables
+    
+    
+    /**
+     * Directory context.
+     */
+    protected DirContext context;
+    
+    
+    /**
+     * Associated resource.
+     */
+    protected Object resource;
+    
+    
+    /**
+     * Associated DirContext.
+     */
+    protected DirContext collection;
+    
+    
+    /**
+     * Other unknown object.
+     */
+    protected Object object;
+    
+    
+    /**
+     * Attributes.
+     */
+    protected Attributes attributes;
+    
+    
+    /**
+     * Date.
+     */
+    protected long date;
+    
+    
+    /**
+     * Permission
+     */
+    protected Permission permission;
+
+
+    // ------------------------------------------------------------- Properties
+    
+    
+    /**
+     * Connect to the DirContext, and retrive the bound object, as well as
+     * its attributes. If no object is bound with the name specified in the
+     * URL, then an IOException is thrown.
+     * 
+     * @throws IOException Object not found
+     */
+    public void connect()
+        throws IOException {
+        
+        if (!connected) {
+            
+            try {
+                // TODO: What is this ???  (costin)
+                date = System.currentTimeMillis();
+                String path = getURL().getFile();
+
+                /* This deals with a strange case, where the
+                   name is prefixed by hostname and contextname.
+
+                   A webapp should never use this - all resources
+                   are local ( or a different mean should be used to
+                   locate external res ).
+
+                   The top-level handler must use the hostname + context to
+                   locate files in a particular context.
+                   
+                  if (context instanceof ProxyDirContext) {
+                    ProxyDirContext proxyDirContext = 
+                        (ProxyDirContext) context;
+                    String hostName = proxyDirContext.getHostName();
+                    String contextName = proxyDirContext.getContextName();
+                    if (hostName != null) {
+                        if (!path.startsWith("/" + hostName + "/"))
+                            return;
+                        path = path.substring(hostName.length()+ 1);
+                    }
+                    if (contextName != null) {
+                        if (!path.startsWith(contextName + "/")) {
+                            return;
+                        } else {
+                            path = path.substring(contextName.length());
+                        }
+                    }
+                }
+                */
+                object = context.lookup(path);
+                attributes = context.getAttributes(path);
+                //                if (object instanceof Resource)
+                //  resource = (Resource) object;
+                if (object instanceof DirContext)
+                    collection = (DirContext) object;
+                else
+                    resource=object;
+            } catch (NamingException e) {
+                // Object not found
+            }
+            
+            connected = true;
+            
+        }
+        
+    }
+    
+    
+    /**
+     * Return the content length value.
+     */
+    public int getContentLength() {
+        if (!connected) {
+            // Try to connect (silently)
+            try {
+                connect();
+            } catch (IOException e) {
+            }
+        }
+        
+        if (attributes == null)
+            return (-1);
+
+        return (int)AttributeHelper.getContentLength( attributes );
+    }
+
+    /**
+     * Return the content type value.
+     */
+    public String getContentType() {
+        if (!connected) {
+            // Try to connect (silently)
+            try {
+                connect();
+            } catch (IOException e) {
+            }
+        }
+        
+        if (attributes == null)
+            return null;
+        
+        return AttributeHelper.getContentType(attributes);
+    }
+    
+    
+    /**
+     * Return the last modified date.
+     * TODO: it's broken
+     */
+    public long getDate() {
+        return date;
+    }
+    
+    
+    /**
+     * Return the last modified date.
+     */
+    public long getLastModified() {
+
+        if (!connected) {
+            // Try to connect (silently)
+            try {
+                connect();
+            } catch (IOException e) {
+            }
+        }
+
+        if (attributes == null)
+            return 0;
+
+        return AttributeHelper.getLastModified( attributes );
+    }
+    
+    
+    /**
+     * Returns the name of the specified header field.
+     */
+    public String getHeaderField(String name) {
+
+        if (!connected) {
+            // Try to connect (silently)
+            try {
+                connect();
+            } catch (IOException e) {
+            }
+        }
+        
+        if (attributes == null)
+            return (null);
+
+        Attribute attribute = attributes.get(name);
+        try {
+            return attribute.get().toString();
+        } catch (Exception e) {
+            // Shouldn't happen, unless the attribute has no value
+        }
+
+        return (null);
+        
+    }
+    
+    
+    /**
+     * Get object content.
+     */
+    public Object getContent()
+        throws IOException {
+        
+        if (!connected)
+            connect();
+        
+        if (resource != null)
+            return getInputStream();
+        if (collection != null)
+            return collection;
+        if (object != null)
+            return object;
+        
+        throw new FileNotFoundException();
+        
+    }
+    
+    
+    /**
+     * Get object content.
+     */
+    public Object getContent(Class[] classes)
+        throws IOException {
+        
+        Object object = getContent();
+        
+        for (int i = 0; i < classes.length; i++) {
+            if (classes[i].isInstance(object))
+                return object;
+        }
+        
+        return null;
+        
+    }
+    
+    
+    /**
+     * Get input stream.
+     */
+    public InputStream getInputStream() 
+        throws IOException {
+        
+        if (!connected)
+            connect();
+        
+        if (resource == null) {
+            throw new FileNotFoundException();
+        } else {
+            // Reopen resource
+            try {
+                resource =  context.lookup(getURL().getFile());
+            } catch (NamingException e) {
+            }
+        }
+        
+        //        return (resource.streamContent());
+        return getInputStream(resource);
+        
+    }
+
+    /** Try to extract content from a resource found in the directory
+     *  Code from Resource and ProxyContext.
+     */
+    public static InputStream getInputStream( Object resource ) {
+        if( resource instanceof InputStream )
+            return (InputStream) resource;
+
+        // Found in: ProxyDirContext.lookup ( strange, only in one ).
+        return new ByteArrayInputStream(resource.toString().getBytes());
+    }
+    
+    /**
+     * Get the Permission for this URL
+     */
+    public Permission getPermission() {
+
+        return permission;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+    
+    
+    /**
+     * List children of this collection. The names given are relative to this
+     * URI's path. The full uri of the children is then : path + "/" + name.
+     */
+    public Enumeration list()
+        throws IOException {
+        
+        if (!connected) {
+            connect();
+        }
+        
+        if ((resource == null) && (collection == null)) {
+            throw new FileNotFoundException();
+        }
+        
+        Vector result = new Vector();
+        
+        if (collection != null) {
+            try {
+                NamingEnumeration enum = context.list(getURL().getFile());
+                while (enum.hasMoreElements()) {
+                    NameClassPair ncp = (NameClassPair) enum.nextElement();
+                    result.addElement(ncp.getName());
+                }
+            } catch (NamingException e) {
+                // Unexpected exception
+                throw new FileNotFoundException();
+            }
+        }
+        
+        return result.elements();
+        
+    }
+    
+    
+}
diff --git a/connectors/naming/src/org/apache/naming/handler/jndi/DirContextURLStreamHandler.java b/connectors/naming/src/org/apache/naming/handler/jndi/DirContextURLStreamHandler.java
new file mode 100644
index 0000000..b771a4c
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/handler/jndi/DirContextURLStreamHandler.java
@@ -0,0 +1,230 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.handler.jndi;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.util.Hashtable;
+
+import javax.naming.directory.DirContext;
+
+/**
+ * Stream handler to a JNDI directory context.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @version $Revision$
+ */
+public class DirContextURLStreamHandler 
+    extends URLStreamHandler {
+    
+    
+    // ----------------------------------------------------------- Constructors
+    
+    
+    public DirContextURLStreamHandler() {
+    }
+    
+    
+    public DirContextURLStreamHandler(DirContext context) {
+        this.context = context;
+    }
+    
+    
+    // -------------------------------------------------------------- Variables
+    
+    
+    /**
+     * Bindings class loader - directory context. Keyed by CL id.
+     */
+    private static Hashtable clBindings = new Hashtable();
+    
+    
+    /**
+     * Bindings thread - directory context. Keyed by thread id.
+     */
+    private static Hashtable threadBindings = new Hashtable();
+    
+    
+    // ----------------------------------------------------- Instance Variables
+    
+    
+    /**
+     * Directory context.
+     */
+    protected DirContext context = null;
+    
+    
+    // ------------------------------------------------------------- Properties
+    
+    
+    // ----------------------------------------------- URLStreamHandler Methods
+    
+    
+    /**
+     * Opens a connection to the object referenced by the <code>URL</code> 
+     * argument.
+     */
+    protected URLConnection openConnection(URL u) 
+        throws IOException {
+        DirContext currentContext = this.context;
+        if (currentContext == null)
+            currentContext = get();
+        return new DirContextURLConnection(currentContext, u);
+    }
+    
+    
+    // --------------------------------------------------------- Public Methods
+    public static final String PROTOCOL_HANDLER_VARIABLE = 
+        "java.protocol.handler.pkgs";
+
+    public static final String PACKAGE = "org.apache.naming.handler.jndi";
+
+    
+    /**
+     * Set the java.protocol.handler.pkgs system property.
+     */
+    public static void setProtocolHandler() {
+        String value = System.getProperty(PROTOCOL_HANDLER_VARIABLE);
+        if (value == null) {
+            value = PACKAGE;
+            System.setProperty(PROTOCOL_HANDLER_VARIABLE, value);
+        } else if (value.indexOf( PACKAGE ) == -1) {
+            value += "|" + PACKAGE;
+            System.setProperty(PROTOCOL_HANDLER_VARIABLE, value);
+        }
+    }
+    
+    
+    /**
+     * Returns true if the thread or the context class loader of the current 
+     * thread is bound.
+     */
+    public static boolean isBound() {
+        return (clBindings.containsKey
+                (Thread.currentThread().getContextClassLoader()))
+            || (threadBindings.containsKey(Thread.currentThread()));
+    }
+    
+    
+    /**
+     * Binds a directory context to a class loader.
+     */
+    public static void bind(DirContext dirContext) {
+        ClassLoader currentCL = 
+            Thread.currentThread().getContextClassLoader();
+        if (currentCL != null)
+            clBindings.put(currentCL, dirContext);
+    }
+    
+    
+    /**
+     * Unbinds a directory context to a class loader.
+     */
+    public static void unbind() {
+        ClassLoader currentCL = 
+            Thread.currentThread().getContextClassLoader();
+        if (currentCL != null)
+            clBindings.remove(currentCL);
+    }
+    
+    
+    /**
+     * Binds a directory context to a thread.
+     */
+    public static void bindThread(DirContext dirContext) {
+        threadBindings.put(Thread.currentThread(), dirContext);
+    }
+    
+    
+    /**
+     * Unbinds a directory context to a thread.
+     */
+    public static void unbindThread() {
+        threadBindings.remove(Thread.currentThread());
+    }
+    
+    
+    /**
+     * Get the bound context.
+     */
+    public static DirContext get() {
+
+        DirContext result = null;
+
+        Thread currentThread = Thread.currentThread();
+        ClassLoader currentCL = currentThread.getContextClassLoader();
+
+        // Checking CL binding
+        result = (DirContext) clBindings.get(currentCL);
+        if (result != null)
+            return result;
+
+        // Checking thread biding
+        result = (DirContext) threadBindings.get(currentThread);
+
+        // Checking parent CL binding
+        currentCL = currentCL.getParent();
+        while (currentCL != null) {
+            result = (DirContext) clBindings.get(currentCL);
+            if (result != null)
+                return result;
+            currentCL = currentCL.getParent();
+        }
+
+        if (result == null)
+            throw new IllegalStateException("Illegal class loader binding");
+
+        return result;
+
+    }
+    
+    
+    /**
+     * Binds a directory context to a class loader.
+     */
+    public static void bind(ClassLoader cl, DirContext dirContext) {
+        clBindings.put(cl, dirContext);
+    }
+    
+    
+    /**
+     * Unbinds a directory context to a class loader.
+     */
+    public static void unbind(ClassLoader cl) {
+        clBindings.remove(cl);
+    }
+    
+    
+    /**
+     * Get the bound context.
+     */
+    public static DirContext get(ClassLoader cl) {
+        return (DirContext) clBindings.get(cl);
+    }
+    
+    
+    /**
+     * Get the bound context.
+     */
+    public static DirContext get(Thread thread) {
+        return (DirContext) threadBindings.get(thread);
+    }
+    
+    
+}
diff --git a/connectors/naming/src/org/apache/naming/handler/jndi/DirContextURLStreamHandlerFactory.java b/connectors/naming/src/org/apache/naming/handler/jndi/DirContextURLStreamHandlerFactory.java
new file mode 100644
index 0000000..31adc12
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/handler/jndi/DirContextURLStreamHandlerFactory.java
@@ -0,0 +1,65 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.handler.jndi;
+
+import java.net.URLStreamHandler;
+import java.net.URLStreamHandlerFactory;
+
+/**
+ * Factory for Stream handlers to a JNDI directory context.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @version $Revision$
+ */
+public class DirContextURLStreamHandlerFactory 
+    implements URLStreamHandlerFactory {
+    
+    
+    // ----------------------------------------------------------- Constructors
+    
+    
+    public DirContextURLStreamHandlerFactory() {
+    }
+    
+    
+    // ----------------------------------------------------- Instance Variables
+    
+    
+    // ------------------------------------------------------------- Properties
+    
+    
+    // ---------------------------------------- URLStreamHandlerFactory Methods
+    
+    
+    /**
+     * Creates a new URLStreamHandler instance with the specified protocol.
+     * Will return null if the protocol is not <code>jndi</code>.
+     * 
+     * @param protocol the protocol (must be "jndi" here)
+     * @return a URLStreamHandler for the jndi protocol, or null if the 
+     * protocol is not JNDI
+     */
+    public URLStreamHandler createURLStreamHandler(String protocol) {
+        if (protocol.equals("jndi")) {
+            return new DirContextURLStreamHandler();
+        } else {
+            return null;
+        }
+    }
+    
+    
+}
diff --git a/connectors/naming/src/org/apache/naming/handler/jndi/Handler.java b/connectors/naming/src/org/apache/naming/handler/jndi/Handler.java
new file mode 100644
index 0000000..888e197
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/handler/jndi/Handler.java
@@ -0,0 +1,39 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.handler.jndi;
+
+
+//import org.apache.naming.core.DirContextURLStreamHandler;
+
+/**
+ * Stream handler to a JNDI directory context.
+ * 
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @version $Revision$
+ */
+public class Handler 
+    extends DirContextURLStreamHandler {
+    
+    
+    // ----------------------------------------------------------- Constructors
+    
+    
+    public Handler() {
+    }
+    
+    
+}
diff --git a/connectors/naming/src/org/apache/naming/handler/jndi/package.html b/connectors/naming/src/org/apache/naming/handler/jndi/package.html
new file mode 100644
index 0000000..e50d969
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/handler/jndi/package.html
@@ -0,0 +1,20 @@
+<h3>Handler</h3>
+
+Objects stored in the directory may have content - and act as a VFS. 
+
+This package deals with accessing files in the directory using URLs.
+
+There are 2 cases:
+
+1. Generic access via InitialContext. We will use a naming structure 
+under the InitialContext to locate the file.
+  
+   jndi:/my/path
+
+will be translated to a lookup for '/my/path'. 
+
+2. The old behavior: the thread is specifically bound to a 
+ProxyContext, then this is used to locate files in the particular
+webapp. We'll emulate this by treating jndi: as a reference
+to a particular branch in the java: webpp resource.
+
diff --git a/connectors/naming/src/org/apache/naming/handler/package.html b/connectors/naming/src/org/apache/naming/handler/package.html
new file mode 100644
index 0000000..38bba6d
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/handler/package.html
@@ -0,0 +1,3 @@
+<h2>URL handlers<h2>
+
+This package contains URL handlers that allow access to jndi resources.
\ No newline at end of file
diff --git a/connectors/naming/src/org/apache/naming/modules/cache/ProxyDirContext.java b/connectors/naming/src/org/apache/naming/modules/cache/ProxyDirContext.java
new file mode 100644
index 0000000..c1fd835
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/modules/cache/ProxyDirContext.java
@@ -0,0 +1,1508 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.modules.cache;
+
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.Map;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NameParser;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+
+import org.apache.commons.collections.LRUMap;
+import org.apache.naming.core.BaseDirContext;
+import org.apache.naming.util.AttributeHelper;
+import org.apache.tomcat.util.res.StringManager;
+
+/* Changes( costin ): The goal is to make it a generic JNDI cache, not specific
+   to file system. 
+
+  - no more wrapping in Resource and ResourceAttributes.
+   We just cache, and the caller can use tools to do the wrapping or operate on atts.
+  - we use only lastModified ( not contentLength ).
+  -
+
+  TODO:
+  - add a special CacheInputStream - that will save the byte[] in the cache entry.
+  - 2 TTL: one will prevent accessing the dir ( even for lastModified ), one will
+    expire the entry regardless. The first should be very short ( .1 sec ? ), for
+    to avoid very frequent accesses to the same entry.
+    Alternative ( probably the best ): use a background thread to check 'lastModified',
+    like we do in 3.3 for class reloading.
+*/
+
+
+
+/**
+ * Proxy Directory Context implementation.
+ *
+ * Will cache directory entries - attributes and content. This can be used
+ * to eliminate expensive dir access and to avoid keeping large directories in memory.
+ *
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ */
+public class ProxyDirContext implements DirContext {
+      private static org.apache.commons.logging.Log log=
+         org.apache.commons.logging.LogFactory.getLog( ProxyDirContext.class );
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Builds a proxy directory context using the given environment.
+     */
+    public ProxyDirContext(Hashtable env, DirContext dirContext) {
+        this.env = env;
+        this.dirContext = dirContext;
+        if (dirContext instanceof BaseDirContext) {
+            // Initialize parameters based on the associated dir context, like
+            // the caching policy.
+            if (((BaseDirContext) dirContext).isCached()) {
+                cache = Collections.synchronizedMap(new LRUMap(cacheSize));
+                cacheTTL = ((BaseDirContext) dirContext).getCacheTTL();
+                cacheObjectMaxSize = 
+                    ((BaseDirContext) dirContext).getCacheObjectMaxSize();
+            }
+        }
+    }
+
+    /**
+     * Builds a clone of this proxy dir context, wrapping the given directory
+     * context, and sharing the same cache.
+     */
+    protected ProxyDirContext(ProxyDirContext proxyDirContext, 
+                              DirContext dirContext) {
+        this.env = proxyDirContext.env;
+        this.dirContext = dirContext;
+        this.cache = proxyDirContext.cache;
+        this.cacheSize = proxyDirContext.cacheSize;
+        this.cacheTTL = proxyDirContext.cacheTTL;
+        this.cacheObjectMaxSize = proxyDirContext.cacheObjectMaxSize;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Environment.
+     */
+    protected Hashtable env;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm = StringManager.getManager("org.apache.naming.res");
+
+
+    /**
+     * Associated DirContext.
+     */
+    protected DirContext dirContext;
+
+
+    /**
+     * Cache.
+     * Path -> Cache entry.
+     */
+    protected Map cache = null;
+
+
+    /**
+     * Cache size
+     */
+    protected int cacheSize = 1000;
+
+
+    /**
+     * Cache TTL.
+     */
+    protected int cacheTTL = 5000; // 5s
+
+
+    /**
+     * Max size of resources which will have their content cached.
+     */
+    protected int cacheObjectMaxSize = 32768; // 32 KB
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the actual directory context we are wrapping.
+     */
+    public DirContext getDirContext() {
+        return this.dirContext;
+    }
+
+
+    // -------------------------------------------------------- Context Methods
+
+
+    /**
+     * Retrieves the named object. If name is empty, returns a new instance 
+     * of this context (which represents the same naming context as this 
+     * context, but its environment may be modified independently and it may 
+     * be accessed concurrently).
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookup(Name name)
+        throws NamingException {
+        CacheEntry entry = cacheLookupAndLoad(name.toString());
+        if (entry != null) {
+            if (entry.resource != null) {
+                // Check content caching.
+                
+                return entry.resource;
+            } else {
+                return entry.context;
+            }
+        }
+        log.info("Strange, entry was no loadeded " + name );
+        Object object = dirContext.lookup(parseName(name));
+//         if (object instanceof InputStream)
+//             return new Resource((InputStream) object);
+//         else
+            return object;
+    }
+
+
+    /**
+     * Retrieves the named object.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookup(String name)
+        throws NamingException {
+        CacheEntry entry = cacheLookupAndLoad(name);
+        if (entry != null) {
+            if (entry.resource != null) {
+                return entry.resource;
+            } else {
+                return entry.context;
+            }
+        } 
+        log.info("Strange, entry was no loadeded " + name );
+       
+        Object object = dirContext.lookup(parseName(name));
+//         if (object instanceof InputStream) {
+//             return new Resource((InputStream) object);
+//         } else if (object instanceof DirContext) {
+//             return object;
+//         } else if (object instanceof Resource) {
+//             return object;
+//         } else {
+//             return new Resource(new ByteArrayInputStream
+//                 (object.toString().getBytes()));
+//         }
+        return object;
+    }
+
+
+    /**
+     * Binds a name to an object. All intermediate contexts and the target 
+     * context (that named by all but terminal atomic component of the name) 
+     * must already exist.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(Name name, Object obj)
+        throws NamingException {
+        dirContext.bind(parseName(name), obj);
+        cacheUnload(name.toString());
+    }
+
+
+    /**
+     * Binds a name to an object.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(String name, Object obj)
+        throws NamingException {
+        dirContext.bind(parseName(name), obj);
+        cacheUnload(name);
+    }
+
+
+    /**
+     * Binds a name to an object, overwriting any existing binding. All 
+     * intermediate contexts and the target context (that named by all but 
+     * terminal atomic component of the name) must already exist.
+     * <p>
+     * If the object is a DirContext, any existing attributes associated with 
+     * the name are replaced with those of the object. Otherwise, any 
+     * existing attributes associated with the name remain unchanged.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(Name name, Object obj)
+        throws NamingException {
+        dirContext.rebind(parseName(name), obj);
+        cacheUnload(name.toString());
+    }
+
+
+    /**
+     * Binds a name to an object, overwriting any existing binding.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(String name, Object obj)
+        throws NamingException {
+        dirContext.rebind(parseName(name), obj);
+        cacheUnload(name);
+    }
+
+
+    /**
+     * Unbinds the named object. Removes the terminal atomic name in name 
+     * from the target context--that named by all but the terminal atomic 
+     * part of name.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void unbind(Name name)
+        throws NamingException {
+        dirContext.unbind(parseName(name));
+        cacheUnload(name.toString());
+    }
+
+
+    /**
+     * Unbinds the named object.
+     * 
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void unbind(String name)
+        throws NamingException {
+        dirContext.unbind(parseName(name));
+        cacheUnload(name);
+    }
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the 
+     * old name. Both names are relative to this context. Any attributes 
+     * associated with the old name become associated with the new name. 
+     * Intermediate contexts of the old name are not changed.
+     * 
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception NameAlreadyBoundException if newName is already bound
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rename(Name oldName, Name newName)
+        throws NamingException {
+        dirContext.rename(parseName(oldName), parseName(newName));
+        cacheUnload(oldName.toString());
+    }
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the 
+     * old name.
+     * 
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception NameAlreadyBoundException if newName is already bound
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rename(String oldName, String newName)
+        throws NamingException {
+        dirContext.rename(parseName(oldName), parseName(newName));
+        cacheUnload(oldName);
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them. The contents of any subcontexts are 
+     * not included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(Name name)
+        throws NamingException {
+        return dirContext.list(parseName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(String name)
+        throws NamingException {
+        return dirContext.list(parseName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them. The contents of any subcontexts are not 
+     * included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(Name name)
+        throws NamingException {
+        return dirContext.listBindings(parseName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(String name)
+        throws NamingException {
+        return dirContext.listBindings(parseName(name));
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace. Any 
+     * attributes associated with the name are also removed. Intermediate 
+     * contexts are not destroyed.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * In a federated naming system, a context from one naming system may be 
+     * bound to a name in another. One can subsequently look up and perform 
+     * operations on the foreign context using a composite name. However, an 
+     * attempt destroy the context using this composite name will fail with 
+     * NotContextException, because the foreign context is not a "subcontext" 
+     * of the context in which it is bound. Instead, use unbind() to remove 
+     * the binding of the foreign context. Destroying the foreign context 
+     * requires that the destroySubcontext() be performed on a context from 
+     * the foreign context's "native" naming system.
+     * 
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NotContextException if the name is bound but does not name 
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(Name name)
+        throws NamingException {
+        dirContext.destroySubcontext(parseName(name));
+        cacheUnload(name.toString());
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace.
+     * 
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NotContextException if the name is bound but does not name 
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(String name)
+        throws NamingException {
+        dirContext.destroySubcontext(parseName(name));
+        cacheUnload(name);
+    }
+
+
+    /**
+     * Creates and binds a new context. Creates a new context with the given 
+     * name and binds it in the target context (that named by all but 
+     * terminal atomic component of the name). All intermediate contexts and 
+     * the target context must already exist.
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if creation of the subcontext 
+     * requires specification of mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Context createSubcontext(Name name)
+        throws NamingException {
+        return dirContext.createSubcontext(parseName(name));
+    }
+
+
+    /**
+     * Creates and binds a new context.
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if creation of the subcontext 
+     * requires specification of mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Context createSubcontext(String name)
+        throws NamingException {
+        return dirContext.createSubcontext(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the named object, following links except for the terminal 
+     * atomic component of the name. If the object bound to name is not a 
+     * link, returns the object itself.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link 
+     * (if any).
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookupLink(Name name)
+        throws NamingException {
+        return dirContext.lookupLink(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the named object, following links except for the terminal 
+     * atomic component of the name.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link 
+     * (if any).
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookupLink(String name)
+        throws NamingException {
+        return dirContext.lookupLink(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the parser associated with the named context. In a 
+     * federation of namespaces, different naming systems will parse names 
+     * differently. This method allows an application to get a parser for 
+     * parsing names into their atomic components using the naming convention 
+     * of a particular naming system. Within any single naming system, 
+     * NameParser objects returned by this method must be equal (using the 
+     * equals() test).
+     * 
+     * @param name the name of the context from which to get the parser
+     * @return a name parser that can parse compound names into their atomic 
+     * components
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NameParser getNameParser(Name name)
+        throws NamingException {
+        return dirContext.getNameParser(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the parser associated with the named context.
+     * 
+     * @param name the name of the context from which to get the parser
+     * @return a name parser that can parse compound names into their atomic 
+     * components
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NameParser getNameParser(String name)
+        throws NamingException {
+        return dirContext.getNameParser(parseName(name));
+    }
+
+
+    /**
+     * Composes the name of this context with a name relative to this context.
+     * <p>
+     * Given a name (name) relative to this context, and the name (prefix) 
+     * of this context relative to one of its ancestors, this method returns 
+     * the composition of the two names using the syntax appropriate for the 
+     * naming system(s) involved. That is, if name names an object relative 
+     * to this context, the result is the name of the same object, but 
+     * relative to the ancestor context. None of the names may be null.
+     * 
+     * @param name a name relative to this context
+     * @param prefix the name of this context relative to one of its ancestors
+     * @return the composition of prefix and name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Name composeName(Name name, Name prefix)
+        throws NamingException {
+        prefix = (Name) name.clone();
+	return prefix.addAll(name);
+    }
+
+
+    /**
+     * Composes the name of this context with a name relative to this context.
+     * 
+     * @param name a name relative to this context
+     * @param prefix the name of this context relative to one of its ancestors
+     * @return the composition of prefix and name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public String composeName(String name, String prefix)
+        throws NamingException {
+        return prefix + "/" + name;
+    }
+
+
+    /**
+     * Adds a new environment property to the environment of this context. If 
+     * the property already exists, its value is overwritten.
+     * 
+     * @param propName the name of the environment property to add; may not 
+     * be null
+     * @param propVal the value of the property to add; may not be null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object addToEnvironment(String propName, Object propVal)
+        throws NamingException {
+        return dirContext.addToEnvironment(propName, propVal);
+    }
+
+
+    /**
+     * Removes an environment property from the environment of this context. 
+     * 
+     * @param propName the name of the environment property to remove; 
+     * may not be null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object removeFromEnvironment(String propName)
+        throws NamingException {
+        return dirContext.removeFromEnvironment(propName);
+    }
+
+
+    /**
+     * Retrieves the environment in effect for this context. See class 
+     * description for more details on environment properties. 
+     * The caller should not make any changes to the object returned: their 
+     * effect on the context is undefined. The environment of this context 
+     * may be changed using addToEnvironment() and removeFromEnvironment().
+     * 
+     * @return the environment of this context; never null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Hashtable getEnvironment()
+        throws NamingException {
+        return dirContext.getEnvironment();
+    }
+
+
+    /**
+     * Closes this context. This method releases this context's resources 
+     * immediately, instead of waiting for them to be released automatically 
+     * by the garbage collector.
+     * This method is idempotent: invoking it on a context that has already 
+     * been closed has no effect. Invoking any other method on a closed 
+     * context is not allowed, and results in undefined behaviour.
+     * 
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void close()
+        throws NamingException {
+        dirContext.close();
+    }
+
+
+    /**
+     * Retrieves the full name of this context within its own namespace.
+     * <p>
+     * Many naming services have a notion of a "full name" for objects in 
+     * their respective namespaces. For example, an LDAP entry has a 
+     * distinguished name, and a DNS record has a fully qualified name. This 
+     * method allows the client application to retrieve this name. The string 
+     * returned by this method is not a JNDI composite name and should not be 
+     * passed directly to context methods. In naming systems for which the 
+     * notion of full name does not make sense, 
+     * OperationNotSupportedException is thrown.
+     * 
+     * @return this context's name in its own namespace; never null
+     * @exception OperationNotSupportedException if the naming system does 
+     * not have the notion of a full name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public String getNameInNamespace()
+        throws NamingException {
+        return dirContext.getNameInNamespace();
+    }
+
+
+    // ----------------------------------------------------- DirContext Methods
+
+
+    /**
+     * Retrieves all of the attributes associated with a named object. 
+     * 
+     * @return the set of attributes associated with name. 
+     * Returns an empty attribute set if name has no attributes; never null.
+     * @param name the name of the object from which to retrieve attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(Name name)
+        throws NamingException {
+        CacheEntry entry = cacheLookupAndLoad(name.toString());
+        if (entry != null) {
+            return entry.attributes;
+        }
+        Attributes attributes = dirContext.getAttributes(parseName(name));
+
+        // TODO(costin): Why do we need to wrap it ? It may be better to use decorator
+        //         if (!(attributes instanceof ResourceAttributes)) {
+        //             attributes = new ResourceAttributes(attributes);
+        //         }
+        return attributes;
+    }
+
+
+    /**
+     * Retrieves all of the attributes associated with a named object.
+     * 
+     * @return the set of attributes associated with name
+     * @param name the name of the object from which to retrieve attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(String name)
+        throws NamingException {
+        CacheEntry entry = cacheLookupAndLoad(name);
+        if (entry != null) {
+            return entry.attributes;
+        }
+        Attributes attributes = dirContext.getAttributes(parseName(name));
+//         if (!(attributes instanceof ResourceAttributes)) {
+//             attributes = new ResourceAttributes(attributes);
+//         }
+        return attributes;
+    }
+
+
+    /**
+     * Retrieves selected attributes associated with a named object. 
+     * See the class description regarding attribute models, attribute type 
+     * names, and operational attributes.
+     * 
+     * @return the requested attributes; never null
+     * @param name the name of the object from which to retrieve attributes
+     * @param attrIds the identifiers of the attributes to retrieve. null 
+     * indicates that all attributes should be retrieved; an empty array 
+     * indicates that none should be retrieved
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(Name name, String[] attrIds)
+        throws NamingException {
+        Attributes attributes = 
+            dirContext.getAttributes(parseName(name), attrIds);
+//         if (!(attributes instanceof ResourceAttributes)) {
+//             attributes = new ResourceAttributes(attributes);
+//         }
+        return attributes;
+    }
+
+
+    /**
+     * Retrieves selected attributes associated with a named object.
+     * 
+     * @return the requested attributes; never null
+     * @param name the name of the object from which to retrieve attributes
+     * @param attrIds the identifiers of the attributes to retrieve. null 
+     * indicates that all attributes should be retrieved; an empty array 
+     * indicates that none should be retrieved
+     * @exception NamingException if a naming exception is encountered
+     */
+     public Attributes getAttributes(String name, String[] attrIds)
+         throws NamingException {
+        Attributes attributes = 
+            dirContext.getAttributes(parseName(name), attrIds);
+//         if (!(attributes instanceof ResourceAttributes)) {
+//             attributes = new ResourceAttributes(attributes);
+//         }
+        return attributes;
+     }
+
+
+    /**
+     * Modifies the attributes associated with a named object. The order of 
+     * the modifications is not specified. Where possible, the modifications 
+     * are performed atomically.
+     * 
+     * @param name the name of the object whose attributes will be updated
+     * @param mod_op the modification operation, one of: ADD_ATTRIBUTE, 
+     * REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE
+     * @param attrs the attributes to be used for the modification; may not 
+     * be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(Name name, int mod_op, Attributes attrs)
+        throws NamingException {
+        dirContext.modifyAttributes(parseName(name), mod_op, attrs);
+    }
+
+
+    /**
+     * Modifies the attributes associated with a named object.
+     * 
+     * @param name the name of the object whose attributes will be updated
+     * @param mod_op the modification operation, one of: ADD_ATTRIBUTE, 
+     * REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE
+     * @param attrs the attributes to be used for the modification; may not 
+     * be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(String name, int mod_op, Attributes attrs)
+        throws NamingException {
+        dirContext.modifyAttributes(parseName(name), mod_op, attrs);
+    }
+
+
+    /**
+     * Modifies the attributes associated with a named object using an an 
+     * ordered list of modifications. The modifications are performed in the 
+     * order specified. Each modification specifies a modification operation 
+     * code and an attribute on which to operate. Where possible, the 
+     * modifications are performed atomically.
+     * 
+     * @param name the name of the object whose attributes will be updated
+     * @param mods an ordered sequence of modifications to be performed; may 
+     * not be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(Name name, ModificationItem[] mods)
+        throws NamingException {
+        dirContext.modifyAttributes(parseName(name), mods);
+    }
+
+
+    /**
+     * Modifies the attributes associated with a named object using an an 
+     * ordered list of modifications.
+     * 
+     * @param name the name of the object whose attributes will be updated
+     * @param mods an ordered sequence of modifications to be performed; may 
+     * not be null
+     * @exception AttributeModificationException if the modification cannot be
+     * completed successfully
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void modifyAttributes(String name, ModificationItem[] mods)
+        throws NamingException {
+        dirContext.modifyAttributes(parseName(name), mods);
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes. If attrs 
+     * is null, the resulting binding will have the attributes associated 
+     * with obj if obj is a DirContext, and no attributes otherwise. If attrs 
+     * is non-null, the resulting binding will have attrs as its attributes; 
+     * any attributes associated with obj are ignored.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(Name name, Object obj, Attributes attrs)
+        throws NamingException {
+        dirContext.bind(parseName(name), obj, attrs);
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(String name, Object obj, Attributes attrs)
+        throws NamingException {
+        dirContext.bind(parseName(name), obj, attrs);
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes, 
+     * overwriting any existing binding. If attrs is null and obj is a 
+     * DirContext, the attributes from obj are used. If attrs is null and obj 
+     * is not a DirContext, any existing attributes associated with the object
+     * already bound in the directory remain unchanged. If attrs is non-null, 
+     * any existing attributes associated with the object already bound in 
+     * the directory are removed and attrs is associated with the named 
+     * object. If obj is a DirContext and attrs is non-null, the attributes 
+     * of obj are ignored.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(Name name, Object obj, Attributes attrs)
+        throws NamingException {
+        dirContext.rebind(parseName(name), obj, attrs);
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes, 
+     * overwriting any existing binding.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(String name, Object obj, Attributes attrs)
+        throws NamingException {
+        dirContext.rebind(parseName(name), obj, attrs);
+    }
+
+
+    /**
+     * Creates and binds a new context, along with associated attributes. 
+     * This method creates a new subcontext with the given name, binds it in 
+     * the target context (that named by all but terminal atomic component of 
+     * the name), and associates the supplied attributes with the newly 
+     * created object. All intermediate and target contexts must already 
+     * exist. If attrs is null, this method is equivalent to 
+     * Context.createSubcontext().
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @param attrs the attributes to associate with the newly created context
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if the name is already bound
+     * @exception InvalidAttributesException if attrs does not contain all 
+     * the mandatory attributes required for creation
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext createSubcontext(Name name, Attributes attrs)
+        throws NamingException {
+        return dirContext.createSubcontext(parseName(name), attrs);
+    }
+
+
+    /**
+     * Creates and binds a new context, along with associated attributes.
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @param attrs the attributes to associate with the newly created context
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if the name is already bound
+     * @exception InvalidAttributesException if attrs does not contain all 
+     * the mandatory attributes required for creation
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext createSubcontext(String name, Attributes attrs)
+        throws NamingException {
+        return dirContext.createSubcontext(parseName(name), attrs);
+    }
+
+
+    /**
+     * Retrieves the schema associated with the named object. The schema 
+     * describes rules regarding the structure of the namespace and the 
+     * attributes stored within it. The schema specifies what types of 
+     * objects can be added to the directory and where they can be added; 
+     * what mandatory and optional attributes an object can have. The range 
+     * of support for schemas is directory-specific.
+     * 
+     * @param name the name of the object whose schema is to be retrieved
+     * @return the schema associated with the context; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext getSchema(Name name)
+        throws NamingException {
+        return dirContext.getSchema(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the schema associated with the named object.
+     * 
+     * @param name the name of the object whose schema is to be retrieved
+     * @return the schema associated with the context; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext getSchema(String name)
+        throws NamingException {
+        return dirContext.getSchema(parseName(name));
+    }
+
+
+    /**
+     * Retrieves a context containing the schema objects of the named 
+     * object's class definitions.
+     * 
+     * @param name the name of the object whose object class definition is to 
+     * be retrieved
+     * @return the DirContext containing the named object's class 
+     * definitions; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext getSchemaClassDefinition(Name name)
+        throws NamingException {
+        return dirContext.getSchemaClassDefinition(parseName(name));
+    }
+
+
+    /**
+     * Retrieves a context containing the schema objects of the named 
+     * object's class definitions.
+     * 
+     * @param name the name of the object whose object class definition is to 
+     * be retrieved
+     * @return the DirContext containing the named object's class 
+     * definitions; never null
+     * @exception OperationNotSupportedException if schema not supported
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext getSchemaClassDefinition(String name)
+        throws NamingException {
+        return dirContext.getSchemaClassDefinition(parseName(name));
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set 
+     * of attributes, and retrieves selected attributes. The search is 
+     * performed using the default SearchControls settings.
+     * 
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or 
+     * null, all objects in the target context are returned.
+     * @param attributesToReturn the attributes to return. null indicates 
+     * that all attributes are to be returned; an empty array indicates that 
+     * none are to be returned.
+     * @return a non-null enumeration of SearchResult objects. Each 
+     * SearchResult contains the attributes identified by attributesToReturn 
+     * and the name of the corresponding object, named relative to the 
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(Name name, Attributes matchingAttributes,
+                                    String[] attributesToReturn)
+        throws NamingException {
+        return dirContext.search(parseName(name), matchingAttributes, 
+                                 attributesToReturn);
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set 
+     * of attributes, and retrieves selected attributes.
+     * 
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or 
+     * null, all objects in the target context are returned.
+     * @param attributesToReturn the attributes to return. null indicates 
+     * that all attributes are to be returned; an empty array indicates that 
+     * none are to be returned.
+     * @return a non-null enumeration of SearchResult objects. Each 
+     * SearchResult contains the attributes identified by attributesToReturn 
+     * and the name of the corresponding object, named relative to the 
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, Attributes matchingAttributes,
+                                    String[] attributesToReturn)
+        throws NamingException {
+        return dirContext.search(parseName(name), matchingAttributes, 
+                                 attributesToReturn);
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set 
+     * of attributes. This method returns all the attributes of such objects. 
+     * It is equivalent to supplying null as the atributesToReturn parameter 
+     * to the method search(Name, Attributes, String[]).
+     * 
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or 
+     * null, all objects in the target context are returned.
+     * @return a non-null enumeration of SearchResult objects. Each 
+     * SearchResult contains the attributes identified by attributesToReturn 
+     * and the name of the corresponding object, named relative to the 
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(Name name, Attributes matchingAttributes)
+        throws NamingException {
+        return dirContext.search(parseName(name), matchingAttributes);
+    }
+
+
+    /**
+     * Searches in a single context for objects that contain a specified set 
+     * of attributes.
+     * 
+     * @param name the name of the context to search
+     * @param matchingAttributes the attributes to search for. If empty or 
+     * null, all objects in the target context are returned.
+     * @return a non-null enumeration of SearchResult objects. Each 
+     * SearchResult contains the attributes identified by attributesToReturn 
+     * and the name of the corresponding object, named relative to the 
+     * context named by name.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, Attributes matchingAttributes)
+        throws NamingException {
+        return dirContext.search(parseName(name), matchingAttributes);
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the 
+     * given search filter. Performs the search as specified by the search 
+     * controls.
+     * 
+     * @param name the name of the context or object to search
+     * @param filter the filter expression to use for the search; may not be 
+     * null
+     * @param cons the search controls that control the search. If null, 
+     * the default search controls are used (equivalent to 
+     * (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisfy 
+     * the filter; never null
+     * @exception InvalidSearchFilterException if the search filter specified 
+     * is not supported or understood by the underlying directory
+     * @exception InvalidSearchControlsException if the search controls 
+     * contain invalid settings
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(Name name, String filter, 
+                                    SearchControls cons)
+        throws NamingException {
+        return dirContext.search(parseName(name), filter, cons);
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the 
+     * given search filter. Performs the search as specified by the search 
+     * controls.
+     * 
+     * @param name the name of the context or object to search
+     * @param filter the filter expression to use for the search; may not be 
+     * null
+     * @param cons the search controls that control the search. If null, 
+     * the default search controls are used (equivalent to 
+     * (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisfy 
+     * the filter; never null
+     * @exception InvalidSearchFilterException if the search filter 
+     * specified is not supported or understood by the underlying directory
+     * @exception InvalidSearchControlsException if the search controls 
+     * contain invalid settings
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, String filter, 
+                                    SearchControls cons)
+        throws NamingException {
+        return dirContext.search(parseName(name), filter, cons);
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the 
+     * given search filter. Performs the search as specified by the search 
+     * controls.
+     * 
+     * @param name the name of the context or object to search
+     * @param filterExpr the filter expression to use for the search. 
+     * The expression may contain variables of the form "{i}" where i is a 
+     * nonnegative integer. May not be null.
+     * @param filterArgs the array of arguments to substitute for the 
+     * variables in filterExpr. The value of filterArgs[i] will replace each 
+     * occurrence of "{i}". If null, equivalent to an empty array.
+     * @param cons the search controls that control the search. If null, the 
+     * default search controls are used (equivalent to (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisy the 
+     * filter; never null
+     * @exception ArrayIndexOutOfBoundsException if filterExpr contains {i} 
+     * expressions where i is outside the bounds of the array filterArgs
+     * @exception InvalidSearchControlsException if cons contains invalid 
+     * settings
+     * @exception InvalidSearchFilterException if filterExpr with filterArgs 
+     * represents an invalid search filter
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(Name name, String filterExpr,
+                                    Object[] filterArgs, SearchControls cons)
+        throws NamingException {
+        return dirContext.search(parseName(name), filterExpr, filterArgs, 
+                                 cons);
+    }
+
+
+    /**
+     * Searches in the named context or object for entries that satisfy the 
+     * given search filter. Performs the search as specified by the search 
+     * controls.
+     * 
+     * @param name the name of the context or object to search
+     * @param filterExpr the filter expression to use for the search. 
+     * The expression may contain variables of the form "{i}" where i is a 
+     * nonnegative integer. May not be null.
+     * @param filterArgs the array of arguments to substitute for the 
+     * variables in filterExpr. The value of filterArgs[i] will replace each 
+     * occurrence of "{i}". If null, equivalent to an empty array.
+     * @param cons the search controls that control the search. If null, the 
+     * default search controls are used (equivalent to (new SearchControls())).
+     * @return an enumeration of SearchResults of the objects that satisy the 
+     * filter; never null
+     * @exception ArrayIndexOutOfBoundsException if filterExpr contains {i} 
+     * expressions where i is outside the bounds of the array filterArgs
+     * @exception InvalidSearchControlsException if cons contains invalid 
+     * settings
+     * @exception InvalidSearchFilterException if filterExpr with filterArgs 
+     * represents an invalid search filter
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration search(String name, String filterExpr,
+                                    Object[] filterArgs, SearchControls cons)
+        throws NamingException {
+        return dirContext.search(parseName(name), filterExpr, filterArgs, 
+                                 cons);
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Parses a name.
+     * 
+     * @return the parsed name
+     */
+    protected String parseName(String name) 
+        throws NamingException {
+        return name;
+    }
+
+
+    /**
+     * Parses a name.
+     * 
+     * @return the parsed name
+     */
+    protected Name parseName(Name name) 
+        throws NamingException {
+        return name;
+    }
+
+
+    /**
+     * Lookup in cache.
+     */
+    protected CacheEntry cacheLookupAndLoad(String name) {
+        if (cache == null)
+            return (null);
+        CacheEntry cacheEntry = (CacheEntry) cache.get(name);
+        if (cacheEntry == null) {
+            cacheEntry = new CacheEntry();
+            cacheEntry.name = name;
+            // Load entry
+            if (!cacheLoad(cacheEntry))
+                return null;
+            return (cacheEntry);
+        } else {
+            if (!validate(cacheEntry)) {
+                if (!revalidate(cacheEntry)) {
+                    cacheUnload(cacheEntry.name);
+                    return (null);
+                } else {
+                    cacheEntry.timestamp = 
+                        System.currentTimeMillis() + cacheTTL;
+                }
+            }
+            return (cacheEntry);
+        }
+    }
+
+
+    /**
+     * Validate entry.
+     */
+    protected boolean validate(CacheEntry entry) {
+        if ((entry.resource != null) 
+            //            && (entry.resource.getContent() != null) 
+            && (System.currentTimeMillis() < entry.timestamp)) {
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * Revalidate entry.
+     */
+    protected boolean revalidate(CacheEntry entry) {
+        // Get the attributes at the given path, and check the last 
+        // modification date
+        if (entry.attributes == null)
+            return false;
+        long lastModified = AttributeHelper.getLastModified(entry.attributes);
+        //        long contentLength = entry.attributes.getContentLength();
+        if (lastModified <= 0)
+            return false;
+        try {
+//             Attributes tempAttributes = dirContext.getAttributes(entry.name);
+             Attributes attributes = dirContext.getAttributes(entry.name);
+//             ResourceAttributes attributes = null;
+//             if (!(tempAttributes instanceof ResourceAttributes)) {
+//                 attributes = new ResourceAttributes(tempAttributes);
+//             } else {
+//                 attributes = (ResourceAttributes) tempAttributes;
+//             }
+            long lastModified2 = AttributeHelper.getLastModified(attributes);
+            //            long contentLength2 = attributes.getContentLength();
+            return (lastModified == lastModified2) ;
+                //  && (contentLength == contentLength2);
+        } catch (NamingException e) {
+            return false;
+        }
+    }
+
+
+    /**
+     * Load entry into cache.
+     */
+    protected boolean cacheLoad(CacheEntry entry) {
+
+        if (cache == null)
+            return false;
+
+        String name = entry.name;
+
+        // Retrieve missing info
+
+        // Retrieving attributes
+        if (entry.attributes == null) {
+            try {
+                Attributes attributes = dirContext.getAttributes(entry.name);
+//                 if (!(attributes instanceof ResourceAttributes)) {
+//                     entry.attributes = 
+//                         new ResourceAttributes(attributes);
+//                 } else {
+//                     entry.attributes = (ResourceAttributes) attributes;
+//                 }
+            } catch (NamingException e) {
+                return false;
+            }
+        }
+
+        // Retriving object
+        if ((entry.resource == null) && (entry.context == null)) {
+            try {
+                Object object = dirContext.lookup(name);
+//                 if (object instanceof InputStream) {
+//                     entry.resource = new Resource((InputStream) object);
+//                 } else if (object instanceof DirContext) {
+//                     entry.context = (DirContext) object;
+//                 } else if (object instanceof Resource) {
+//                     entry.resource = (Resource) object;
+//                 } else {
+//                     entry.resource = new Resource(new ByteArrayInputStream
+//                         (object.toString().getBytes()));
+//                 }
+                entry.resource=object;
+            } catch (NamingException e) {
+                return false;
+            }
+        }
+
+        // TODO: lazy loading. We may list a dir, there's no reason to load
+        // all entries ( we may just look at attributes )
+        
+        // Load object content. We cache entries without content ( users, etc ) 
+        /*
+          if ((entry.resource != null) && (entry.resource.getContent() == null) 
+            //            && (entry.attributes.getContentLength() >= 0)
+            && (entry.attributes.getContentLength() < cacheObjectMaxSize)) {
+            int length = (int) entry.attributes.getContentLength();
+            InputStream is = null;
+            try {
+                is = entry.resource.streamContent();
+                int pos = 0;
+                byte[] b = new byte[length];
+                while (pos < length) {
+                    int n = is.read(b, pos, length - pos);
+                    if (n < 0)
+                        break;
+                    pos = pos + n;
+                }
+                entry.resource.setContent(b);
+            } catch (IOException e) {
+                ; // Ignore
+            } finally {
+                try {
+                    if (is != null)
+                        is.close();
+                } catch (IOException e) {
+                    ; // Ignore
+                }
+            }
+        }
+        */
+        // Set timestamp
+        entry.timestamp = System.currentTimeMillis() + cacheTTL;
+
+        // Add new entry to cache
+        cache.put(name, entry);
+
+        return true;
+
+    }
+
+
+    /**
+     * Remove entry from cache.
+     */
+    protected boolean cacheUnload(String name) {
+        if (cache == null)
+            return false;
+        return (cache.remove(name) != null);
+    }
+
+    // ------------------------------------------------- CacheEntry Inner Class
+
+
+    protected class CacheEntry {
+
+
+        // ------------------------------------------------- Instance Variables
+
+
+        long timestamp = -1;
+        String name = null;
+        Attributes attributes = null;
+        //ResourceAttributes attributes = null;
+        Object resource = null;
+        DirContext context = null;
+
+
+        // ----------------------------------------------------- Public Methods
+
+
+        public void recycle() {
+            timestamp = -1;
+            name = null;
+            attributes = null;
+            resource = null;
+            context = null;
+        }
+
+
+        public String toString() {
+            return ("Cache entry: " + name + "\n"
+                    + "Attributes: " + attributes + "\n"
+                    + "Resource: " + resource + "\n"
+                    + "Context: " + context);
+        }
+
+
+    }
+
+
+}
+
diff --git a/connectors/naming/src/org/apache/naming/modules/fs/FileAttributes.java b/connectors/naming/src/org/apache/naming/modules/fs/FileAttributes.java
new file mode 100644
index 0000000..8bd2b96
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/modules/fs/FileAttributes.java
@@ -0,0 +1,116 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.modules.fs;
+
+import java.io.File;
+
+import javax.naming.directory.Attribute;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+
+/**
+ * This specialized resource attribute implementation does some lazy 
+ * reading (to speed up simple checks, like checking the last modified 
+ * date).
+ */
+public class FileAttributes extends BasicAttributes {
+    // -------------------------------------------------------- Constructor
+    
+    public FileAttributes(File file) {
+        this.file = file;
+    }
+    
+    // --------------------------------------------------- Member Variables
+    
+    
+    protected File file;
+    
+    
+    protected boolean accessed = false;
+        
+        
+    // ----------------------------------------- ResourceAttributes Methods
+    public static String CONTENT_LENGTH="contentLength";
+    
+    public Attribute get(String attrId) {
+        if( CONTENT_LENGTH.equalsIgnoreCase(attrId) ) {
+            // XXX use our own att, with long support
+            return new BasicAttribute(CONTENT_LENGTH, new Long( getContentLength() ));
+        }
+	return (super.get(attrId));
+    }
+
+        
+    /**
+     * Is collection.
+     */
+//     public boolean isCollection() {
+//         if (!accessed) {
+//             collection = file.isDirectory();
+//             accessed = true;
+//         }
+//         return super.isCollection();
+//     }
+
+    // Those methods avoid using an Attribute and return the real value.
+    // There is no caching at this level - use the higher level caching.
+        
+    /**
+     * Get content length.
+     * 
+     * @return content length value
+     */
+    public long getContentLength() {
+        long contentLength = file.length();
+        return contentLength;
+    }
+        
+        
+    /**
+     * Get creation time.
+     * 
+     * @return creation time value
+     */
+    public long getCreation() {
+        long creation = file.lastModified();
+        return creation;
+    }
+    
+    /**
+     * Get last modified time.
+     * 
+     * @return lastModified time value
+     */
+    public long getLastModified() {
+        long lastModified = file.lastModified();
+        return lastModified;
+    }
+    
+    /**
+     * Get resource type.
+     * 
+     * @return String resource type
+     */
+    //         public String getResourceType() {
+    //             if (!accessed) {
+//                 //collection = file.isDirectory();
+//                 accessed = true;
+//             }
+//             return super.getResourceType();
+//         }
+}
+
diff --git a/connectors/naming/src/org/apache/naming/modules/fs/FileDirContext.java b/connectors/naming/src/org/apache/naming/modules/fs/FileDirContext.java
new file mode 100644
index 0000000..95fbba2
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/modules/fs/FileDirContext.java
@@ -0,0 +1,744 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.modules.fs;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.naming.Name;
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+import org.apache.naming.core.BaseDirContext;
+import org.apache.naming.core.NamingContextEnumeration;
+import org.apache.naming.core.NamingEntry;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * DirContext for a filesystem directory.
+ *
+ * The 'bind' operation will accept an InputStream ( TODO: File, any
+ * resource with content )
+ * and create the file. ( TODO: what attributes can we support ? )
+ *
+ * The lookup operation will return a FileDirContext or a File.
+ *
+ * Supported attributes: (TODO: lastModified, size, ...)
+ *
+ * Note that JNDI allows memory-efficient style, without having one wrapper
+ * object for each real resource.
+ *
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ */
+public class FileDirContext extends BaseDirContext {
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( FileDirContext.class );
+
+    // -------------------------------------------------------------- Constants
+
+    protected StringManager sm =
+        StringManager.getManager("org.apache.naming.res");
+
+    protected static final int BUFFER_SIZE = 2048;
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Builds a file directory context using the given environment.
+     */
+    public FileDirContext() {
+        super();
+    }
+
+
+    /**
+     * Builds a file directory context using the given environment.
+     */
+    public FileDirContext(Hashtable env) {
+        super(env);
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The document base directory.
+     */
+    protected File base = null;
+
+
+    /**
+     * Absolute normalized filename of the base.
+     */
+    protected String absoluteBase = null;
+
+
+    /**
+     * Case sensitivity.
+     */
+    protected boolean caseSensitive = true;
+
+
+    /**
+     * The document base path.
+     */
+    protected String docBase = null;
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Set the document root.
+     * 
+     * @param docBase The new document root
+     * 
+     * @exception IllegalArgumentException if the specified value is not
+     *  supported by this implementation
+     * @exception IllegalArgumentException if this would create a
+     *  malformed URL
+     */
+    public void setDocBase(String docBase) {
+
+	// Validate the format of the proposed document root
+	if (docBase == null)
+	    throw new IllegalArgumentException
+		(sm.getString("resources.null"));
+
+	// Calculate a File object referencing this document base directory
+	base = new File(docBase);
+        try {
+            base = base.getCanonicalFile();
+        } catch (IOException e) {
+            // Ignore
+        }
+
+	// Validate that the document base is an existing directory
+	if (!base.exists() || !base.isDirectory() || !base.canRead())
+	    throw new IllegalArgumentException
+		(sm.getString("fileResources.base", docBase));
+        this.absoluteBase = base.getAbsolutePath();
+
+	// Change the document root property
+	this.docBase = docBase;
+
+    }
+
+    /**
+     * Return the document root for this component.
+     */
+    public String getDocBase() {
+	return (this.docBase);
+    }
+
+
+    /**
+     * Set case sensitivity.
+     */
+    public void setCaseSensitive(boolean caseSensitive) {
+        this.caseSensitive = caseSensitive;
+    }
+
+
+    /**
+     * Is case sensitive ?
+     */
+    public boolean isCaseSensitive() {
+        return caseSensitive;
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Release any resources allocated for this directory context.
+     */
+    public void release() {
+        caseSensitive = true;
+        absoluteBase = null;
+        base = null;
+        super.release();
+    }
+
+    public void setAttribute( String name, Object v ) {
+        new Throwable().printStackTrace();
+        System.out.println(name + " " + v );
+    }
+
+    // -------------------- BaseDirContext implementation --------------------
+
+    /**
+     * Retrieves the named object. The result is a File relative to the docBase
+     * or a FileDirContext for directories.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookup(Name nameObj, boolean resolveLinkx)
+        throws NamingException
+    {
+        if( log.isDebugEnabled() ) log.debug( "lookup " + nameObj );
+
+        System.out.println("XXX " + nameObj.get(0));
+        if( "fs:".equals( nameObj.get(0).toString() ))
+            nameObj=nameObj.getSuffix(1);
+        
+        String name=nameObj.toString(); // we need to convert anyway, for File constructor
+        
+        Object result = null;
+        File file = file(name);
+        
+        if (file == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+        
+        if (file.isDirectory()) {
+            FileDirContext tempContext = new FileDirContext(env);
+            tempContext.setDocBase(file.getPath());
+            result = tempContext;
+        } else {
+            // TODO: based on the name, return various 'styles' of
+            // content
+            // TODO: use lazy streams, cacheable
+            result = file; //new FileResource(file);
+        }
+        
+        return result;
+    }
+
+
+    /**
+     * Unbinds the named object. Removes the terminal atomic name in name 
+     * from the target context--that named by all but the terminal atomic 
+     * part of name.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void unbind(Name nameObj)
+        throws NamingException
+    {
+        if( "fs:".equals( nameObj.get(0).toString() ))
+            nameObj=nameObj.getSuffix(1);
+        String name=nameObj.toString();
+        if( log.isDebugEnabled() ) log.debug( "unbind " + name );
+        File file = file(name);
+
+        if (file == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+
+        if (!file.delete())
+            throw new NamingException
+                (sm.getString("resources.unbindFailed", name));
+
+    }
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the 
+     * old name. Both names are relative to this context. Any attributes 
+     * associated with the old name become associated with the new name. 
+     * Intermediate contexts of the old name are not changed.
+     * 
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception NameAlreadyBoundException if newName is already bound
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rename(Name oldNameO, Name newNameO)
+        throws NamingException
+    {
+        String oldName=oldNameO.toString();
+        String newName=newNameO.toString();
+        File file = file(oldName);
+
+        if (file == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", oldName));
+
+        File newFile = new File(base, newName);
+        
+        file.renameTo(newFile);        
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them. The contents of any subcontexts are 
+     * not included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(Name nameN)
+        throws NamingException
+    {
+        String name=nameN.toString();
+        if( log.isDebugEnabled() ) log.debug( "list " + name );
+        File file = file(name);
+
+        if (file == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+
+        Vector entries = list(file);
+
+        return new NamingContextEnumeration(entries.elements(), this, false);
+
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them. The contents of any subcontexts are not 
+     * included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(Name nameN)
+        throws NamingException
+    {
+        String name=nameN.toString();
+        if( log.isDebugEnabled() ) log.debug( "listBindings " + name );
+
+        File file = file(name);
+
+        if (file == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+
+        Vector entries = list(file);
+
+        return new NamingContextEnumeration(entries.elements(), this, true);
+
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace. Any 
+     * attributes associated with the name are also removed. Intermediate 
+     * contexts are not destroyed.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * In a federated naming system, a context from one naming system may be 
+     * bound to a name in another. One can subsequently look up and perform 
+     * operations on the foreign context using a composite name. However, an 
+     * attempt destroy the context using this composite name will fail with 
+     * NotContextException, because the foreign context is not a "subcontext" 
+     * of the context in which it is bound. Instead, use unbind() to remove 
+     * the binding of the foreign context. Destroying the foreign context 
+     * requires that the destroySubcontext() be performed on a context from 
+     * the foreign context's "native" naming system.
+     * 
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NotContextException if the name is bound but does not name 
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(Name name)
+        throws NamingException
+    {
+        unbind(name);
+    }
+
+
+    /**
+     * Retrieves the full name of this context within its own namespace.
+     * <p>
+     * Many naming services have a notion of a "full name" for objects in 
+     * their respective namespaces. For example, an LDAP entry has a 
+     * distinguished name, and a DNS record has a fully qualified name. This 
+     * method allows the client application to retrieve this name. The string 
+     * returned by this method is not a JNDI composite name and should not be 
+     * passed directly to context methods. In naming systems for which the 
+     * notion of full name does not make sense, 
+     * OperationNotSupportedException is thrown.
+     * 
+     * @return this context's name in its own namespace; never null
+     * @exception OperationNotSupportedException if the naming system does 
+     * not have the notion of a full name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public String getNameInNamespace()
+        throws NamingException {
+        return docBase;
+    }
+
+
+    // ----------------------------------------------------- DirContext Methods
+
+
+    /**
+     * Retrieves selected attributes associated with a named object. 
+     * See the class description regarding attribute models, attribute type 
+     * names, and operational attributes.
+     * 
+     * @return the requested attributes; never null
+     * @param name the name of the object from which to retrieve attributes
+     * @param attrIds the identifiers of the attributes to retrieve. null 
+     * indicates that all attributes should be retrieved; an empty array 
+     * indicates that none should be retrieved
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Attributes getAttributes(Name nameN, String[] attrIds)
+        throws NamingException
+    {
+        String name=nameN.toString();
+        if( log.isDebugEnabled() ) log.debug( "getAttributes " + name );
+
+        // Building attribute list
+        File file = file(name);
+
+        if (file == null)
+            throw new NamingException
+                (sm.getString("resources.notFound", name));
+
+        return new FileAttributes(file);
+
+    }
+
+    /**
+     * Binds a name to an object, along with associated attributes. If attrs 
+     * is null, the resulting binding will have the attributes associated 
+     * with obj if obj is a DirContext, and no attributes otherwise. If attrs 
+     * is non-null, the resulting binding will have attrs as its attributes; 
+     * any attributes associated with obj are ignored.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(Name nameN, Object obj, Attributes attrs)
+        throws NamingException {
+
+        String name=nameN.toString();
+        // Note: No custom attributes allowed
+        
+        File file = new File(base, name);
+        if (file.exists())
+            throw new NameAlreadyBoundException
+                (sm.getString("resources.alreadyBound", name));
+        
+        rebind(name, obj, attrs);
+    }
+
+
+    /**
+     * Binds a name to an object, along with associated attributes, 
+     * overwriting any existing binding. If attrs is null and obj is a 
+     * DirContext, the attributes from obj are used. If attrs is null and obj 
+     * is not a DirContext, any existing attributes associated with the object
+     * already bound in the directory remain unchanged. If attrs is non-null, 
+     * any existing attributes associated with the object already bound in 
+     * the directory are removed and attrs is associated with the named 
+     * object. If obj is a DirContext and attrs is non-null, the attributes 
+     * of obj are ignored.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @param attrs the attributes to associate with the binding
+     * @exception InvalidAttributesException if some "mandatory" attributes 
+     * of the binding are not supplied
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(Name nameN, Object obj, Attributes attrs)
+        throws NamingException {
+        String name=nameN.toString();
+
+        // Note: No custom attributes allowed
+        // Check obj type
+
+        File file = new File(base, name);
+
+        InputStream is = null;
+//         if (obj instanceof Resource) {
+//             try {
+//                 is = ((Resource) obj).streamContent();
+//             } catch (IOException e) {
+//             }
+//         } else
+
+        // TODO support File, byte[], String
+        if (obj instanceof InputStream) {
+            is = (InputStream) obj;
+        } else if (obj instanceof DirContext) {
+            if (file.exists()) {
+                if (!file.delete())
+                    throw new NamingException
+                        (sm.getString("resources.bindFailed", name));
+            }
+            if (!file.mkdir())
+                throw new NamingException
+                    (sm.getString("resources.bindFailed", name));
+        }
+        if (is == null)
+            throw new NamingException
+                (sm.getString("resources.bindFailed", name));
+
+        // Open os
+
+        try {
+            FileOutputStream os = null;
+            byte buffer[] = new byte[BUFFER_SIZE];
+            int len = -1;
+            try {
+                os = new FileOutputStream(file);
+                while (true) {
+                    len = is.read(buffer);
+                    if (len == -1)
+                        break;
+                    os.write(buffer, 0, len);
+                }
+            } finally {
+                if (os != null)
+                    os.close();
+                is.close();
+            }
+        } catch (IOException e) {
+            throw new NamingException
+                (sm.getString("resources.bindFailed", e));
+        }
+    }
+
+
+    /**
+     * Creates and binds a new context, along with associated attributes. 
+     * This method creates a new subcontext with the given name, binds it in 
+     * the target context (that named by all but terminal atomic component of 
+     * the name), and associates the supplied attributes with the newly 
+     * created object. All intermediate and target contexts must already 
+     * exist. If attrs is null, this method is equivalent to 
+     * Context.createSubcontext().
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @param attrs the attributes to associate with the newly created context
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if the name is already bound
+     * @exception InvalidAttributesException if attrs does not contain all 
+     * the mandatory attributes required for creation
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext createSubcontext(Name nameN, Attributes attrs)
+        throws NamingException
+    {
+        String name=nameN.toString();
+        File file = new File(base, name);
+        if (file.exists())
+            throw new NameAlreadyBoundException
+                (sm.getString("resources.alreadyBound", name));
+        if (!file.mkdir())
+            throw new NamingException
+                (sm.getString("resources.bindFailed", name));
+        return (DirContext) lookup(name);
+    }
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return a context-relative path, beginning with a "/", that represents
+     * the canonical version of the specified path after ".." and "." elements
+     * are resolved out.  If the specified path attempts to go outside the
+     * boundaries of the current context (i.e. too many ".." path elements
+     * are present), return <code>null</code> instead.
+     *
+     * @param path Path to be normalized
+     */
+    protected String normalize(String path) {
+
+	String normalized = path;
+
+	// Normalize the slashes and add leading slash if necessary
+	if (normalized.indexOf('\\') >= 0)
+	    normalized = normalized.replace('\\', '/');
+	if (!normalized.startsWith("/"))
+	    normalized = "/" + normalized;
+
+	// Resolve occurrences of "//" in the normalized path
+	while (true) {
+	    int index = normalized.indexOf("//");
+	    if (index < 0)
+		break;
+	    normalized = normalized.substring(0, index) +
+		normalized.substring(index + 1);
+	}
+
+	// Resolve occurrences of "/./" in the normalized path
+	while (true) {
+	    int index = normalized.indexOf("/./");
+	    if (index < 0)
+		break;
+	    normalized = normalized.substring(0, index) +
+		normalized.substring(index + 2);
+	}
+
+	// Resolve occurrences of "/../" in the normalized path
+	while (true) {
+	    int index = normalized.indexOf("/../");
+	    if (index < 0)
+		break;
+	    if (index == 0)
+		return (null);	// Trying to go outside our context
+	    int index2 = normalized.lastIndexOf('/', index - 1);
+	    normalized = normalized.substring(0, index2) +
+		normalized.substring(index + 3);
+	}
+
+	// Return the normalized path that we have completed
+	return (normalized);
+
+    }
+
+
+    /**
+     * Return a File object representing the specified normalized
+     * context-relative path if it exists and is readable.  Otherwise,
+     * return <code>null</code>.
+     *
+     * @param name Normalized context-relative path (with leading '/')
+     */
+    protected File file(String name) {
+
+        File file = new File(base, name);
+        if (file.exists() && file.canRead()) {
+
+            // Check that this file belongs to our root path
+            String canPath = null;
+            try {
+                canPath = file.getCanonicalPath();
+            } catch (IOException e) {
+            }
+            if (canPath == null)
+                return null;
+
+            if (!canPath.startsWith(absoluteBase)) {
+                return null;
+            }
+
+            // Windows only check
+            if ((caseSensitive) && (File.separatorChar  == '\\')) {
+                String fileAbsPath = file.getAbsolutePath();
+                if (fileAbsPath.endsWith("."))
+                    fileAbsPath = fileAbsPath + "/";
+                String absPath = normalize(fileAbsPath);
+                if (canPath != null)
+                    canPath = normalize(canPath);
+                if ((absoluteBase.length() < absPath.length()) 
+                    && (absoluteBase.length() < canPath.length())) {
+                    absPath = absPath.substring(absoluteBase.length() + 1);
+                    if ((canPath == null) || (absPath == null))
+                        return null;
+                    if (absPath.equals(""))
+                        absPath = "/";
+                    canPath = canPath.substring(absoluteBase.length() + 1);
+                    if (canPath.equals(""))
+                        canPath = "/";
+                    if (!canPath.equals(absPath))
+                        return null;
+                }
+            }
+
+        } else {
+            if( log.isDebugEnabled() ) log.debug( file + " " +
+                                                  file.exists() + " " +
+                                                  file.canRead() );
+            return null;
+        }
+        return file;
+
+    }
+
+
+    /**
+     * List the resources which are members of a collection.
+     * 
+     * @param file Collection
+     * @return Vector containg NamingEntry objects
+     */
+    protected Vector list(File file) {
+
+        Vector entries = new Vector();
+        if (!file.isDirectory())
+            return entries;
+        String[] names = file.list();
+        Arrays.sort(names);             // Sort alphabetically
+        if (names == null)
+            return entries;
+        NamingEntry entry = null;
+
+        for (int i = 0; i < names.length; i++) {
+
+            File currentFile = new File(file, names[i]);
+            Object object = null;
+            if (currentFile.isDirectory()) {
+                FileDirContext tempContext = new FileDirContext(env);
+                tempContext.setDocBase(file.getPath());
+                object = tempContext;
+            } else {
+                //object = new FileResource(currentFile);
+                object = currentFile;
+            }
+            entry = new NamingEntry(names[i], object, null, NamingEntry.ENTRY);
+            entries.addElement(entry);
+
+        }
+
+        return entries;
+
+    }
+
+}
+
diff --git a/connectors/naming/src/org/apache/naming/modules/fs/fsURLContextFactory.java b/connectors/naming/src/org/apache/naming/modules/fs/fsURLContextFactory.java
new file mode 100644
index 0000000..d4d5e61
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/modules/fs/fsURLContextFactory.java
@@ -0,0 +1,91 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.modules.fs;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.spi.InitialContextFactory;
+import javax.naming.spi.ObjectFactory;
+//import org.apache.naming.ContextBindings;
+
+/**
+ * Context factory for the "file:" namespace.
+ * <p>
+ * <b>Important note</b> : This factory MUST be associated with the "java" URL
+ * prefix, which can be done by either :
+ * <ul>
+ * <li>Adding a 
+ * java.naming.factory.url.pkgs=org.apache.naming property to the JNDI properties file</li>
+ * <li>Setting an environment variable named Context.URL_PKG_PREFIXES with 
+ * its value including the org.apache.naming package name. 
+ * More detail about this can be found in the JNDI documentation : 
+ * {@link javax.naming.spi.NamingManager#getURLContext(java.lang.String, java.util.Hashtable)}.</li>
+ * </ul>
+ * 
+ * @author Remy Maucherat
+ */
+
+public class fsURLContextFactory implements ObjectFactory,InitialContextFactory
+{
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( fsURLContextFactory.class );
+    /**
+     * Initial context.
+     */
+    protected static Context initialContext = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Crete a new Context's instance.
+     */
+    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
+                                    Hashtable environment)
+        throws NamingException {
+        // Called with null/null/null if fs:/tmp/test
+        if( log.isDebugEnabled() ) log.debug( "getObjectInstance " + obj + " " + name + " " + nameCtx + " " + environment);
+
+        FileDirContext fc= new FileDirContext(environment);
+        fc.setDocBase( "/" );
+        fc.setURLPrefix("fs:");
+        return fc;
+    }
+
+
+    /**
+     * Get a new (writable) initial context.
+     */
+    public Context getInitialContext(Hashtable environment)
+        throws NamingException
+    {
+        // If the thread is not bound, return a shared writable context
+        if (initialContext == null) {
+            FileDirContext fc= new FileDirContext(environment);
+            fc.setDocBase( "/" );
+            fc.setURLPrefix("fs:");
+            initialContext=fc;
+            if( log.isDebugEnabled() )
+                log.debug("Create initial fs context "+ environment);
+        }
+        return initialContext;
+    }
+}
+
diff --git a/connectors/naming/src/org/apache/naming/modules/id/IdContext.java b/connectors/naming/src/org/apache/naming/modules/id/IdContext.java
new file mode 100644
index 0000000..1a403ae
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/modules/id/IdContext.java
@@ -0,0 +1,44 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.modules.id;
+
+/**
+ * Store int handles in a DirContext.
+ *
+ * This replaces the 3.3 notes mechanism ( which were stored in ContextManager ).
+ * The context name is the 'namespace' for the notes ( Request, Container, etc ).
+ * One subcontext will be created for each note, with the id, description, etc.
+ *
+ * This is also used for Coyote hooks - to create the int hook id.
+ *
+ * Example:
+ *   id:/coyote/hooks/commit -> 1
+ *   id:/coyote/hooks/pre_request -> 2 ...
+ * 
+ *   id:/t33/hooks/pre_request -> 1 ...
+ *
+ *   id:/t33/ContextManager/myNote1 -> 1
+ *
+ * The bound object is an Integer ( for auto-generated ids ).
+ *
+ * XXX Should it be a complex object ? Should we allow pre-binding of
+ *  certain objects ?
+ * XXX Persistence: can we bind a Reference to persistent data ? 
+ */
+public class IdContext {
+
+}
diff --git a/connectors/naming/src/org/apache/naming/modules/java/ContextBindings.java b/connectors/naming/src/org/apache/naming/modules/java/ContextBindings.java
new file mode 100644
index 0000000..3a1a818
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/modules/java/ContextBindings.java
@@ -0,0 +1,370 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.modules.java;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.apache.naming.core.ContextAccessController;
+import org.apache.tomcat.util.res.StringManager;
+
+// this can be a nice generic util that binds per thread or CL any object.
+
+/**
+ * Handles the associations :
+ * <ul>
+ * <li>Catalina context name with the NamingContext</li>
+ * <li>Calling thread with the NamingContext</li>
+ * </ul>
+ *
+ * @author Remy Maucherat
+ */
+public class ContextBindings {
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( ContextBindings.class );
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * Bindings name - naming context. Keyed by name.
+     */
+    private static Hashtable contextNameBindings = new Hashtable();
+
+
+    /**
+     * Bindings thread - naming context. Keyed by thread id.
+     */
+    private static Hashtable threadBindings = new Hashtable();
+
+
+    /**
+     * Bindings thread - name. Keyed by thread id.
+     */
+    private static Hashtable threadNameBindings = new Hashtable();
+
+
+    /**
+     * Bindings class loader - naming context. Keyed by CL id.
+     */
+    private static Hashtable clBindings = new Hashtable();
+
+
+    /**
+     * Bindings class loader - name. Keyed by CL id.
+     */
+    private static Hashtable clNameBindings = new Hashtable();
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm = 
+        StringManager.getManager("org.apache.naming");
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Binds a context name.
+     * 
+     * @param name Name of the context
+     * @param context Associated naming context instance
+     */
+    public static void bindContext(Object name, Context context) {
+        bindContext(name, context, null);
+    }
+
+
+    /**
+     * Binds a context name.
+     * 
+     * @param name Name of the context
+     * @param context Associated naming context instance
+     * @param token Security token
+     */
+    public static void bindContext(Object name, Context context, 
+                                   Object token) {
+        if (ContextAccessController.checkSecurityToken(name, token))
+            contextNameBindings.put(name, context);
+    }
+
+
+    /**
+     * Unbind context name.
+     * 
+     * @param name Name of the context
+     */
+    public static void unbindContext(Object name) {
+        unbindContext(name, null);
+    }
+
+
+    /**
+     * Unbind context name.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void unbindContext(Object name, Object token) {
+        if (ContextAccessController.checkSecurityToken(name, token))
+            contextNameBindings.remove(name);
+    }
+
+
+    /**
+     * Retrieve a naming context.
+     * 
+     * @param name Name of the context
+     */
+    static Context getContext(Object name) {
+        return (Context) contextNameBindings.get(name);
+    }
+
+
+    /**
+     * Binds a naming context to a thread.
+     * 
+     * @param name Name of the context
+     */
+    public static void bindThread(Object name) 
+        throws NamingException {
+        bindThread(name, null);
+    }
+
+
+    /**
+     * Binds a naming context to a thread.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void bindThread(Object name, Object token) 
+        throws NamingException {
+        //        log.info( "BIND: " + name + " " + token );
+        if (ContextAccessController.checkSecurityToken(name, token)) {
+            Context context = (Context) contextNameBindings.get(name);
+            if (context == null)
+                throw new NamingException
+                    (sm.getString("contextBindings.unknownContext", name));
+            threadBindings.put(Thread.currentThread(), context);
+            threadNameBindings.put(Thread.currentThread(), name);
+        }
+    }
+
+
+    /**
+     * Unbinds a naming context to a thread.
+     * 
+     * @param name Name of the context
+     */
+    public static void unbindThread(Object name) {
+        unbindThread(name, null);
+    }
+
+
+    /**
+     * Unbinds a naming context to a thread.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void unbindThread(Object name, Object token) {
+        if (ContextAccessController.checkSecurityToken(name, token)) {
+            threadBindings.remove(Thread.currentThread());
+            threadNameBindings.remove(Thread.currentThread());
+        }
+    }
+
+
+    /**
+     * Retrieves the naming context bound to a thread.
+     */
+    public static Context getThread()
+        throws NamingException {
+        Context context = 
+            (Context) threadBindings.get(Thread.currentThread());
+        log.info( "Context=getThread: " + context );
+        if (context == null)
+            throw new NamingException
+                (sm.getString("contextBindings.noContextBoundToThread"));
+        return context;
+    }
+
+
+    /**
+     * Retrieves the naming context name bound to a thread.
+     */
+    static Object getThreadName()
+        throws NamingException {
+        Object name = threadNameBindings.get(Thread.currentThread());
+        if (name == null)
+            throw new NamingException
+                (sm.getString("contextBindings.noContextBoundToThread"));
+        return name;
+    }
+
+
+    /**
+     * Tests if current thread is bound to a context.
+     */
+    public static boolean isThreadBound() {
+        return (threadBindings.containsKey(Thread.currentThread()));
+    }
+
+
+    /**
+     * Binds a naming context to a class loader.
+     * 
+     * @param name Name of the context
+     */
+    public static void bindClassLoader(Object name) 
+        throws NamingException {
+        bindClassLoader(name, null);
+    }
+
+
+    /**
+     * Binds a naming context to a thread.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void bindClassLoader(Object name, Object token) 
+        throws NamingException {
+        bindClassLoader
+            (name, token, Thread.currentThread().getContextClassLoader());
+    }
+
+
+    /**
+     * Binds a naming context to a thread.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void bindClassLoader(Object name, Object token, 
+                                       ClassLoader classLoader) 
+        throws NamingException {
+        if (ContextAccessController.checkSecurityToken(name, token)) {
+            Context context = (Context) contextNameBindings.get(name);
+            if (context == null)
+                throw new NamingException
+                    (sm.getString("contextBindings.unknownContext", name));
+            clBindings.put(classLoader, context);
+            clNameBindings.put(classLoader, name);
+        }
+    }
+
+
+    /**
+     * Unbinds a naming context to a class loader.
+     * 
+     * @param name Name of the context
+     */
+    public static void unbindClassLoader(Object name) {
+        unbindClassLoader(name, null);
+    }
+
+
+    /**
+     * Unbinds a naming context to a class loader.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void unbindClassLoader(Object name, Object token) {
+        unbindClassLoader(name, token, 
+                          Thread.currentThread().getContextClassLoader());
+    }
+
+
+    /**
+     * Unbinds a naming context to a class loader.
+     * 
+     * @param name Name of the context
+     * @param token Security token
+     */
+    public static void unbindClassLoader(Object name, Object token, 
+                                         ClassLoader classLoader) {
+        if (ContextAccessController.checkSecurityToken(name, token)) {
+            Object n = clNameBindings.get(classLoader);
+            if (!(n.equals(name))) {
+                return;
+            }
+            clBindings.remove(classLoader);
+            clNameBindings.remove(classLoader);
+        }
+    }
+
+
+    /**
+     * Retrieves the naming context bound to a class loader.
+     */
+    public static Context getClassLoader()
+        throws NamingException {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        Context context = null;
+        do {
+            context = (Context) clBindings.get(cl);
+            log.info( "Context=getClassLoader: " + context + " " + cl );
+            if (context != null) {
+                return context;
+            }
+        } while ((cl = cl.getParent()) != null);
+        throw new NamingException
+            (sm.getString("contextBindings.noContextBoundToCL"));
+    }
+
+
+    /**
+     * Retrieves the naming context name bound to a class loader.
+     */
+    static Object getClassLoaderName()
+        throws NamingException {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        Object name = null;
+        do {
+            name = clNameBindings.get(cl);
+            if (name != null) {
+                return name;
+            }
+        } while ((cl = cl.getParent()) != null);
+        throw new NamingException
+            (sm.getString("contextBindings.noContextBoundToCL"));
+    }
+
+
+    /**
+     * Tests if current class loader is bound to a context.
+     */
+    public static boolean isClassLoaderBound() {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        do {
+            if (clBindings.containsKey(cl)) {
+                return true;
+            }
+        } while ((cl = cl.getParent()) != null);
+        return false;
+    }
+
+
+}
+
diff --git a/connectors/naming/src/org/apache/naming/modules/java/SelectorContext.java b/connectors/naming/src/org/apache/naming/modules/java/SelectorContext.java
new file mode 100644
index 0000000..1f26399
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/modules/java/SelectorContext.java
@@ -0,0 +1,690 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.modules.java;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NameParser;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+import org.apache.naming.core.BaseContext;
+import org.apache.naming.modules.memory.MemoryNamingContext;
+import org.apache.tomcat.util.res.StringManager;
+
+/* This delegates to another context, removing a prefix.
+   XXX make it generic, move to core. The context thread can be
+   selected only once - in the java: factory, to avoid overhead.
+*/
+
+/**
+ * Per thread context, implementing java: like contexts.
+ *
+ * @author Remy Maucherat
+ */
+public class SelectorContext extends BaseContext {
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     * Namespace URL.
+     */
+    public static final String prefix = "java:";
+
+
+    /**
+     * Namespace URL length.
+     */
+    public static final int prefixLength = prefix.length();
+
+
+    /**
+     * Initial context prefix.
+     */
+    public static final String IC_PREFIX = "IC_";
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Builds a Catalina selector context using the given environment.
+     */
+    public SelectorContext(Hashtable env) {
+        super( env );
+    }
+
+
+    /**
+     * Builds a Catalina selector context using the given environment.
+     */
+    public SelectorContext(Hashtable env, boolean initialContext) {
+        this(env);
+        this.initialContext = initialContext;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Environment.
+     */
+    protected Hashtable env;
+
+
+    /**
+     * The string manager for this package.
+     */
+    protected StringManager sm = StringManager.getManager("org.apache.naming.res");
+
+
+    /**
+     * Request for an initial context.
+     */
+    protected boolean initialContext = false;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // -------------------------------------------------------- Context Methods
+
+
+    public Object lookup(Name name)
+        throws NamingException {
+        // Strip the URL header
+        // Find the appropriate NamingContext according to the current bindings
+        // Execute the lookup on that context
+        return getBoundContext().lookup(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the named object.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookup(String name)
+        throws NamingException {
+        // Strip the URL header
+        // Find the appropriate NamingContext according to the current bindings
+        // Execute the lookup on that context
+        return getBoundContext().lookup(parseName(name));
+    }
+
+
+    /**
+     * Binds a name to an object. All intermediate contexts and the target 
+     * context (that named by all but terminal atomic component of the name) 
+     * must already exist.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(Name name, Object obj)
+        throws NamingException {
+        getBoundContext().bind(parseName(name), obj);
+    }
+
+
+    /**
+     * Binds a name to an object.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(String name, Object obj)
+        throws NamingException {
+        getBoundContext().bind(parseName(name), obj);
+    }
+
+
+    /**
+     * Binds a name to an object, overwriting any existing binding. All 
+     * intermediate contexts and the target context (that named by all but 
+     * terminal atomic component of the name) must already exist.
+     * <p>
+     * If the object is a DirContext, any existing attributes associated with 
+     * the name are replaced with those of the object. Otherwise, any 
+     * existing attributes associated with the name remain unchanged.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(Name name, Object obj)
+        throws NamingException {
+        getBoundContext().rebind(parseName(name), obj);
+    }
+
+
+    /**
+     * Binds a name to an object, overwriting any existing binding.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param obj the object to bind; possibly null
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rebind(String name, Object obj)
+        throws NamingException {
+        getBoundContext().rebind(parseName(name), obj);
+    }
+
+
+    /**
+     * Unbinds the named object. Removes the terminal atomic name in name 
+     * from the target context--that named by all but the terminal atomic 
+     * part of name.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void unbind(Name name)
+        throws NamingException {
+        getBoundContext().unbind(parseName(name));
+    }
+
+
+    /**
+     * Unbinds the named object.
+     * 
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void unbind(String name)
+        throws NamingException {
+        getBoundContext().unbind(parseName(name));
+    }
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the 
+     * old name. Both names are relative to this context. Any attributes 
+     * associated with the old name become associated with the new name. 
+     * Intermediate contexts of the old name are not changed.
+     * 
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception NameAlreadyBoundException if newName is already bound
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rename(Name oldName, Name newName)
+        throws NamingException {
+        getBoundContext().rename(parseName(oldName), parseName(newName));
+    }
+
+
+    /**
+     * Binds a new name to the object bound to an old name, and unbinds the 
+     * old name.
+     * 
+     * @param oldName the name of the existing binding; may not be empty
+     * @param newName the name of the new binding; may not be empty
+     * @exception NameAlreadyBoundException if newName is already bound
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void rename(String oldName, String newName)
+        throws NamingException {
+        getBoundContext().rename(parseName(oldName), parseName(newName));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them. The contents of any subcontexts are 
+     * not included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(Name name)
+        throws NamingException {
+        return getBoundContext().list(parseName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(String name)
+        throws NamingException {
+        return getBoundContext().list(parseName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them. The contents of any subcontexts are not 
+     * included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(Name name)
+        throws NamingException {
+        return getBoundContext().listBindings(parseName(name));
+    }
+
+
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(String name)
+        throws NamingException {
+        return getBoundContext().listBindings(parseName(name));
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace. Any 
+     * attributes associated with the name are also removed. Intermediate 
+     * contexts are not destroyed.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * In a federated naming system, a context from one naming system may be 
+     * bound to a name in another. One can subsequently look up and perform 
+     * operations on the foreign context using a composite name. However, an 
+     * attempt destroy the context using this composite name will fail with 
+     * NotContextException, because the foreign context is not a "subcontext" 
+     * of the context in which it is bound. Instead, use unbind() to remove 
+     * the binding of the foreign context. Destroying the foreign context 
+     * requires that the destroySubcontext() be performed on a context from 
+     * the foreign context's "native" naming system.
+     * 
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NotContextException if the name is bound but does not name 
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(Name name)
+        throws NamingException {
+        getBoundContext().destroySubcontext(parseName(name));
+    }
+
+
+    /**
+     * Destroys the named context and removes it from the namespace.
+     * 
+     * @param name the name of the context to be destroyed; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NotContextException if the name is bound but does not name 
+     * a context, or does not name a context of the appropriate type
+     */
+    public void destroySubcontext(String name)
+        throws NamingException {
+        getBoundContext().destroySubcontext(parseName(name));
+    }
+
+
+    /**
+     * Creates and binds a new context. Creates a new context with the given 
+     * name and binds it in the target context (that named by all but 
+     * terminal atomic component of the name). All intermediate contexts and 
+     * the target context must already exist.
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if creation of the subcontext 
+     * requires specification of mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Context createSubcontext(Name name)
+        throws NamingException {
+        return getBoundContext().createSubcontext(parseName(name));
+    }
+
+
+    /**
+     * Creates and binds a new context.
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if creation of the subcontext 
+     * requires specification of mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Context createSubcontext(String name)
+        throws NamingException {
+        return getBoundContext().createSubcontext(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the named object, following links except for the terminal 
+     * atomic component of the name. If the object bound to name is not a 
+     * link, returns the object itself.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link 
+     * (if any).
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookupLink(Name name)
+        throws NamingException {
+        return getBoundContext().lookupLink(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the named object, following links except for the terminal 
+     * atomic component of the name.
+     * 
+     * @param name the name of the object to look up
+     * @return the object bound to name, not following the terminal link 
+     * (if any).
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object lookupLink(String name)
+        throws NamingException {
+        return getBoundContext().lookupLink(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the parser associated with the named context. In a 
+     * federation of namespaces, different naming systems will parse names 
+     * differently. This method allows an application to get a parser for 
+     * parsing names into their atomic components using the naming convention 
+     * of a particular naming system. Within any single naming system, 
+     * NameParser objects returned by this method must be equal (using the 
+     * equals() test).
+     * 
+     * @param name the name of the context from which to get the parser
+     * @return a name parser that can parse compound names into their atomic 
+     * components
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NameParser getNameParser(Name name)
+        throws NamingException {
+        return getBoundContext().getNameParser(parseName(name));
+    }
+
+
+    /**
+     * Retrieves the parser associated with the named context.
+     * 
+     * @param name the name of the context from which to get the parser
+     * @return a name parser that can parse compound names into their atomic 
+     * components
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NameParser getNameParser(String name)
+        throws NamingException {
+        return getBoundContext().getNameParser(parseName(name));
+    }
+
+
+    /**
+     * Composes the name of this context with a name relative to this context.
+     * <p>
+     * Given a name (name) relative to this context, and the name (prefix) 
+     * of this context relative to one of its ancestors, this method returns 
+     * the composition of the two names using the syntax appropriate for the 
+     * naming system(s) involved. That is, if name names an object relative 
+     * to this context, the result is the name of the same object, but 
+     * relative to the ancestor context. None of the names may be null.
+     * 
+     * @param name a name relative to this context
+     * @param prefix the name of this context relative to one of its ancestors
+     * @return the composition of prefix and name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Name composeName(Name name, Name prefix)
+        throws NamingException {
+	prefix = (Name) name.clone();
+	return prefix.addAll(name);
+    }
+
+
+    /**
+     * Composes the name of this context with a name relative to this context.
+     * 
+     * @param name a name relative to this context
+     * @param prefix the name of this context relative to one of its ancestors
+     * @return the composition of prefix and name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public String composeName(String name, String prefix)
+        throws NamingException {
+        return prefix + "/" + name;
+    }
+
+
+    /**
+     * Adds a new environment property to the environment of this context. If 
+     * the property already exists, its value is overwritten.
+     * 
+     * @param propName the name of the environment property to add; may not 
+     * be null
+     * @param propVal the value of the property to add; may not be null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object addToEnvironment(String propName, Object propVal)
+        throws NamingException {
+        return getBoundContext().addToEnvironment(propName, propVal);
+    }
+
+
+    /**
+     * Removes an environment property from the environment of this context. 
+     * 
+     * @param propName the name of the environment property to remove; 
+     * may not be null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Object removeFromEnvironment(String propName)
+        throws NamingException {
+        return getBoundContext().removeFromEnvironment(propName);
+    }
+
+
+    /**
+     * Retrieves the environment in effect for this context. See class 
+     * description for more details on environment properties. 
+     * The caller should not make any changes to the object returned: their 
+     * effect on the context is undefined. The environment of this context 
+     * may be changed using addToEnvironment() and removeFromEnvironment().
+     * 
+     * @return the environment of this context; never null
+     * @exception NamingException if a naming exception is encountered
+     */
+    public Hashtable getEnvironment()
+        throws NamingException {
+        return getBoundContext().getEnvironment();
+    }
+
+
+    /**
+     * Closes this context. This method releases this context's resources 
+     * immediately, instead of waiting for them to be released automatically 
+     * by the garbage collector.
+     * This method is idempotent: invoking it on a context that has already 
+     * been closed has no effect. Invoking any other method on a closed 
+     * context is not allowed, and results in undefined behaviour.
+     * 
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void close()
+        throws NamingException {
+        getBoundContext().close();
+    }
+
+
+    /**
+     * Retrieves the full name of this context within its own namespace.
+     * <p>
+     * Many naming services have a notion of a "full name" for objects in 
+     * their respective namespaces. For example, an LDAP entry has a 
+     * distinguished name, and a DNS record has a fully qualified name. This 
+     * method allows the client application to retrieve this name. The string 
+     * returned by this method is not a JNDI composite name and should not be 
+     * passed directly to context methods. In naming systems for which the 
+     * notion of full name does not make sense, 
+     * OperationNotSupportedException is thrown.
+     * 
+     * @return this context's name in its own namespace; never null
+     * @exception OperationNotSupportedException if the naming system does 
+     * not have the notion of a full name
+     * @exception NamingException if a naming exception is encountered
+     */
+    public String getNameInNamespace()
+        throws NamingException {
+        return prefix;
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Get the bound context.
+     */
+    protected Context getBoundContext()
+        throws NamingException {
+
+        if (initialContext) {
+            String ICName = IC_PREFIX;
+            if (ContextBindings.isThreadBound()) {
+                ICName += ContextBindings.getThreadName();
+            } else if (ContextBindings.isClassLoaderBound()) {
+                ICName += ContextBindings.getClassLoaderName();
+            }
+            Context initialContext = ContextBindings.getContext(ICName);
+            if (initialContext == null) {
+                // Allocating a new context and binding it to the appropriate 
+                // name
+                // XXX Should return null, let the caller create something
+                // Or use a different constructor.
+                initialContext = new MemoryNamingContext(env);
+                ContextBindings.bindContext(ICName, initialContext);
+            }
+            return initialContext;
+        } else {
+            if (ContextBindings.isThreadBound()) {
+                return ContextBindings.getThread();
+            } else {
+                return ContextBindings.getClassLoader();
+            }
+        }
+
+    }
+
+
+    /**
+     * Strips the URL header.
+     * 
+     * @return the parsed name
+     * @exception NamingException if there is no "java:" header or if no 
+     * naming context has been bound to this thread
+     */
+    protected String parseName(String name) 
+        throws NamingException {
+        
+	if ((!initialContext) && (name.startsWith(prefix))) {
+            return (name.substring(prefixLength));
+        } else {
+            if (initialContext) {
+                return (name);
+            } else {
+                throw new NamingException
+                    (sm.getString("selectorContext.noJavaUrl"));
+            }
+        }
+        
+    }
+
+
+    /**
+     * Strips the URL header.
+     * 
+     * @return the parsed name
+     * @exception NamingException if there is no "java:" header or if no 
+     * naming context has been bound to this thread
+     */
+    protected Name parseName(Name name) 
+        throws NamingException {
+
+	if ((!initialContext) && (!name.isEmpty()) 
+            && (name.get(0).equals(prefix))) {
+            return (name.getSuffix(1));
+        } else {
+            if (initialContext) {
+                return (name);
+            } else {
+                throw new NamingException
+                    (sm.getString("selectorContext.noJavaUrl"));
+            }
+        }
+
+    }
+}
+
diff --git a/connectors/naming/src/org/apache/naming/modules/java/javaURLContextFactory.java b/connectors/naming/src/org/apache/naming/modules/java/javaURLContextFactory.java
new file mode 100644
index 0000000..6d7fd48
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/modules/java/javaURLContextFactory.java
@@ -0,0 +1,108 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.modules.java;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.spi.InitialContextFactory;
+import javax.naming.spi.ObjectFactory;
+
+import org.apache.naming.modules.memory.MemoryNamingContext;
+
+/**
+ * Context factory for the "java:" namespace.
+ * <p>
+ * <b>Important note</b> : This factory MUST be associated with the "java" URL
+ * prefix, which can be done by either :
+ * <ul>
+ * <li>Adding a 
+ * java.naming.factory.url.pkgs=org.apache.naming property to the JNDI properties file</li>
+ * <li>Setting an environment variable named Context.URL_PKG_PREFIXES with 
+ * its value including the org.apache.naming package name. 
+ * More detail about this can be found in the JNDI documentation : 
+ * {@link javax.naming.spi.NamingManager#getURLContext(java.lang.String, java.util.Hashtable)}.</li>
+ * </ul>
+ * 
+ * @author Remy Maucherat
+ */
+
+public class javaURLContextFactory implements ObjectFactory, InitialContextFactory
+{
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    public static final String MAIN = "initialContext";
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Initial context.
+     */
+    protected static Context initialContext = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    // -------------------------------------------------- ObjectFactory Methods
+
+
+    /**
+     * Crete a new Context's instance.
+     */
+    public Object getObjectInstance(Object obj, Name name, Context nameCtx,
+                                    Hashtable environment)
+        throws NamingException {
+        if ((ContextBindings.isThreadBound()) || 
+            (ContextBindings.isClassLoaderBound())) {
+            return new SelectorContext(environment);
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Get a new (writable) initial context.
+     */
+    public Context getInitialContext(Hashtable environment)
+        throws NamingException {
+        if (ContextBindings.isThreadBound() || 
+            (ContextBindings.isClassLoaderBound())) {
+            // Redirect the request to the bound initial context
+            return new SelectorContext(environment, true);
+        } else {
+            // If the thread is not bound, return a shared writable context
+            if (initialContext == null)
+                initialContext = new MemoryNamingContext(environment);
+            return initialContext;
+        }
+    }
+
+
+}
+
diff --git a/connectors/naming/src/org/apache/naming/modules/memory/MemoryNamingContext.java b/connectors/naming/src/org/apache/naming/modules/memory/MemoryNamingContext.java
new file mode 100644
index 0000000..6d892e4
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/modules/memory/MemoryNamingContext.java
@@ -0,0 +1,389 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.modules.memory;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.LinkRef;
+import javax.naming.Name;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NotContextException;
+import javax.naming.Reference;
+import javax.naming.Referenceable;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.spi.NamingManager;
+
+import org.apache.naming.core.BaseDirContext;
+import org.apache.naming.core.NamingContextEnumeration;
+import org.apache.naming.core.NamingEntry;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * In-memory context.
+ *
+ * IMPLEMENTATION. We use NamingEntry stored in a tree of hashtables. 
+ *
+ * @author Remy Maucherat
+ * @author Costin Manolache
+ */
+public class MemoryNamingContext extends BaseDirContext {
+
+    public MemoryNamingContext()
+        throws NamingException
+    {
+        super();
+    }
+
+    /**
+     * Builds a naming context using the given environment.
+     */
+    public MemoryNamingContext(Hashtable env) 
+        throws NamingException
+    {
+        super( env );
+        this.bindings = new Hashtable();
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The string manager for this package.
+     */
+    protected static StringManager sm =
+        StringManager.getManager("org.apache.naming.res");
+
+    /**
+     * Bindings in this Context.
+     */
+    protected Hashtable bindings;
+
+    public void setBindings( Hashtable bindings ) {
+        this.bindings = bindings;
+    }
+
+    // -------------------------------------------------------- Context Methods
+
+    /**
+     * Unbinds the named object. Removes the terminal atomic name in name 
+     * from the target context--that named by all but the terminal atomic 
+     * part of name.
+     * <p>
+     * This method is idempotent. It succeeds even if the terminal atomic 
+     * name is not bound in the target context, but throws 
+     * NameNotFoundException if any of the intermediate contexts do not exist. 
+     * 
+     * @param name the name to bind; may not be empty
+     * @exception NameNotFoundException if an intermediate context does not 
+     * exist
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void unbind(Name name, boolean isContext)
+        throws NamingException
+    {
+        checkWritable(name);
+        
+	while ((!name.isEmpty()) && (name.get(0).length() == 0))
+	    name = name.getSuffix(1);
+        
+        if (name.isEmpty())
+            throw new NamingException
+                (sm.getString("namingContext.invalidName"));
+        
+        NamingEntry entry = (NamingEntry) bindings.get(name.get(0));
+        
+        if (entry == null) {
+            throw new NameNotFoundException
+                (sm.getString("namingContext.nameNotBound", name.get(0)));
+        }
+        
+        if (name.size() > 1) {
+            if (entry.type == NamingEntry.CONTEXT) {
+                ((Context) entry.value).unbind(name.getSuffix(1));
+            } else {
+                throw new NamingException
+                    (sm.getString("namingContext.contextExpected"));
+            }
+        } else {
+            if (entry.type == NamingEntry.CONTEXT) {
+                ((Context) entry.value).close();
+                bindings.remove(name.get(0));
+            } else {
+                if( isContext ) {
+                    throw new NotContextException
+                        (sm.getString("namingContext.contextExpected"));
+                } else {
+                    bindings.remove(name.get(0));
+                }
+            }
+        }
+    }
+
+    /**
+     * Enumerates the names bound in the named context, along with the class 
+     * names of objects bound to them. The contents of any subcontexts are 
+     * not included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the names and class names of the bindings in 
+     * this context. Each element of the enumeration is of type NameClassPair.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration list(Name name)
+        throws NamingException {
+        // Removing empty parts
+        while ((!name.isEmpty()) && (name.get(0).length() == 0))
+            name = name.getSuffix(1);
+
+        if (name.isEmpty()) {
+            return new NamingContextEnumeration(bindings.elements(), this, false);
+        }
+        
+        NamingEntry entry = (NamingEntry) bindings.get(name.get(0));
+        
+        if (entry == null) {
+            throw new NameNotFoundException
+                (sm.getString("namingContext.nameNotBound", name.get(0)));
+        }
+        
+        if (entry.type != NamingEntry.CONTEXT) {
+            throw new NamingException
+                (sm.getString("namingContext.contextExpected"));
+        }
+        return ((Context) entry.value).list(name.getSuffix(1));
+    }
+
+    private Name removeEmptyPrefix(Name name ) {
+        while ((!name.isEmpty()) && (name.get(0).length() == 0))
+            name = name.getSuffix(1);
+        return name;
+    }
+    
+    /**
+     * Enumerates the names bound in the named context, along with the 
+     * objects bound to them. The contents of any subcontexts are not 
+     * included.
+     * <p>
+     * If a binding is added to or removed from this context, its effect on 
+     * an enumeration previously returned is undefined.
+     * 
+     * @param name the name of the context to list
+     * @return an enumeration of the bindings in this context. 
+     * Each element of the enumeration is of type Binding.
+     * @exception NamingException if a naming exception is encountered
+     */
+    public NamingEnumeration listBindings(Name name)
+        throws NamingException {
+        // Removing empty parts
+        while ((!name.isEmpty()) && (name.get(0).length() == 0))
+            name = name.getSuffix(1);
+        
+        if (name.isEmpty()) {
+            return new NamingContextEnumeration(bindings.elements(), this, true);
+        }
+        
+        NamingEntry entry = (NamingEntry) bindings.get(name.get(0));
+        
+        if (entry == null) {
+            throw new NameNotFoundException
+                (sm.getString("namingContext.nameNotBound", name.get(0)));
+        }
+        
+        if (entry.type != NamingEntry.CONTEXT) {
+            throw new NamingException
+                (sm.getString("namingContext.contextExpected"));
+        }
+        return ((Context) entry.value).listBindings(name.getSuffix(1));
+    }
+
+    /**
+     * Creates and binds a new context. Creates a new context with the given 
+     * name and binds it in the target context (that named by all but 
+     * terminal atomic component of the name). All intermediate contexts and 
+     * the target context must already exist.
+     * 
+     * @param name the name of the context to create; may not be empty
+     * @return the newly created context
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if creation of the subcontext 
+     * requires specification of mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public DirContext createSubcontext(Name name, Attributes attrs)
+        throws NamingException
+    {
+        checkWritable(name);
+        
+        DirContext newContext = new MemoryNamingContext(env);
+        bind(name, newContext);
+        return newContext;
+    }
+
+    // XXX Make it iterative, less objects
+    private NamingEntry findNamingEntry(Name name, boolean resolveLinks)
+        throws NamingException
+    {
+         if (name.isEmpty()) {
+//             // If name is empty, a newly allocated naming context is returned
+//             MemoryNamingContext mmc= new MemoryNamingContext(env);
+//             mmc.setBindings( bindings );
+//             return mmc;
+             return null;
+         }
+        
+        NamingEntry entry = (NamingEntry) bindings.get(name.get(0));
+        
+        if (entry == null) {
+            throw new NameNotFoundException
+                (sm.getString("namingContext.nameNotBound", name.get(0)));
+        }
+        
+        if (name.size() > 1) {
+            // If the size of the name is greater that 1, then we go through a
+            // number of subcontexts.
+            if (entry.type != NamingEntry.CONTEXT) {
+                throw new NamingException
+                    (sm.getString("namingContext.contextExpected"));
+            }
+            return entry;
+        } else {
+            return entry;
+        }
+    }
+
+    public Object lookup(Name name, boolean resolveLinks)
+        throws NamingException
+    {
+        // Removing empty parts
+        while ((!name.isEmpty()) && (name.get(0).length() == 0))
+            name = name.getSuffix(1);
+        
+        NamingEntry entry=findNamingEntry( name, resolveLinks );
+
+        if( entry.type == NamingEntry.CONTEXT ) {
+            return ((BaseDirContext) entry.value).lookup(name.getSuffix(1), resolveLinks);
+        }
+        
+        if ((resolveLinks) && (entry.type == NamingEntry.LINK_REF)) {
+            String link = ((LinkRef) entry.value).getLinkName();
+            if (link.startsWith(".")) {
+                // Link relative to this context
+                return lookup(link.substring(1));
+            } else {
+                return (new InitialContext(env)).lookup(link);
+            }
+        } else if (entry.type == NamingEntry.REFERENCE) {
+            try {
+                Object obj = NamingManager.getObjectInstance
+                    (entry.value, name, this, env);
+                if (obj != null) {
+                    entry.value = obj;
+                    entry.type = NamingEntry.ENTRY;
+                }
+                return obj;
+            } catch (NamingException e) {
+                throw e;
+            } catch (Exception e) {
+                throw new NamingException(e.getMessage());
+            }
+        } else {
+            return entry.value;
+        }
+    }
+
+    /**
+     * Binds a name to an object. All intermediate contexts and the target 
+     * context (that named by all but terminal atomic component of the name) 
+     * must already exist.
+     * 
+     * @param name the name to bind; may not be empty
+     * @param object the object to bind; possibly null
+     * @param rebind if true, then perform a rebind (ie, overwrite)
+     * @exception NameAlreadyBoundException if name is already bound
+     * @exception InvalidAttributesException if object did not supply all 
+     * mandatory attributes
+     * @exception NamingException if a naming exception is encountered
+     */
+    public void bind(Name name, Object obj, Attributes attrs,
+                     boolean rebind)
+        throws NamingException
+    {
+        checkWritable(name);
+        
+	while ((!name.isEmpty()) && (name.get(0).length() == 0))
+	    name = name.getSuffix(1);
+
+        if (name.isEmpty())
+            throw new NamingException
+                (sm.getString("namingContext.invalidName"));
+        
+        NamingEntry entry = (NamingEntry) bindings.get(name.get(0));
+        
+        if (name.size() > 1) {
+            if (entry == null) {
+                throw new NameNotFoundException
+                    (sm.getString("namingContext.nameNotBound", name.get(0)));
+            }
+            if (entry.type == NamingEntry.CONTEXT) {
+                if (rebind) {
+                    ((Context) entry.value).rebind(name.getSuffix(1), obj);
+                } else {
+                    ((Context) entry.value).bind(name.getSuffix(1), obj);
+                }
+            } else {
+                throw new NamingException
+                    (sm.getString("namingContext.contextExpected"));
+            }
+        } else {
+            if ((!rebind) && (entry != null)) {
+                throw new NamingException
+                    (sm.getString("namingContext.alreadyBound", name.get(0)));
+            } else {
+                // Getting the type of the object and wrapping it within a new
+                // NamingEntry
+                Object toBind = 
+                    NamingManager.getStateToBind(obj, name, this, env);
+                if (toBind instanceof Context) {
+                    entry = new NamingEntry(name.get(0), toBind, attrs,  
+                                            NamingEntry.CONTEXT);
+                } else if (toBind instanceof LinkRef) {
+                    entry = new NamingEntry(name.get(0), toBind, attrs, 
+                                            NamingEntry.LINK_REF);
+                } else if (toBind instanceof Reference) {
+                    entry = new NamingEntry(name.get(0), toBind, attrs, 
+                                            NamingEntry.REFERENCE);
+                } else if (toBind instanceof Referenceable) {
+                    toBind = ((Referenceable) toBind).getReference();
+                    entry = new NamingEntry(name.get(0), toBind, attrs, 
+                                            NamingEntry.REFERENCE);
+                } else {
+                    entry = new NamingEntry(name.get(0), toBind, attrs, 
+                                            NamingEntry.ENTRY);
+                }
+                bindings.put(name.get(0), entry);
+            }
+        }
+    }
+}
+
diff --git a/connectors/naming/src/org/apache/naming/modules/memory/memoryURLContextFactory.java b/connectors/naming/src/org/apache/naming/modules/memory/memoryURLContextFactory.java
new file mode 100644
index 0000000..2b104ac
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/modules/memory/memoryURLContextFactory.java
@@ -0,0 +1,48 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.modules.memory;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.spi.InitialContextFactory;
+
+/**
+ */
+public class memoryURLContextFactory implements InitialContextFactory
+{
+    /**
+     * Initial context.
+     */
+    protected static Context initialContext = null;
+
+    /**
+     * Get a new (writable) initial context.
+     */
+    public Context getInitialContext(Hashtable environment)
+        throws NamingException
+    {
+        // If the thread is not bound, return a shared writable context
+        if (initialContext == null)
+            initialContext = new MemoryNamingContext(environment);
+        return initialContext;
+    }
+
+
+}
+
diff --git a/connectors/naming/src/org/apache/naming/res/LocalStrings.properties b/connectors/naming/src/org/apache/naming/res/LocalStrings.properties
new file mode 100644
index 0000000..3580fbe
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/res/LocalStrings.properties
@@ -0,0 +1,31 @@
+contextBindings.unknownContext=Unknown context name : {0}
+contextBindings.noContextBoundToThread=No naming context bound to this thread
+contextBindings.noContextBoundToCL=No naming context bound to this class loader
+selectorContext.noJavaUrl=This context must be accessed throught a java: URL
+namingContext.contextExpected=Name is not bound to a Context
+namingContext.nameNotBound=Name {0} is not bound in this Context
+namingContext.readOnly=Context is read only
+namingContext.invalidName=Name is not valid
+namingContext.alreadyBound=Name {0} is already bound in this Context
+namingContext.noAbsoluteName=Can't generate an absolute name for this namespace
+
+fileResources.base=Document base {0} does not exist or is not a readable directory
+warResources.notWar=Doc base must point to a WAR file
+warResources.invalidWar=Invalid or unreadable WAR file : {0}
+jarResources.syntax=Document base {0} must start with 'jar:' and end with '!/'
+resources.alreadyStarted=Resources has already been started
+resources.connect=Cannot connect to document base {0}
+resources.input=Cannot create input stream for resource {0}
+resources.notStarted=Resources has not yet been started
+resources.null=Document base cannot be null
+resources.notFound=Resource {0} not found
+resources.path=Context relative path {0} must start with '/'
+resources.alreadyBound=Name {0} is already bound in this Context
+resources.bindFailed=Bind failed: {0}
+resources.unbindFailed=Unbind failed: {0}
+standardResources.alreadyStarted=Resources has already been started
+standardResources.directory=File base {0} is not a directory
+standardResources.exists=File base {0} does not exist
+standardResources.notStarted=Resources has not yet been started
+standardResources.null=Document base cannot be null
+standardResources.slash=Document base {0} must not end with a slash
diff --git a/connectors/naming/src/org/apache/naming/res/LocalStrings_es.properties b/connectors/naming/src/org/apache/naming/res/LocalStrings_es.properties
new file mode 100644
index 0000000..5ab05a2
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/res/LocalStrings_es.properties
@@ -0,0 +1,31 @@
+contextBindings.unknownContext=Contexto {0} desconocido 
+contextBindings.noContextBoundToThread=No hay contexto de nombres asociado a este hilo
+contextBindings.noContextBoundToCL=No hay asociado contexto de nombres a este cargador de clases
+selectorContext.noJavaUrl=Este contexto debe de ser accedido a traves de una URL de tipo "java:"
+namingContext.contextExpected=El nombre no está asociado a ningún Contexto
+namingContext.nameNotBound=El nombre {0} no está asociado a este contexto
+namingContext.readOnly=El contexto es de sólo lectura
+namingContext.invalidName=Nombre no válido
+namingContext.alreadyBound=El nombre {0} está ya asociado en este Contexto
+namingContext.noAbsoluteName=No se puede generar un nombre absoluto para este espacio de nombres
+
+fileResources.base=El Documento base {0} no existe o no es un directorio legible
+warResources.notWar=Doc base debe de apuntar a un archivo WAR
+warResources.invalidWar=Archivo WAR inválido o ilegible: {0}
+jarResources.syntax=El Documento base {0} debe de comenzar con 'jar:' y acabar con '!/'
+resources.alreadyStarted=Ya han sido arrancados los Recursos
+resources.connect=No puedo conectar a documento base {0}
+resources.input=No puedo crear corriente de entrada para recurso {0}
+resources.notStarted=Aún no han sido arrancados los Recursos
+resources.null=El Documento base no puede ser nulo
+resources.notFound=Recurso {0} no hallado
+resources.path=La trayectoria relativa de Contexto {0} debe de comenzar con '/'
+resources.alreadyBound=El nombre {0} ya está asociado en este Contexto
+resources.bindFailed=Falló la Asociación: {0}
+resources.unbindFailed=Falló la Desasociación: {0}
+standardResources.alreadyStarted=Ya han sido arrancados los Recursos
+standardResources.directory=El archivo base {0} no es un directorio
+standardResources.exists=El archivo base {0} no existe
+standardResources.notStarted=Aún no han sido arrancados los Recursos
+standardResources.null=El Documento base no puede ser nulo
+standardResources.slash=El Documento base {0} no debe de acabar con barra
diff --git a/connectors/naming/src/org/apache/naming/res/LocalStrings_fr.properties b/connectors/naming/src/org/apache/naming/res/LocalStrings_fr.properties
new file mode 100644
index 0000000..ac4f3de
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/res/LocalStrings_fr.properties
@@ -0,0 +1,31 @@
+contextBindings.unknownContext=nom de context inconnu : {0}
+contextBindings.noContextBoundToThread=Aucun de contexte de nommage associé à ce thread
+contextBindings.noContextBoundToCL=Aucun de contexte de nommage associé class loader
+selectorContext.noJavaUrl=Ce contexte doit être accédé via une java: URL
+namingContext.contextExpected=Le nom n''est pas associé à un Contexte
+namingContext.nameNotBound=Le nom {0} n''est pas associé à ce Contexte
+namingContext.readOnly=Le contexte est en lecture seule
+namingContext.invalidName=Le nom n''est pas valide
+namingContext.alreadyBound=Le nom {0} est déjà associé dans ce Contexte
+namingContext.noAbsoluteName=Impossible de générer un nom absolut pour ce namespace
+
+fileResources.base=La base de Document {0} n''existe pas ou n''est pas un directory lisible
+warResources.notWar=La base de Document doit pointer sur un fichier WAR
+warResources.invalidWar=Fichier WAR invalide ou illisible : {0}
+jarResources.syntax=La base de Document {0} doit commencé par 'jar:' et finir avec '!/'
+resources.alreadyStarted=Les ressources ont déjà été démarrées
+resources.connect=Impossible de se connecter à la base de document {0}
+resources.input=Impossible de créer un flux d''entrée (input stream) pour la ressource {0}
+resources.notStarted=Les ressources n''ont pas encore été démarrées
+resources.null=La base de Document ne peut être nulle
+resources.notFound=La ressource {0} est introuvable
+resources.path=Le chemin relatif de Contexte {0} doit commencer par '/'
+resources.alreadyBound=Le nom {0} est déjà associé dans ce Contexte
+resources.bindFailed=L''association a échouée: {0}
+resources.unbindFailed=La désassocation a échouée : {0}
+standardResources.alreadyStarted=Les Ressources ont déjà été démarrées
+standardResources.directory=La base de fichier {0} n''est pas un directory
+standardResources.exists=La base de fichier {0} n''existe pas
+standardResources.notStarted=Les ressources n''ont pas encore été démarrées
+standardResources.null=La base de Document ne peut être nulle
+standardResources.slash=La base de Document {0} ne doit pas finir par slash
diff --git a/connectors/naming/src/org/apache/naming/res/LocalStrings_ja.properties b/connectors/naming/src/org/apache/naming/res/LocalStrings_ja.properties
new file mode 100644
index 0000000..64176d2
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/res/LocalStrings_ja.properties
@@ -0,0 +1,31 @@
+contextBindings.unknownContext=\u672a\u77e5\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u540d\u3067\u3059 : {0}
+contextBindings.noContextBoundToThread=\u540d\u524d\u4ed8\u3051\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306f\u3053\u306e\u30b9\u30ec\u30c3\u30c9\u306b\u30d0\u30a4\u30f3\u30c9\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+contextBindings.noContextBoundToCL=\u540d\u524d\u4ed8\u3051\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306f\u3053\u306e\u30af\u30e9\u30b9\u30ed\u30fc\u30c0\u306b\u30d0\u30a4\u30f3\u30c9\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+selectorContext.noJavaUrl=\u3053\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306fjava: URL\u3092\u7528\u3044\u3066\u30a2\u30af\u30bb\u30b9\u3055\u308c\u306d\u3070\u306a\u308a\u307e\u305b\u3093
+namingContext.contextExpected=\u540d\u524d\u304c\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306b\u30d0\u30a4\u30f3\u30c9\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+namingContext.nameNotBound=\u540d\u524d {0} \u306f\u3053\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306b\u30d0\u30a4\u30f3\u30c9\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+namingContext.readOnly=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306f\u30ea\u30fc\u30c9\u30aa\u30f3\u30ea\u30fc\u3067\u3059
+namingContext.invalidName=\u540d\u524d\u306f\u7121\u52b9\u3067\u3059
+namingContext.alreadyBound=\u540d\u524d {0} \u306f\u65e2\u306b\u3053\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306b\u30d0\u30a4\u30f3\u30c9\u3055\u308c\u3066\u3044\u307e\u3059
+namingContext.noAbsoluteName=\u3053\u306e\u540d\u524d\u7a7a\u9593\u306b\u7d76\u5bfe\u540d\u3092\u751f\u6210\u3067\u304d\u307e\u305b\u3093
+
+fileResources.base=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9 {0} \u304c\u5b58\u5728\u3057\u306a\u3044\u3001\u307e\u305f\u306f\u8aad\u3081\u306a\u3044\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3067\u3059
+warResources.notWar=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9\u306fWAR\u30d5\u30a1\u30a4\u30eb\u3092\u793a\u3055\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+warResources.invalidWar=\u7121\u52b9\u307e\u305f\u306f\u8aad\u3081\u306a\u3044WAR\u30d5\u30a1\u30a4\u30eb\u3067\u3059 : {0}
+jarResources.syntax=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9 {0} \u306f'jar:'\u3067\u59cb\u307e\u308a\u3001'!/'\u3067\u7d42\u3089\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+resources.alreadyStarted=\u30ea\u30bd\u30fc\u30b9\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+resources.connect=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9 {0} \u306b\u63a5\u7d9a\u3067\u304d\u307e\u305b\u3093
+resources.input=\u30ea\u30bd\u30fc\u30b9 {0} \u306b\u5165\u529b\u30b9\u30c8\u30ea\u30fc\u30e0\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093
+resources.notStarted=\u30ea\u30bd\u30fc\u30b9\u306f\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+resources.null=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9\u306fnull\u3067\u3042\u3063\u3066\u306f\u3044\u3051\u307e\u305b\u3093
+resources.notFound=\u30ea\u30bd\u30fc\u30b9 {0} \u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+resources.path=\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u76f8\u5bfe\u30d1\u30b9 {0} \u306f'/'\u3067\u59cb\u307e\u3089\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
+resources.alreadyBound=\u540d\u524d {0} \u306f\u65e2\u306b\u3053\u306e\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u306b\u30d0\u30a4\u30f3\u30c9\u3055\u308c\u3066\u3044\u307e\u3059
+resources.bindFailed=\u30d0\u30a4\u30f3\u30c9\u304c\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+resources.unbindFailed=\u30a2\u30f3\u30d0\u30a4\u30f3\u30c9\u304c\u5931\u6557\u3057\u307e\u3057\u305f: {0}
+standardResources.alreadyStarted=\u30ea\u30bd\u30fc\u30b9\u306f\u65e2\u306b\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u3059
+standardResources.directory=\u30d5\u30a1\u30a4\u30eb\u30d9\u30fc\u30b9 {0} \u306f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3067\u306f\u3042\u308a\u307e\u305b\u3093
+standardResources.exists=\u30d5\u30a1\u30a4\u30eb\u30d9\u30fc\u30b9 {0} \u306f\u5b58\u5728\u3057\u307e\u305b\u3093
+standardResources.notStarted=\u30ea\u30bd\u30fc\u30b9\u304c\u307e\u3060\u8d77\u52d5\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+standardResources.null=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9\u306fnull\u3067\u3042\u3063\u3066\u306f\u3044\u3051\u307e\u305b\u3093
+standardResources.slash=\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u30d9\u30fc\u30b9 {0} \u306f\u30b9\u30e9\u30c3\u30b7\u30e5\u3067\u7d42\u3063\u3066\u306f\u3044\u3051\u307e\u305b\u3093
diff --git a/connectors/naming/src/org/apache/naming/util/AttributeHelper.java b/connectors/naming/src/org/apache/naming/util/AttributeHelper.java
new file mode 100644
index 0000000..e8a1099
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/util/AttributeHelper.java
@@ -0,0 +1,158 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.util;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+// import org.apache.naming.resources.Resource;
+// import org.apache.naming.resources.ResourceAttributes;
+
+/**
+ * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
+ * @author Costin Manolache
+ */
+public class AttributeHelper
+{
+    /**
+     * Content length.
+     */
+    public static final String CONTENT_LENGTH = "getcontentlength";
+    public static final String ALTERNATE_CONTENT_LENGTH = "content-length";
+
+    /**
+     * MIME type of the content.
+     */
+    public static final String CONTENT_TYPE = "getcontenttype";
+    public static final String ALTERNATE_TYPE = "content-type";
+    
+
+    /**
+     * Last modification date. XXX Use standard LDAP att name
+     */
+    public static final String LAST_MODIFIED = "getlastmodified";
+    public static final String ALTERNATE_LAST_MODIFIED = "last-modified";
+    /**
+     * Date formats using for Date parsing.
+     */
+    protected static final SimpleDateFormat formats[] = {
+        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
+        new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US),
+        new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
+        new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
+    };
+
+
+    /**
+     * Get content length.
+     * 
+     * @return content length value
+     */
+    public static long getContentLength(Attributes attributes) {
+        long contentLength=-1;
+        if (contentLength != -1L)
+            return contentLength;
+        if (attributes != null) {
+            Attribute attribute = attributes.get(CONTENT_LENGTH);
+            if( attribute==null )
+                attribute=attributes.get(ALTERNATE_CONTENT_LENGTH );
+            if (attribute != null) {
+                try {
+                    Object value = attribute.get();
+                    if (value instanceof Long) {
+                        contentLength = ((Long) value).longValue();
+                    } else {
+                        try {
+                            contentLength = Long.parseLong(value.toString());
+                        } catch (NumberFormatException e) {
+                            ; // Ignore
+                        }
+                    }
+                } catch (NamingException e) {
+                    ; // No value for the attribute
+                }
+            }
+        }
+        return contentLength;
+    }
+    
+    /**
+     * Return the content type value.
+     */
+    public static String getContentType(Attributes attributes) {
+        Attribute attribute = attributes.get(CONTENT_TYPE);
+        if( attribute == null ) return null;
+        
+        try {
+            String s= attribute.get().toString();
+            return s;
+        } catch (Exception e) {
+            // Shouldn't happen, unless the attribute has no value
+        }
+        return null;
+    }
+    
+    /** Find the last modified of an entry. It's using various common
+     *  attribute names, and support Long, Date, String att values.
+     */
+    public static long getLastModified( Attributes attributes ) {
+        long lastModified=-1;
+        Date lastModifiedDate;
+        
+        Attribute attribute = attributes.get(LAST_MODIFIED);
+        if( attribute==null )
+            attribute=attributes.get(ALTERNATE_LAST_MODIFIED);
+        
+        if (attribute != null) {
+            try {
+                Object value = attribute.get();
+                if (value instanceof Long) {
+                    lastModified = ((Long) value).longValue();
+                } else if (value instanceof Date) {
+                    lastModified = ((Date) value).getTime();
+                    lastModifiedDate = (Date) value;
+                } else {
+                    String lastModifiedDateValue = value.toString();
+                    Date result = null;
+                    // Parsing the HTTP Date
+                    for (int i = 0; (result == null) && 
+                             (i < formats.length); i++) {
+                        try {
+                            result = 
+                                formats[i].parse(lastModifiedDateValue);
+                        } catch (ParseException e) {
+                            ;
+                        }
+                    }
+                    if (result != null) {
+                        lastModified = result.getTime();
+                        lastModifiedDate = result;
+                    }
+                }
+            } catch (NamingException e) {
+                ; // No value for the attribute
+            }
+        }
+        return lastModified;
+    }
+        
+}
diff --git a/connectors/naming/src/org/apache/naming/util/DomXml.java b/connectors/naming/src/org/apache/naming/util/DomXml.java
new file mode 100644
index 0000000..ce292bd
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/util/DomXml.java
@@ -0,0 +1,196 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+// moved from jk2 config package. 
+
+/**
+ *
+ * @author Costin Manolache
+ */
+public class DomXml {
+    String file;
+    String name;
+
+    // -------------------- Settings -------------------- 
+
+    /** 
+     */
+    public void setFile( String file ) {
+        this.file=file; 
+    }
+
+    /** 
+     */
+    public void setName( String name ) {
+        this.name=name; 
+    }
+
+    // -------------------- Implementation --------------------
+    Node domN;
+    
+    /** Return the top level node
+     */
+    public Node getNode() {
+        return domN;
+    }
+
+    // -------------------- ant wrapper --------------------
+    
+    public void execute() {
+        try {
+            if( file== null) {
+                log.error("No file attribute");
+                return;
+            }
+
+            File docF=new File(file);
+
+            Document doc=readXml(docF);
+            if( doc == null ) return;
+
+            domN = doc.getDocumentElement();
+            if( domN==null ) {
+                log.error("Can't find the root node");
+                return;
+            }
+
+        } catch( Exception ex ) {
+            ex.printStackTrace();
+        }
+    }
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( DomXml.class );
+
+    
+    // -------------------- DOM utils --------------------
+
+    /** Get the content of a node
+     */
+    public static String getContent(Node n ) {
+        if( n==null ) return null;
+        Node n1=n.getFirstChild();
+        // XXX Check if it's a text node
+
+        String s1=n1.getNodeValue();
+        return s1.trim();
+    }
+    
+    /** Get the first child
+     */
+    public static Node getChild( Node parent, String name ) {
+        if( parent==null ) return null;
+        Node first=parent.getFirstChild();
+        if( first==null ) return null;
+        for (Node node = first; node != null;
+             node = node.getNextSibling()) {
+            //System.out.println("getNode: " + name + " " + node.getNodeName());
+            if( name.equals( node.getNodeName() ) ) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    /** Get the first child's content ( i.e. it's included TEXT node )
+     */
+    public static String getChildContent( Node parent, String name ) {
+        Node first=parent.getFirstChild();
+        if( first==null ) return null;
+        for (Node node = first; node != null;
+             node = node.getNextSibling()) {
+            //System.out.println("getNode: " + name + " " + node.getNodeName());
+            if( name.equals( node.getNodeName() ) ) {
+                return getContent( node );
+            }
+        }
+        return null;
+    }
+
+    /** Get the node in the list of siblings
+     */
+    public static Node getNext( Node current ) {
+        Node first=current.getNextSibling();
+        String name=current.getNodeName();
+        if( first==null ) return null;
+        for (Node node = first; node != null;
+             node = node.getNextSibling()) {
+            //System.out.println("getNode: " + name + " " + node.getNodeName());
+            if( name.equals( node.getNodeName() ) ) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    public static class NullResolver implements EntityResolver {
+        public InputSource resolveEntity (String publicId,
+                                                   String systemId)
+            throws SAXException, IOException
+        {
+            if( log.isTraceEnabled()) 
+                log.trace("ResolveEntity: " + publicId + " " + systemId);
+            return new InputSource(new StringReader(""));
+        }
+    }
+
+    public void saveXml( Node n, File xmlF ) {
+        
+    }
+    
+    public static Document readXml(File xmlF)
+        throws SAXException, IOException, ParserConfigurationException
+    {
+        if( ! xmlF.exists() ) {
+            log.error("No xml file " + xmlF );
+            return null;
+        }
+        DocumentBuilderFactory dbf =
+            DocumentBuilderFactory.newInstance();
+
+        dbf.setValidating(false);
+        dbf.setIgnoringComments(false);
+        dbf.setIgnoringElementContentWhitespace(true);
+        //dbf.setCoalescing(true);
+        //dbf.setExpandEntityReferences(true);
+
+        DocumentBuilder db = null;
+        db = dbf.newDocumentBuilder();
+        db.setEntityResolver( new NullResolver() );
+        
+        // db.setErrorHandler( new MyErrorHandler());
+
+        Document doc = db.parse(xmlF);
+        return doc;
+    }
+
+}
diff --git a/connectors/naming/src/org/apache/naming/util/RecyclableNamingEnumeration.java b/connectors/naming/src/org/apache/naming/util/RecyclableNamingEnumeration.java
new file mode 100644
index 0000000..24c6db4
--- /dev/null
+++ b/connectors/naming/src/org/apache/naming/util/RecyclableNamingEnumeration.java
@@ -0,0 +1,111 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.naming.util;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+/**
+ * Naming enumeration implementation.
+ *
+ * @author Remy Maucherat
+ * @version $Revision$ $Date$
+ */
+
+public class RecyclableNamingEnumeration 
+    implements NamingEnumeration {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    public RecyclableNamingEnumeration(Vector entries) {
+        this.entries = entries;
+        recycle();
+    }
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * Entries.
+     */
+    protected Vector entries;
+
+
+    /**
+     * Underlying enumeration.
+     */
+    protected Enumeration enum;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Retrieves the next element in the enumeration.
+     */
+    public Object next()
+        throws NamingException {
+        return nextElement();
+    }
+
+
+    /**
+     * Determines whether there are any more elements in the enumeration.
+     */
+    public boolean hasMore()
+        throws NamingException {
+        return enum.hasMoreElements();
+    }
+
+
+    /**
+     * Closes this enumeration.
+     */
+    public void close()
+        throws NamingException {
+    }
+
+
+    public boolean hasMoreElements() {
+        return enum.hasMoreElements();
+    }
+
+
+    public Object nextElement() {
+        return enum.nextElement();
+    }
+
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Recycle.
+     */
+    void recycle() {
+        enum = entries.elements();
+    }
+
+
+}
+
diff --git a/connectors/procrun/README.txt b/connectors/procrun/README.txt
new file mode 100644
index 0000000..8f25239
--- /dev/null
+++ b/connectors/procrun/README.txt
@@ -0,0 +1,12 @@
+
+Placeholder for procrun binaries used for Tomcat 5.x
+
+Rename binaries to reflect default service name:
+  prunsrv.exe -> tomcat5.exe
+  prunmgr.exe -> tomcat5w.exe
+
+Documentation for the commons-daemon project, which is
+where procrun comes from, is at
+http://jakarta.apache.org/commons/daemon.  That site
+has links to the CVS repository and the source code for
+the project.
diff --git a/connectors/procrun/bin/tomcat5.exe b/connectors/procrun/bin/tomcat5.exe
new file mode 100644
index 0000000..463cd9c
--- /dev/null
+++ b/connectors/procrun/bin/tomcat5.exe
Binary files differ
diff --git a/connectors/procrun/bin/tomcat5w.exe b/connectors/procrun/bin/tomcat5w.exe
new file mode 100644
index 0000000..d765557
--- /dev/null
+++ b/connectors/procrun/bin/tomcat5w.exe
Binary files differ
diff --git a/connectors/scandoc/scandoc.pl b/connectors/scandoc/scandoc.pl
new file mode 100755
index 0000000..85b70a7
--- /dev/null
+++ b/connectors/scandoc/scandoc.pl
@@ -0,0 +1,1342 @@
+#!/usr/bin/perl
+#
+# ScanDoc - Version 0.14,  A C/C++ Embedded Documentation Analyser
+# ----------------------------------------------------------------
+#
+# Distributed under the "Artistic License".  See the file 
+# "COPYING" that accompanies the ScanDoc distribution.
+#
+# See http://scandoc.sourceforge.net/ for more information and
+# complete documentation.
+#
+# (c) 1997 - 2000 Talin and others.
+
+require "ctime.pl";
+require "getopts.pl";
+
+# 1 = on (verbose); 0 = off 
+$debug = 0;
+
+# Get the current date
+$date = &ctime(time);
+
+# Set the default tab size
+$tabSize = 4;
+
+$minorVersion = 14;
+$majorVersion = 0;
+$scandocURL   = "http://scandoc.sourceforge.net/";
+
+# Set up default templates
+&Getopts( 'i:d:p:t:' );
+
+if ($#ARGV < 0) {
+  die "Usage: -i <doc-template> -p <output-path> -t<tabsize> -d<sym>=<value> [ <input-files> ... ]\n";
+}
+
+# Read the template
+if (!defined $opt_i) {
+  $opt_i = "default.pl";
+}
+&readTemplate( $opt_i );
+
+# Set the destination path.
+$destPath = "";
+$destPath = $opt_p if (defined($opt_p));
+
+# Set the tab size.
+$tabSize = $opt_t if (defined($opt_t));
+
+# Handle defines
+if ($opt_d) {
+  foreach $def (split( /,/, $opt_d )) {
+    if ($def =~ /\s*(\w*)\=(.*)/) {
+      $${1} = $2;
+    }
+    else {
+      $${1} = 1;
+    }
+  }
+}
+
+# For each input filename, parse it
+while ($srcfile = shift(@ARGV)) {
+
+  $linenumber = 0;
+  open( FILE, $srcfile ) || die "Can't open file $srcfile\n";
+  print STDERR "Reading \"$srcfile\"\n";
+  
+  $docTag = 'description';
+  $docEmpty = 1;
+  $packageName = '.general';
+  $author = '';
+  $version = '';
+  $class = 0;
+  $_ = '';
+  
+  while (&parseDeclaration( '' )) {}
+}
+
+# Collate subclasses and associate with class record.
+foreach $className (keys %subclasses) {
+  my $class = &classRecord( $className );
+  
+  if ($class) {
+    my @subs = ();
+    # print STDERR "$className ", join( ',', @{$subclasses{ $className }} ), "\n";
+    foreach $subName ($subclasses{ $className }) {
+      if (&classRecord( $subName )) {
+	push @subs, $subName;
+      }
+      $class->{ 'subs' } = @subs;
+    }
+  }
+}
+
+# Turn packages into objects. Special case for "default" package.
+foreach $pkg (keys %packages)
+{
+  # print STDERR $pkg, "\n";
+  bless $packages{ $pkg }, PackageRecord;
+  if ($pkg eq '.general') {
+    $packages{ $pkg }{ 'name' } = "General";
+  }
+  else {
+    $packages{ $pkg }{ 'name' } = $pkg;
+  }
+  # print STDERR $packages{ $pkg }->Classes(), "\n";
+}
+
+# Execute template file
+# print STDERR $docTemplate; # For debugging
+eval $docTemplate;
+print STDERR $@;
+
+exit;
+
+# ======================= Subroutines ================================
+
+# Read a line of input, and remove blank lines and preprocessor directives.
+sub rdln {
+  my ($skip_next_line) = 0;
+  if (defined ($_)) {
+    my ($previous_line) = $_;
+    while ( (/^(\s*|\#.*)$/ || $skip_next_line ) && ($_ = <FILE>)) {
+      if ($previous_line =~ m/\\\s*/) { $skip_next_line = 1; }
+      else { $skip_next_line = 0; }
+      $previous_line = $_;
+      $linenumber++; 
+      if ($debug) { print STDERR "(0:$srcfile) $linenumber.\n"; } 
+    }
+  }
+}
+
+# Don't skip "#"
+sub rdln2 {
+  if (defined ($_)) {
+    while (/^(\s*)$/ && ($_ = <FILE>)) {$linenumber++; if ($debug) { print STDERR "(0:$srcfile) $linenumber.\n"; } }
+  }
+}
+
+# Remove comments from current line
+sub removeComment {
+  s|//.*||;
+}
+
+# parsing functions
+sub matchKW		{ &rdln; return (s/^\s*($_[0])//, $1) if defined ($_); return (0, 0); }
+#sub matchStruct		{ &rdln; return (s/^\s*(struct|class)//, $1) if defined ($_); return (0, 0); }
+#sub matchPermission	{ &rdln; return (s/^\s*(public|private|protected)// && $1) if defined ($_); return (0,0); }
+sub matchID		{ &rdln; return (s/^\s*([A-Za-z_]\w*)//, $1) if defined ($_); return (0,0); }
+sub matchColon		{ &rdln; return (s/^\s*\://) if defined ($_); return 0; }
+sub matchComma		{ &rdln; return (s/^\s*\,//) if defined ($_); return 0; }
+sub matchSemi		{ &rdln; return (s/^\s*\;//) if defined ($_); return 0; }
+sub matchRBracket	{ &rdln; return (s/^\s*\{//) if defined ($_); return 0; }
+sub matchLBracket	{ &rdln; return (s/^\s*\}//) if defined ($_); return 0; }
+sub matchRParen		{ &rdln; return (s/^\s*\(//) if defined ($_); return 0; }
+sub matchLParen		{ &rdln; return (s/^\s*\)//) if defined ($_); return 0; }
+sub matchRAngle		{ &rdln; return (s/^\s*\<//) if defined ($_); return 0; }
+sub matchLAngle		{ &rdln; return (s/^\s*\>//) if defined ($_); return 0; }
+sub matchDecl           { &rdln; return (s/^(\s*[\s\w\*\[\]\~\&\n\:]+)//, $1) if defined ($_); return (0, 0); }
+sub matchOper		{ &rdln; return (s/^\s*([\~\&\^\>\<\=\!\%\*\+\-\/\|\w]*)// && $1) if defined ($_); return 0; }
+sub matchFuncOper	{ &rdln; return (s/^\s*(\(\))// && $1) if defined ($_); return 0; }
+sub matchAny		{ &rdln; return (s/^\s*(\S+)//, $1) if defined ($_); return (0, 0); }
+sub matchChar		{ &rdln; return (s/^(.)//, $1) if defined ($_); return (0, 0); }
+sub matchChar2	        { &rdln2; return (s/^(.)//, $1) if defined ($_); return (0, 0); }
+sub matchString 	{ &rdln; return (s/^\"(([^\\\"]|(\\.)))*\"//, $1) if defined ($_); return (0, 0); }
+
+# Skip to next semicolon
+sub skipToSemi {
+  
+  while (!&matchSemi) {
+    
+    &rdln;
+    s|//.*||;			# Eat comments
+      if (&matchLBracket) {
+	&skipBody;
+	next;
+      }
+    last if !s/^\s*([^\s\{\;]+)//;
+    # print STDERR "$1 ";
+  }
+}
+
+# Skip function body
+sub skipBody {
+  local( $nest );
+  
+  $nest = 1;
+  
+  for (;;) {
+    if (&matchRBracket) { $nest++; }
+    elsif (&matchLBracket) {
+      $nest--;
+      last if !$nest;
+    }
+    else { 
+      last if ((($valid,) = &matchKW( "[^\{\}]")) && !$valid);
+    }
+  }
+}
+
+# Skip a string. (multiline)
+sub skipString {
+  local( $char, $lastchar);
+  $lastchar = "\"";
+  
+  for (;;) {
+    ($valid, $char) = &matchChar2;
+    if (($char eq "\"") && ($lastchar ne "\\")) { last; }
+    if ($lastchar eq "\\") { $lastchar = " "; }
+    else { $lastchar = $char; }
+  }
+}
+
+
+# Skip everything in parenthesis.
+sub skipParenBody {
+  local( $nest );
+  
+  $nest = 1;
+  
+  for (;;) {
+    if (&matchRParen) { $nest++; }
+    elsif (&matchLParen) {
+      $nest--;
+      last if !$nest;
+    }
+    else { 
+      last if ((($valid,) = &matchKW( "[^\(\)]")) && !$valid);
+    }
+  }
+}
+
+# Parse (*name) syntax
+sub parseParenPointer {
+
+  $parenPointerFunction = "";
+
+  if (s/^(\s*\(\s*\*)//) {
+    $decl .= $1;
+    $nest = 1;
+    
+    for (;;) {
+      # Preserve spaces, eliminate in-line comments
+      &removeComment;
+      while (s/^(\s+)//) { $decl .= $1; &rdln; }
+      
+      if (&matchRParen) { $nest++; $decl .= "("; }
+      elsif (&matchLParen) {
+	$decl .= ")";
+	$nest--;
+	last if !$nest;
+      }
+      elsif ((($valid, $d) = &matchKW( "[^\(\)]*")) && $valid) { $decl .= $d; }
+      else { last; }
+    }
+    
+    # Just in case there are array braces afterwards.
+    while ((($valid, $d) = &matchDecl) && $valid) { $decl .= $d; }
+    $parenPointerFunction = $decl;
+    $parenPointerFunction =~ s/^\s+//;	# Remove whitespace from beginning
+    $parenPointerFunction =~ s/\s+$//;	# Remove whitespace from end
+  }
+}
+
+# Parse template arguments
+sub matchAngleArgs {
+  
+  if (&matchRAngle) {
+    local ($args, $nest);
+    
+    $args = "&lt;";
+    $nest = 1;
+    
+    for (;;) {
+      if (&matchRAngle) { $nest++; $args .= "&lt;"; }
+      elsif (&matchLAngle) {
+	$nest--;
+	$args .= "&gt;";
+	last if !$nest;
+      }
+      elsif ((($valid, $d) = &matchChar) && $valid) { $args .= $d; }
+      else { last; }
+    }
+    return $args;
+  }
+  else { return ''; }
+}
+
+# convert tabs to spaces
+sub expandTabs {
+  local	($text) = @_;
+  local 	($n);
+  
+  while (($n = index($text,"\t")) >= 0) {
+    substr($text, $n, 1) = " " x ($tabSize-($n % $tabSize));
+  }
+  
+  return $text;
+}
+
+# Process a line of text from a "special" comment
+sub handleCommentLine {
+  local ($_) = @_;
+  
+  if ($docEmpty) {
+    # Eliminate blank lines at the head of the doc.
+    return if (/^\s*$/);
+  }
+  
+  # First, expand tabs.
+  $_ = &expandTabs( $_ );
+	
+  # Remove gratuitous \s*\s  (james)
+  s/(^|\n)\s*\*\s/$1/g;
+  
+  # If it's one of the standard tags
+  if (s/^\s*\@(see|package|version|author|param|return|result|exception|keywords|deffunc|defvar|heading|todo)\s*//) {
+    my $tag = $1;
+    $tag = 'return' if ($tag eq 'result');
+    
+    # for param and exception, split the param name and the text
+    # seperate them with tabs.
+    if ($tag eq "param" || $tag eq "exception") {
+      s/^\s*(\w+)\s*(.*)/\t$1\t$2/;
+    }
+    elsif ($tag eq "heading") {
+      # 'heading' is processed by the template, if at all.
+      $_ = "\@heading\t$_";
+      $tag = "description";
+    }
+    elsif ($tag eq 'todo') {
+      if ($todolist{ $srcfile } ne '') {
+	$todolist{ $srcfile } .= "\n";
+      }
+    }
+    
+    # If it's @deffunc or @defvar
+    if ($tag =~ /def(.*)/) {
+      
+      $type = $1;
+      
+      # @deffunc and @defvar force a comment to be written out as if there was a
+      # declaration.
+      # Designed for use with macros and other constructs I can't parse.
+      
+      if (/(\S+)\s+(.*)$/) {
+	$name = $1;
+	$decl = $2;
+	$dbname = &uniqueName( "$baseScope$name" );
+	
+	my $entry = { 'type'    => $type,
+		      'name'    => $name,
+		      'longname'=> $name,
+		      'fullname'=> "$name$decl",
+		      'scopename'=>"$baseScope$name",
+		      'uname'   => $dbname,
+		      'decl'    => $decl,
+		      'package' => $packageName };
+
+        bless $entry, MemberRecord;
+
+	if ($class) {
+	  $entry->{ 'class' } = "$context";
+	  $class->{ 'members' }{ $dbname } = $entry;
+	} 
+	else {
+	  $packages{ $packageName }{ 'globals' }{ $dbname } = $entry;
+	}
+	$docTag = 'description';
+	&dumpComments( $entry );
+	return;
+      }
+    }
+    elsif ($tag eq 'package') {
+      s/^\s*//;
+      s/\s*$//;
+      $packageName = $_;
+      $docTag = 'description';
+      return;
+    }
+    elsif ($tag eq 'author') {
+      $author = $_;
+      $docTag = 'description';
+      return;
+    }
+    elsif ($tag eq 'version') {
+      $version = $_;
+      $docTag = 'description';
+      return;
+    }
+    
+    $docTag = $tag;
+  }
+  elsif (/^\s*@\w+/) {
+    # any other line that begins with an @ should be inserted into the main
+    # description for later expansion.
+    $docTag = 'description';
+  }
+  
+  # "To-do" lists are handled specially, and not associated with a class.
+  if ($docTag eq 'todo') {
+    $todolist{ $srcfile } .= $_;
+    return;
+  }
+  
+  # Append to current doc tag, regardless of whether it's a new line
+  # or a continuation. Also mark this doc as non-empty.
+  $docTags{ $docTag } .= $_;
+  $docEmpty = 0;
+  
+  # @see doesn't persist.
+  if ($docTag eq 'see') { $docTag = 'description'; }
+  
+  # print STDERR ":$_";
+}
+
+# Clear doc tag information at end of class or file
+sub clearComments {
+  
+  $docTag = 'description';
+  $docEmpty = 1;
+  %docTags = ();
+}
+
+# Add doc tag information to current documented item
+sub dumpComments {
+  local ($hashref) = @_;
+  
+  if ($docEmpty == 0) {
+    
+    if ($author ne  '') { $hashref->{ 'author'  } = $author;  }
+    if ($version ne '') { $hashref->{ 'version' } = $version; }
+    $hashref->{ 'sourcefile' } = $srcfile;
+    
+    # Store the tags for this documentation into the global doc symbol table
+    foreach $key (keys %docTags) {
+      my $data = $docTags{ $key };
+
+      $data =~ s/\s*$//;
+      
+      $hashref->{ $key } = $data;
+    }
+  }
+  
+  &clearComments();
+}
+
+# Generate a unique name from the given name.
+sub uniqueName {
+  local ($name) = @_;
+  
+  # Duplicate doc entries need to be distinguished, so give them a different label.
+  while ($docs{ $name }) {
+    if ($name =~ /-(\d+)$/) {
+      $name = $` . "-" . ($1 + 1);
+    }
+    else { $name .= "-2"; }
+  }
+  
+  $docs{ $name } = 1;
+  return $name;
+}
+
+# Get the current class record.
+sub classRecord {
+  local ($className) = @_;
+  local ($pkg) = $classToPackage{ $className };
+  
+  if ($pkg) {
+    return $packages{ $pkg }{ 'classes' }{ $className };
+  }
+  return 0;
+}
+
+# Parse a declaration in the file
+sub parseDeclaration {
+  
+  local ($context) = @_;
+  local ($baseScope) = '';
+  local ($decl);
+  my ($token);
+  
+  if ($context) { $baseScope = $context . "::"; }
+  
+  &rdln;
+
+  if (!defined ($_)) { return 0; }
+  
+  if (s|^\s*//\*\s+||) {
+    # Special C++ comment
+    &handleCommentLine( $' );
+    $_ = ''; &rdln;
+  }	
+  elsif (s|^\s*//||) { 
+    # Ordinary C++ comment
+    $_ = '';
+    &rdln;
+  }
+  elsif (s|^\s*\/\*\*\s+||) {
+    # Special C comments
+    
+    s/\={3,}|\-{3,}|\*{3,}//;			# Eliminate banner strips
+    $text = '';
+    $docTag = 'description';
+    
+    # Special comment
+    while (!/\*\//) { &handleCommentLine( $_ ); $text .= $_; $_ = <FILE>; $linenumber++; if ($debug) { print STDERR "(1) $linenumber\n."; }}
+    s/\={3,}|\-{3,}|\*{3,}//;			# Eliminate banner strips
+    /\*\//;
+    &handleCommentLine( $` );
+    $text.= $`; $_ = $';
+  }
+  elsif (s|^\s*\/\*||) {
+    # Ordinary C comment
+    $text = "";
+    
+    while (!/\*\//) { $text .= $_; $_ = <FILE>; $linenumber++; if ($debug) { print STDERR "(2) $linenumber\n."; }}
+    /\*\//;
+    $text.= $`; $_ = $';
+  }
+  elsif ((($valid, $tag) = &matchKW( "template")) && $valid) {
+    # Template definition
+    $args = &matchAngleArgs;
+    &rdln;
+    
+    ##$tmplParams = $args; JAMES
+    $result = &parseDeclaration( $context );
+    ##$tmplParams = ''; JAMES
+    return $result;
+  }
+  elsif ((($valid, $tag) = &matchKW("class|struct")) && $valid) {
+    # Class or structure definition
+    local ($className,$class);
+    
+    if ((($valid, $className) = &matchID) && $valid) {
+      
+      return 1 if (&matchSemi);		# Only a struct tag
+      
+      # A class instance
+      if ((($valid,)=&matchID) && $valid) {
+	&matchSemi;
+	return 1;
+      }
+      
+      my $fullName = "$baseScope$className"; ##$tmplParams"; JAMES
+      # print STDERR "CLASS $fullName\n";
+      
+      my @bases = ();
+      
+      if (&matchColon) {
+	
+	for (;;) {
+	  my $p;
+	  &matchKW( "virtual" );
+	  $perm = "private";
+	  if ((($valid, $p) = &matchKW( "public|private|protected" )) && $valid) { $perm = $p; }
+	  &matchKW( "virtual" );
+	  
+	  last if !(  (($valid, $base) = &matchID) && $valid  );
+	  
+	  push @bases, $base;
+	  push @{ $subclasses{ $base } }, $fullName;
+	  # print STDERR " : $perm $base\n";
+	  last if !&matchComma;
+	}
+      }
+      
+      #	print STDERR "\n";
+      # print STDERR "parsing class $fullName\n";
+
+      if ($docEmpty == 0) {
+	$class = { 'type'    => $tag,
+		   'name'    => $fullName,
+		   'longname'=> "$tag $className",
+		   'fullname'=> "$tag $className",
+		   'scopename'=> "$tag $fullName",
+		   'uname'   => $fullName,
+		   'bases'   => \@bases,
+		   'package' => $packageName,
+		   'members' => {} };
+	
+	# print STDERR "$className: @bases\n";
+	
+	bless $class, ClassRecord;
+	
+	print STDERR "   parsing class $fullName\n";
+	# $classToPackage{ $className } = $packageName;
+	$classToPackage{ $fullName } = $packageName;
+	# $classList{ $className } = $class;
+	$classList{ $fullName } = $class;
+	$packages{ $packageName }{ 'classes' }{ $fullName } = $class;
+	&dumpComments( $class );
+      }
+      
+      if (&matchRBracket) {
+	local ($perm) = ("private");
+	
+	while (!&matchLBracket) {
+	  my $p;
+	  if ((($valid, $p) = &matchKW( "public\:|private\:|protected\:" )) && $valid) {
+	    $perm = $p;
+	  }
+	  else {
+	    &parseDeclaration( $fullName )
+	      || die "Unmatched brace! line = $linenumber\n";
+	  }
+	}
+	
+	&matchSemi;
+      }
+      
+      &clearComments;
+    }
+  }
+  elsif ( ((($valid,)=&matchKW( "enum")) && $valid) || ((($valid,)=&matchKW( "typedef" )) && $valid)) {
+    &skipToSemi;
+  }
+  elsif ((($valid,)=&matchKW( "friend\s*class" )) && $valid) {
+    &skipToSemi;
+  }
+  elsif ((($valid, $token) = &matchKW("extern\\s*\\\"C\\\"")) && $valid) {
+    &matchRBracket;
+    while (!&matchLBracket) {
+      &parseDeclaration( '' ) || die "Unmatched brace! line = $linenumber\n";
+    }
+    &matchSemi;
+  }
+  # elsif ($kw = &matchID) {
+  #   $type = "$kw ";
+  #
+  #   if ($kw =~/virtual|static|const|volatile/) {
+  #	$type .= &typ;
+  #   }
+  # }
+  elsif ((($valid, $decl) = &matchDecl) && $valid) {
+    my ($instanceClass) = "";
+    
+    # print STDERR "DECLARATION=$decl, REST=$_, baseScope=$baseScope\n";
+
+    return 1 if ($decl =~ /^\s*$/);
+
+    if (!($class)) {
+      if ($decl =~ s/(\S*\s*)(\S+)\:\:(\S+)\s*$/$1$3/) {
+        $instanceClass = $2;
+      }
+    }
+
+    # Eliminate in-line comments
+    &removeComment;
+    
+    # Check for multi-line declaration
+    while ((($valid, $d) = &matchDecl) && $valid) { $decl .= $d; }
+    
+    # Handle template args, but don't let operator overloading confuse us!
+    $tempArgs = '';
+    if (!($decl =~ /\boperator\b/) && ($tempArgs = &matchAngleArgs)) {
+      $tempArgs = $decl . $tempArgs;
+      $decl = '';
+      while ((($valid, $d) = &matchDecl) && $valid) { $decl .= $d; }
+    }
+    
+    # Look for (*name) syntax
+    &parseParenPointer;
+    
+    # Special handling for operator... syntax
+    $oper = "";
+    if ($decl =~ s/\boperator\b(.*)/operator/) {
+      $oper = $1;
+      $oper .= &matchOper;
+      # If, after all that there's no opers, then try a () operator
+      if (!($oper =~ /\S/)) { $oper .= &matchFuncOper; }
+    }
+
+    ($type,$mod,$decl) = $decl =~ /([\s\w]*)([\s\*\&]+\s?)(\~?\w+(\[.*\])*)/;
+    
+    if ($parenPointerFunction) {
+      $decl=$parenPointerFunction;
+    }
+    
+    $type = $tempArgs . $type;
+    $decl .= $oper;
+    
+    if ($mod =~ /\s/) { $type .= $mod; $mod = ""; }
+    
+    for (;;) {
+      
+      # print STDERR "Looping: $type/$mod/$decl\n";
+      
+      if (&matchRParen) {
+	$nest = 1;
+	$args = "";
+	
+	for (;;) {
+	  # print STDERR "Argloop $_\n";
+	  
+	  # Process argument lists.
+	  
+	  # Preserve spaces, eliminate in-line comments
+	  # REM: Change this to save inline comments and automatically
+	  # generate @param clauses
+	  s|//.*||;
+	  while (s/^(\s+)//) { $args .= " "; &rdln; }
+	  
+	  if (&matchRParen) { $nest++; $args .= "("; }
+	  elsif (&matchLParen) {
+	    $nest--;
+	    last if !$nest;
+	    $args .= ")";
+	  }
+	  elsif ((($valid, $d) = &matchKW( "[\,\=\.\:\-]" )) && $valid) { $args .= $d; }
+	  elsif ((($valid, $d) = &matchDecl) && $valid) { $args .= $d; }
+	  elsif ((($valid, $d) = &matchAngleArgs) && $valid) { $args .= $d; }
+	  elsif ((($valid, $d) = &matchString) && $valid) { $args .= "\"$d\""; }
+	  else { last; }
+	}
+				
+	# print STDERR "$type$mod$baseScope$decl($args);\n";
+	
+	&matchKW( "const" );
+	
+	# Search for any text within the name field
+	# if ($docTag && $decl =~ /\W*(~?\w*).*/)
+	if ($docEmpty == 0) {
+	  $type =~ s/^\s+//;
+	  $mod  =~ s/\&/\&amp;/g;
+	  $args =~ s/\&/\&amp;/g;
+	  $args =~ s/\s+/ /g;
+	  $dbname = &uniqueName( "$baseScope$decl" );
+	  
+	  my $entry = { 'type'    => 'func',
+			'name'    => $decl,
+			'longname'=> "$decl()",
+			'fullname'=> "$type$mod$decl($args)",
+			'scopename'=>"$type$mod$baseScope$decl($args)",
+			'uname'   => $dbname,
+			'decl'    => "$type$mod$decl($args)",
+			'package' => $packageName };
+	  
+	  bless $entry, MemberRecord;
+	  
+	  if ($class) {
+	    $entry->{ 'class' } = "$context";
+	    $class->{ 'members' }{ $dbname } = $entry;
+	  }
+	  elsif ($instanceClass) {
+	    $class = &classRecord ($instanceClass);
+	    if (!($class)) {
+	      print STDERR "WARNING: Skipping \"$instanceClass\:\:$decl\".  Class \"$instanceClass\" not declared ($linenumber).\n";
+	    } else {
+	      $entry->{ 'class' } = "$instanceClass";
+	      $class->{ 'members' }{ $dbname } = $entry;
+	      $class = 0;
+	    }
+	  }
+	  else {
+	    $packages{ $packageName }{ 'globals' }{ $dbname } = $entry;
+	  }
+	  &dumpComments( $entry );
+	}
+	else { &clearComments; }
+	
+	s|//.*||;
+	
+	# Constructor super-call syntax
+	if (&matchColon) {
+	  
+	  # Skip over it.
+	  for (;;) {
+	    &rdln;
+	    last if /^\s*(\{|\;)/;
+	    last if !((($valid,)=&matchAny) && $valid);
+	  }
+	}
+	
+	last if &matchSemi;
+	if (&matchRBracket) { &skipBody; last; }
+	last if !&matchComma;
+	last if !((($valid, $decl) = &matchDecl) && $valid);
+	
+	# Look for (*name) syntax
+	&parseParenPointer;
+	
+	$decl =~ s/^\s*//;
+	$oper = "";
+	if ($decl =~ /\boperator\b/) {
+	  $decl =~ s/\boperator\b(.*)/operator/;
+	  $oper = $1 . &matchOper;
+	}
+	($mod,$d) = $decl =~ /^\s*([\*\&]*)\s*(\~?\w+(\[.*\])*)/;
+	$decl .= $oper;
+	$decl = $d if $d ne "";
+      }
+      else {
+	s|//.*||;
+	
+	$final = 0;
+	
+	if ((($valid,)=&matchKW( "\=" )) && $valid) {
+	  for (;;) {
+	    
+	    if (&matchRBracket) {
+	      &skipBody;
+	      $final = 1;
+	      last;
+	    }
+	    
+	    if (&matchSemi) {
+	      $final = 1;
+	      last;
+	    }
+	    
+	    # var = new ... (...)
+	    if ((($valid,)=&matchKW("new")) && $valid) {
+	      &matchKW("[A-Za-z_0-9 ]*");
+	      if (&matchRParen) {
+	        &skipParenBody;
+	      }
+	    }
+	    
+	    # var = (.....) ...
+	    if (&matchRParen) {
+	      &skipParenBody;
+	    }
+	    
+	    # var = ... * ...
+	    &matchKW ("[\/\*\-\+]*");
+	    
+	    # var = "..."
+	    if ((($valid,) = &matchKW ("[\"]")) && $valid) {
+	      &skipString;
+	    }
+	    #&matchString;
+	    
+	    last if /^\s*,/;
+	    #last if !((($valid,)=&matchAny) && $valid);
+	    last if !((($valid,)=&matchKW("[A-Za-z_0-9 \-]*")) && $valid);
+	    if (&matchSemi) {
+	        $final = 1;
+	        last;
+	    }
+	  }
+	}
+	
+	s|//.*||;
+	
+	# void ~*&foo[];
+	# void foo[];
+	# void far*foo[];
+	# print STDERR "Decl: $type$mod$baseScope$decl;\n";
+
+	# Search for any text within the name field
+	if ($docEmpty == 0 && ($decl =~ /\W*(~?\w*).*/))
+	  {
+	    $mod  =~ s/\&/\&amp;/g;
+	    $name = $decl;
+	    
+	    $dbname = &uniqueName( "$baseScope$1" );
+	    
+	    my $entry = { 'type'     => 'var',
+			  'name'     => $1,
+			  'longname' => "$name",
+			  'fullname' => "$type$mod$decl",
+			  'scopename'=> "$baseScope$type$mod$decl",
+			  'uname'    => $dbname,
+			  'decl'     => "$type$mod$decl",
+			  'package'  => $packageName };
+	    
+	    bless $entry, MemberRecord;
+	    
+	    if ($class) {
+	      $entry->{ 'class' } = "$context";
+	      $class->{ 'members' }{ $dbname } = $entry;
+	    }
+	    else {
+	      $packages{ $packageName }{ 'globals' }{ $dbname } = $entry;
+	    }
+	    &dumpComments( $entry );
+	  }
+	else { &clearComments; }
+	
+	last if $final;
+	last if &matchSemi;
+	last if !&matchComma;
+	last if !((($valid, $decl) = &matchDecl) && $valid);
+	
+	# Look for (*name) syntax
+	&parseParenPointer;
+	
+	$decl =~ s/^\s*//;
+	($mod,$d) = $decl =~ /^\s*([\*\&]*)(\~?\w+(\[.*\])*)/;
+	$decl = $d if $d ne "";
+      }
+    }
+  }
+  elsif ($context ne "" && /^\s*\}/) {
+    # print STDERR "Popping!\n";
+    return 1;
+  }
+  elsif (&matchRBracket) {
+    &skipBody;
+  }
+  elsif ((($valid, $token) = &matchAny) && $valid) {
+    # Comment in for debugging
+    # print STDERR "token: $token \n";
+  }
+  else { return 0; }
+  
+  return 1;
+}
+
+# read a file into a string ( filename, default-value )
+sub readFile {
+  local ( $filename, $result ) = @_;
+  
+  if ($filename && open( FILE, $filename )) {
+    $result = "";
+    while (<FILE>) { $result .= $_; }
+    close( FILE );
+  }
+  return $result;
+}
+
+# Read the entire document template and translate into PERL code.
+sub readTemplate {
+  local ( $filename ) = @_;
+  $docTemplate = '';
+  $indent = '';
+  $literal = 1;  # We're in literal mode.
+  
+  if (!-e $filename) {
+    if (-e "./templates/$filename") { $filename = "./templates/$filename"; }
+    elsif (-e "../templates/$filename") { $filename = "../templates/$filename"; }
+    else { die "Could not find template '$filename'.\n"; }
+  }
+  
+  open( FILE, $filename ) || die "Error opening '$filename'.\n";
+  while (<FILE>) {
+    last if (/END/);
+    
+    # if we found a code entry.
+    for (;;) {
+      &expandTabs( $_ );
+      if ($literal) {
+	# Check for beginning of code block.
+	if (s/^(.*)\<\<//) {
+	  $line = $1; 
+	  if (substr( $line, 0, length( $indent ) ) eq $indent) {
+	    substr( $line, 0, length( $indent ) ) = '';
+	  }
+	  
+	  if ($line ne '') {
+	    $line =~ s/\"/\\\"/g;
+	    $line =~ s/\$\((\w+)\.(\w+)\)/\" \. \$$1->$2() \. \"/g;
+	    $docTemplate .= "${indent}print\"$line\";";
+	  }
+	  # else { $docTemplate .= "\n"; }
+	  $literal = 0;
+	}
+	else {
+	  if (substr( $_, 0, length( $indent ) ) eq $indent) {
+	    substr( $_, 0, length( $indent ) ) = "";
+	  }
+	  chop;
+	  s/\"/\\\"/g;
+	  s/\$\((\w+)\.(\w+)\)/\" \. \$$1->$2() \. \"/g;
+	  $_ = $indent . "print \"" . $_ . "\\n\";\n";
+	  last;
+	}
+      }
+      else {
+	# Check for beginning of literal block.
+	if (s/^(\s*)\>\>//) {
+	  $indent = $1;
+	  $literal = 1;
+	}
+	elsif (s/^(\s*)(.*)\>\>//) {
+	  $docTemplate .= "$indent$2";
+	  $literal = 1;
+	}
+	else {
+	  last;
+	}
+      }
+    }
+    
+    $docTemplate .= $_;
+  }
+  close( FILE );
+  # print $docTemplate;
+}
+
+# Functions intended to be called from doc template file.
+
+# Open a new output file
+sub file {
+  my $mfile = $_[ 0 ];
+  
+  open( STDOUT, ">$destPath$mfile" ) || die "Error writing to '$mfile'\n";
+}
+
+# return list of package objects
+sub packages {
+  my ($p, @r);
+  @r = ();
+  
+  foreach $p (sort keys %packages) {
+    push @r, $packages{ $p };
+  }
+  return @r;
+}
+
+# return list of source files which have to-do lists
+sub todolistFiles {
+  my ($p, @r);
+  @r = ();
+  
+  foreach $p (sort keys %todolist) {
+    push @r, $p;
+  }
+  return @r;
+}
+
+# return list of tab-delimited to-do-list texts.
+sub todolistEntries {
+  local $_ = $todolist{ $_[0] };
+  s/^\s+//;				# Remove whitespace from beginning
+  s/\s+$/\n/;				# Remove whitespace from end
+  return split( /\n/, $_ );
+}
+
+# Convert package name to URL.
+sub packageURL {
+  my $p = $_[0];
+  
+  if ($p eq 'General') { $p = '.general'; }
+  if ($p eq '') { $p = '.general'; }
+  
+  if (ref $packages{ $p }) {
+    return $packages{ $p }->url();
+  }
+  return 0;
+}
+
+# Get the see-also list for an object
+sub seealsoList {
+  my $self = shift;
+  my ($see, $name, $url, $p, @r);
+  @r = ();
+  
+  if (defined ($self->{ 'see' })) {
+    foreach $_ (split(/\n/,$self->{ 'see' })) {
+      
+      if (/^\<a\s+href/) { # if already an HREF.
+	$name = $_;
+	$url = 0;
+      }
+      elsif (/([^\#]*)\#(.*)/) { # If a package name is present
+	$url = &packageURL( $1 ) . '#' . $2;
+	$name = $2;
+      }
+      else {
+	$name = $_;
+	$url = "#$_";
+	
+	# This doesn't appear to do anything - so I commented it.  (james)
+	# Look up the package in the index and use it to construct the html filename.
+	#if (/^([^\:]*)\:\:(.*)/) {
+	#  $className = ($1 eq '') ? '' : $classToPackage{ $1 };
+	#  $p = $packageToFile{ $className };
+	#  if ($p ne '') {
+	#    $url = &packageURL( $1 ) . '#' . $_;
+	#  }
+	#}
+      }
+      
+      $url =~ s/^\:*//;		# Remove leading colons from name
+      $url =~ s/::/-/g;		# Replace :: with dash
+      
+      my $entry = { 'name' => $name,
+		    'url'  => $url };
+      
+      bless $entry, DocReference;
+      
+      push @r, $entry;
+    }
+  }
+  return @r;
+}
+
+# Class for parsed package
+package PackageRecord;
+
+sub classes {
+  my $self = shift;
+  my $classes = $self->{ 'classes' };
+  return map $classes->{ $_ }, (sort keys %$classes);
+}
+
+sub globals {
+  my $self = shift;
+  my $globals = $self->{ 'globals' };
+  return map $globals->{ $_ }, (sort keys %$globals);
+}
+
+sub globalvars {
+  my $self = shift;
+  my $globals = $self->{ 'globals' };
+  my ($p, @r);
+  @r = ();
+  
+  foreach $p (sort keys %$globals) {
+    my $m = $globals->{ $p };
+    if ($m->{ 'type' } ne 'func') { push @r, $m; }
+  }
+  return @r;
+}
+
+sub globalfuncs {
+  my $self = shift;
+  my $globals = $self->{ 'globals' };
+  my ($p, @r);
+  @r = ();
+  
+  foreach $p (sort keys %$globals) {
+    my $m = $globals->{ $p };
+    if ($m->{ 'type' } eq 'func') { push @r, $m; }
+  }
+  return @r;
+}
+
+sub name {
+  my $self = shift;
+  return $self->{ 'name' };
+}
+
+sub url {
+  my $self = shift;
+  return "default-pkg.html" if ($self->{ 'name' } eq '.general');
+  return $self->{ 'name' } . '.html';
+}
+
+sub anchor {
+  my $self = shift;
+  my $url = $self->{ 'name' };
+  return $url;
+}
+
+# Class for parsed class
+package ClassRecord;
+
+sub keywords    { return ${$_[0]}{ 'keywords' }; }
+sub author      { return ${$_[0]}{ 'author' }; }
+sub version     { return ${$_[0]}{ 'version' }; }
+sub name        { return ${$_[0]}{ 'name' }; }
+sub longname    { return ${$_[0]}{ 'longname' }; }
+sub fullname    { return ${$_[0]}{ 'fullname' }; }
+sub scopename   { return ${$_[0]}{ 'scopename' }; }
+sub sourcefile  { return ${$_[0]}{ 'sourcefile' }; }
+#sub description { return &::processDescription( ${$_[0]}{ 'description' } ); }
+sub description { return ${$_[0]}{ 'description' }; }
+sub seealso     { &::seealsoList( $_[0] ); }
+
+sub url {
+  my $self = shift;
+  return 0 unless $self->{ 'package' };
+  my $pname = ::packageURL( $self->{ 'package' } );
+  my $url = $self->{ 'uname' };
+  $url =~ s/::/-/g;
+  return "$pname#$url";
+}
+
+sub anchor {
+  my $self = shift;
+  my $url = $self->{ 'uname' };
+  $url =~ s/::/-/g;
+  return $url;
+}
+
+sub members {
+  my $self = shift;
+  my $members = $self->{ 'members' };
+  my ($p, @r);
+  @r = ();
+  
+  foreach $p (sort keys %$members) {
+    push @r, $members->{ $p };
+  }
+  return @r;
+}
+
+sub membervars {
+  my $self = shift;
+  my $members = $self->{ 'members' };
+  my ($p, @r);
+  @r = ();
+  
+  foreach $p (sort keys %$members) {
+    my $m = $members->{ $p };
+    if ($m->{ 'type' } ne 'func') { push @r, $m; }
+  }
+  return @r;
+}
+
+sub memberfuncs {
+  my $self = shift;
+  my $members = $self->{ 'members' };
+  my ($p, @r);
+  @r = ();
+  
+  foreach $p (sort keys %$members) {
+    my $m = $members->{ $p };
+    if ($m->{ 'type' } eq 'func') { push @r, $m; }
+  }
+  return @r;
+}
+
+sub baseclasses {
+  my $self = shift;
+  my $bases = $self->{ 'bases' };
+  my ($p, $class, @r);
+  @r = ();
+  
+  foreach $p (@$bases) {
+    
+    unless ($class = $::classList{ $p }) {
+      # It's one we don't know about, so just make something up
+      $class = { 'name'    => $p,
+		 'longname'=> "class $p",
+		 'fullname'=> "class $p",
+		 'scopename'=>"class $p",
+		 'uname'   => $p,
+		 'members' => {} };
+      
+      if ($::classToPackage{ $p }) {
+	$class->{ 'package' } = $::classToPackage{ $p };
+      }
+      
+      bless $class, ClassRecord;
+    }
+    push @r, $class;
+  }
+  return @r;
+}
+
+sub subclasses {
+  my $self = shift;
+  my $subs;
+  my ($p, $class, @r);
+  @r = ();
+  
+  if (defined ($self->{ 'subs' })) {
+    $subs = $self->{ 'subs' };
+    foreach $p (sort @$subs) {
+      $class = $::classList{ $p };
+      push @r, $class;
+    }
+  }
+  return @r;
+}
+
+# Class for parsed class member or global
+package MemberRecord;
+
+sub type         { return ${$_[0]}{ 'type' }; }
+sub keywords     { return ${$_[0]}{ 'keywords' }; }
+sub author       { return ${$_[0]}{ 'author' }; }
+sub version      { return ${$_[0]}{ 'version' }; }
+sub name         { return ${$_[0]}{ 'name' }; }
+sub longname     { return ${$_[0]}{ 'longname' }; }
+sub fullname     { return ${$_[0]}{ 'fullname' }; }
+sub scopename    { return ${$_[0]}{ 'scopename' }; }
+sub returnValue  { return ${$_[0]}{ 'return' }; }
+sub sourcefile   { return ${$_[0]}{ 'sourcefile' }; }
+sub description  { return ${$_[0]}{ 'description' }; }
+sub seealso      { &::seealsoList( $_[0] ); }
+
+sub url {
+  my $self = shift;
+  return 0 unless $self->{ 'package' };
+  my $pname = ::packageURL( $self->{ 'package' } );
+  my $url = $self->{ 'uname' };
+  $url =~ s/::/-/g;
+  return "$pname#$url";
+}
+
+sub anchor {
+  my $self = shift;
+  my $url = $self->{ 'uname' };
+  $url =~ s/::/-/g;
+  $url;
+}
+
+sub params {
+  my $self = shift;
+  my $params = $self->{ 'param' };
+  my @r;
+  @r = ();
+  
+  return 0 unless ($params);
+  
+  my @paramList = split( /\t/, $params );
+  
+  for ($i = 1; $i < $#paramList; $i += 2) {
+    my $entry = { 'name'        => $paramList[ $i ],
+		  'description' => $paramList[ $i + 1 ] };
+    
+    bless $entry, ArgRecord;
+    
+    push @r, $entry;
+  }
+  return @r;
+}
+
+sub exceptions {
+  my $self = shift;
+  my $params = $self->{ 'exception' };
+  my @r;
+  @r = ();
+  
+  return 0 unless ($params);
+  
+  my @paramList = split( /\t/, $params );
+  
+  for ($i = 1; $i < $#paramList; $i += 2) {
+    my $entry = { 'name'        => $paramList[ $i ],
+		  'description' => $paramList[ $i + 1 ] };
+    
+    bless $entry, ArgRecord;
+    
+    push @r, $entry;
+  }
+  return @r;
+}
+
+package ArgRecord;
+sub name        { return ${$_[0]}{ 'name' }; }
+sub description { return ${$_[0]}{ 'description' }; }
+
+package DocReference;
+sub name        { return ${$_[0]}{ 'name' }; }
+sub url         { return ${$_[0]}{ 'url' }; }
diff --git a/connectors/scandoc/template.pl b/connectors/scandoc/template.pl
new file mode 100755
index 0000000..fc75ed8
--- /dev/null
+++ b/connectors/scandoc/template.pl
@@ -0,0 +1,652 @@
+<<
+# Scandoc template file.
+#
+# This is an example set of templates that is designed to create several
+# different kinds of index files. It generates a "master index" which intended
+# for use with a frames browser; A "package index" which is the root page of
+# the index, and then "package files" containing documentation for all of the
+# classes within a single package.
+
+###############################################################################
+## Defaults for look and feel
+
+if ($project eq "") { $project = "WebApp Library"; }
+if ($copyright eq "") { $copyright = "2001, The Apache Software Foundation"; }
+if ($body_bgcolor eq "") { $body_bgcolor = '#ffffff'; }
+if ($body_text    eq "") { $body_text    = '#000000'; }
+if ($body_link    eq "") { $body_link    = '#0000ff'; }
+if ($body_vlink   eq "") { $body_vlink   = '#0000ff'; }
+if ($body_alink   eq "") { $body_alink   = '#0000ff'; }
+
+###############################################################################
+# Generate the frameset index                                                 #
+###############################################################################
+
+file "index.html";
+@p = packages();
+$_ = @p[0]->url;
+s/\s/_/g;
+y/[A-Z]/[a-z]/;
+>>
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; iso-8859-1">
+    <title>$project API Documentation</title>
+  </head>
+  <frameset cols="200,*">
+    <frameset rows="150,*">
+      <frame src="packages.html">
+      <frame src="pkg.$_" name="pkg">
+    </frameset>
+    <frame src="doc.$_" name="doc">
+  </frameset>
+</html>
+<<
+
+###############################################################################
+# Generate the packages list                                                  #
+###############################################################################
+
+file "packages.html";
+>>
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; iso-8859-1">
+    <title>$project - Packages List</title>
+  </head>
+  <body link="$body_link" vlink="$body_vlink" alink="$body_alink"
+        bgcolor="$body_bgcolor" text="$body_text">
+    <font size="+1" face="arial,helvetica,sans serif">
+      <nobr><b>$project packages:</b></nobr>
+    </font>
+    <table border="0" cellspacing="0" cellpadding="0">
+      <tr>
+        <td width="10">&nbsp;</td>
+        <td>&nbsp;</td>
+      </tr>
+<<
+
+foreach $p (packages()) {
+    $_ = $p->url;
+    s/\s/_/g;
+    y/[A-Z]/[a-z]/;
+    >>
+          <tr>
+            <td width="10">&nbsp;</td>
+            <td>
+              <font face="arial,helvetica,sans serif">
+                <nobr><a href="pkg.$_" target="pkg">$(p.name)</a></nobr>
+              </font>
+            </td>
+          </tr>
+    <<
+}
+
+>>
+    </table>
+    <hr>
+    <font size="-2" face="arial,helvetica,sans serif">
+      <div align="center">
+        Copyright &copy; $copyright.<br>
+        All Rights Reserved.<br>
+      </div>
+    </font>
+  </body>
+</html>
+<<
+
+###############################################################################
+# Generate the packages table of content for each package                     #
+###############################################################################
+
+foreach $p (packages()) {
+    $_ = $p->url;
+    s/\s/_/g;
+    y/[A-Z]/[a-z]/;
+    file "pkg.$_";
+    >>
+    <html>
+      <head>
+        <meta http-equiv="Content-Type" content="text/html; iso-8859-1">
+        <title>$project - Packages List</title>
+      </head>
+
+      <body link="$body_link" vlink="$body_vlink" alink="$body_alink"
+            bgcolor="$body_bgcolor" text="$body_text">
+        <font size="+1" face="arial,helvetica,sans serif">
+          <nobr><b><a href="doc.$_" target="doc">$(p.name)</a> package:</b></nobr>
+        </font>
+        <table border="0" cellspacing="0" cellpadding="0">
+          <tr>
+            <td width="10">&nbsp;</td>
+            <td>&nbsp;</td>
+          </tr>
+          <tr>
+            <td colspan="2">
+              <font face="arial,helvetica,sans serif">
+                <nobr>Classes:</nobr>
+              </font>
+            </td>
+          </tr>
+    <<
+
+    # Generate a list of classes included in this package
+    foreach $e ($p->classes()) {
+        $_ = $e->url;
+        s/\s/_/g;
+        y/[A-Z]/[a-z]/;
+        >>
+              <tr>
+                <td width="10">&nbsp;</td>
+                <td>
+                  <font face="arial,helvetica,sans serif">
+                    <nobr><a href="doc.$_" target="doc">$(e.fullname)</a></nobr>
+                  </font>
+                </td>
+              </tr>
+        <<
+    }
+
+    # Generate a list of all global functions included in this package
+    if ($p->globalfuncs()) {
+        >>
+              <tr>
+                <td colspan="2">
+                  <font face="arial,helvetica,sans serif">
+                    <nobr>Global Functions:</nobr>
+                  </font>
+                </td>
+              </tr>
+        <<
+        foreach $e ($p->globalfuncs()) {
+            $_ = $e->url;
+            s/\s/_/g;
+            y/[A-Z]/[a-z]/;
+            >>
+                  <tr>
+                    <td width="10">&nbsp;</td>
+                    <td>
+                      <font face="arial,helvetica,sans serif">
+                        <nobr><a href="doc.$_" target="doc">$(e.fullname)</a></nobr>
+                      </font>
+                    </td>
+                  </tr>
+            <<
+        }
+    } else {
+        >>
+              <tr>
+                <td colspan="2">
+                  <font face="arial,helvetica,sans serif">
+                    <nobr><i>No Global Functions defined.</i></nobr>
+                  </font>
+                </td>
+              </tr>
+        <<
+    }
+
+    # Generate a list of all global variables included in this package
+    if ($p->globalvars()) {
+        >>
+              <tr>
+                <td colspan="2">
+                  <font face="arial,helvetica,sans serif">
+                    <nobr>Global Variables:</nobr>
+                  </font>
+                </td>
+              </tr>
+        <<
+        foreach $e ($p->globalvars()) {
+            $_ = $e->url;
+            s/\s/_/g;
+            y/[A-Z]/[a-z]/;
+            >>
+                  <tr>
+                    <td width="10">&nbsp;</td>
+                    <td>
+                      <font face="arial,helvetica,sans serif">
+                        <nobr><a href="doc.$_" target="doc">$(e.fullname)</a></nobr>
+                      </font>
+                    </td>
+                  </tr>
+            <<
+        }
+    } else {
+        >>
+              <tr>
+                <td colspan="2">
+                  <font face="arial,helvetica,sans serif">
+                    <nobr><i>No Global Variables defined.</i></nobr>
+                  </font>
+                </td>
+              </tr>
+        <<
+    }
+
+    # Copyright statement at the bottom
+    >>
+        </table>
+        <hr>
+        <font size="-2" face="arial,helvetica,sans serif">
+          <div align="center">
+            Copyright &copy; $copyright.<br>
+            All Rights Reserved.<br>
+          </div>
+        </font>
+      </body>
+    </html>
+    <<
+}
+
+###############################################################################
+# Generate the packages detailed description for each package                 #
+###############################################################################
+foreach $p (packages()) {
+    $_ = $p->url;
+    s/\s/_/g;
+    y/[A-Z]/[a-z]/;
+    file "doc.$_";
+    >>
+    <html>
+      <head>
+        <meta http-equiv="Content-Type" content="text/html; iso-8859-1">
+        <title>$project - Packages List</title>
+      </head>
+      <body link="$body_link" vlink="$body_vlink" alink="$body_alink"
+            bgcolor="$body_bgcolor" text="$body_text">
+        <table width="100%" cellspacing="0" cellpadding="2" border="1">
+          <tr>
+            <td bgcolor="ccccff">
+              <table border="0" width="100%" cellspacing="0" cellpadding="0">
+                <tr>
+                  <td bgcolor="ccccff" align="left">
+                    <font size="+1" face="arial,helvetica,sans serif">
+                      <b>$project</b>
+                    </font>
+                  </td>
+                  <td bgcolor="ccccff" align="right">
+                    <font size="+1" face="arial,helvetica,sans serif">
+                      <b>$(p.name) package</b>
+                    </font>
+                  </td>
+                </tr>
+              </table>
+            </td>
+          </tr>
+        </table>
+        <br>
+        <table width="100%" cellspacing="0" cellpadding="2" border="1">
+          <tr>
+            <td bgcolor="eeeeff" align="left">
+              <font face="arial,helvetica,sans serif">
+                <b>Classes</b>
+              </font>
+            </td>
+          </tr>
+    <<
+
+    # Generate a TOC of all classes at the top of the page
+    foreach $e ($p->classes()) {
+        $_ = $e->url;
+        s/\s/_/g;
+        y/[A-Z]/[a-z]/;
+        >>
+              <tr>
+                <td>
+                  <dl>
+                    <dt>
+                      <font face="arial,helvetica,sans serif">
+                        <nobr><a href="doc.$_">$(e.fullname)</a></nobr>
+                      </font>
+                    </dt>
+        <<
+        if ($e->members()) {
+            foreach $m ($e->members()) {
+                $_ = $m->url;
+                s/\s/_/g;
+                y/[A-Z]/[a-z]/;
+                >>
+                            <dd>
+                              <font size="-1" face="arial,helvetica,sans serif">
+                                <nobr><a href="doc.$_">$(m.fullname)</a></nobr>
+                              </font>
+                            </dd>
+                <<
+            }
+        }
+        >>
+                  </dl>
+                </td>
+              </tr>
+        <<
+    }
+
+    # Continue with the global functions TOC
+    if ($p->globalfuncs()) {
+        >>
+            </table>
+            <br>
+            <table width="100%" cellspacing="0" cellpadding="2" border="1">
+              <tr>
+                <td bgcolor="eeeeff" align="left">
+                  <font face="arial,helvetica,sans serif">
+                    <b>Global Functions</b>
+                  </font>
+                </td>
+              </tr>
+        <<
+        foreach $e ($p->globalfuncs()) {
+            $_ = $e->url;
+            s/\s/_/g;
+            y/[A-Z]/[a-z]/;
+            >>
+                  <tr>
+                    <td>
+                      <font face="arial,helvetica,sans serif">
+                        <nobr><a href="doc.$_">$(e.fullname)</a></nobr>
+                      </font>
+                    </td>
+                  </tr>
+            <<
+        }
+    }
+
+    # And then finish with the global variables TOC
+    if ($p->globalvars()) {
+        >>
+            </table>
+            <br>
+            <table width="100%" cellspacing="0" cellpadding="2" border="1">
+              <tr>
+                <td bgcolor="eeeeff" align="left">
+                  <font face="arial,helvetica,sans serif">
+                    <b>Global Variables</b>
+                  </font>
+                </td>
+              </tr>
+        <<
+        foreach $e ($p->globalvars()) {
+            $_ = $e->url;
+            s/\s/_/g;
+            y/[A-Z]/[a-z]/;
+            >>
+                  <tr>
+                    <td>
+                      <font face="arial,helvetica,sans serif">
+                        <nobr><a href="doc.$_">$(e.fullname)</a></nobr>
+                      </font>
+                    </td>
+                  </tr>
+            <<
+        }
+    }
+
+    >>
+        </table>
+        <br>
+    <<
+
+    # Then generate the detail for each class in this package
+    foreach $e ($p->classes()) {
+        $_ = $e->url;
+        s/\s/_/g;
+        y/[A-Z]/[a-z]/;
+        >>
+            <table width="100%" cellspacing="0" cellpadding="2" border="1">
+              <tr>
+                <td bgcolor="ccccff" align="left">
+                  <font size="+1" face="arial,helvetica,sans serif">
+                    <a name="$(e.name)">
+                      <b>Class &quot;$(e.name)&quot; Detail:</b>
+                    </a>
+                  </font>
+                </td>
+              </tr>
+            </table>
+              <font size="+1" face="arial,helvetica,sans serif">
+                <b>$(e.name)</b>
+              </font>
+            <dl>
+              <dt><code>$(e.fullname) {</code></dt>
+        <<
+
+        # Generate a code-like representation of the class
+        if ($e->members()) {
+            foreach $m ($e->members()) {
+                >>
+                      <dd><code>$(m.fullname);</code></dd>
+                <<
+            }
+        }
+        >>
+              <dt><code>};</code></dt>
+            </dl>
+            <p>
+              <font face="arial,helvetica,sans serif">
+                $(e.description)
+              </font>
+            </p>
+        <<
+
+        # If we have functions, output them
+        if ($e->memberfuncs()) {
+            >>
+                <table width="100%" cellspacing="0" cellpadding="2" border="1">
+                  <tr>
+                    <td bgcolor="eeeeff" align="left">
+                      <font face="arial,helvetica,sans serif">
+                        <b>Class &quot;$(e.name)&quot; Functions:</b>
+                      </font>
+                    </td>
+                  </tr>
+                </table>
+            <<
+            foreach $m ($e->memberfuncs()) {
+                $_ = join("-",$e->name,$m->name);
+                s/\s/_/g;
+                y/[A-Z]/[a-z]/;
+                >>
+                    <a name="$_">
+                <<
+                &function($m);
+            }
+        }
+
+        # If we have variables, output them
+        if ($e->membervars()) {
+            >>
+                <table width="100%" cellspacing="0" cellpadding="2" border="1">
+                  <tr>
+                    <td bgcolor="eeeeff" align="left">
+                      <font face="arial,helvetica,sans serif">
+                        <b>Class &quot;$(e.name)&quot; Variables:</b>
+                      </font>
+                    </td>
+                  </tr>
+                </table>
+            <<
+            foreach $m ($e->membervars()) {
+                $_ = join("-",$e->name,$m->name);
+                s/\s/_/g;
+                y/[A-Z]/[a-z]/;
+                >>
+                    <a name="$_">
+                <<
+                &variable($m);
+            }
+        }
+    }
+
+    # Output detailed information for each global function
+    if ($p->globalfuncs()) {
+        >>
+            <table width="100%" cellspacing="0" cellpadding="2" border="1">
+              <tr>
+                <td bgcolor="ccccff" align="left">
+                  <font size="+1" face="arial,helvetica,sans serif">
+                    <b>Global Functions Detail:</b>
+                  </font>
+                </td>
+              </tr>
+            </table>
+        <<
+        foreach $e ($p->globalfuncs()) {
+            $_ = $e->name;
+            s/\s/_/g;
+            y/[A-Z]/[a-z]/;
+            >>
+                <a name="$_">
+            <<
+            &function($e);
+        }
+    }
+
+    # Then write detailed information for each global variable
+    if ($p->globalvars()) {
+        >>
+            <table width="100%" cellspacing="0" cellpadding="2" border="1">
+              <tr>
+                <td bgcolor="ccccff" align="left">
+                  <font size="+1" face="arial,helvetica,sans serif">
+                    <b>Global Variables Detail:</b>
+                  </font>
+                </td>
+              </tr>
+            </table>
+        <<
+        foreach $e ($p->globalvars()) {
+            $_ = $e->name;
+            s/\s/_/g;
+            y/[A-Z]/[a-z]/;
+            >>
+                <a name="$_">
+            <<
+            &variable($e);
+        }
+    }
+
+    >>
+        <font size="-2" face="arial,helvetica,sans serif">
+          <div align="center">
+            Copyright &copy; $copyright.<br>
+            All Rights Reserved.<br>
+            Generated with <a href="$scandocURL">ScanDoc
+            $majorVersion.$minorVersion</a> on $date
+          </div>
+        </font>
+      </body>
+    </html>
+    <<
+}
+
+# Write out the detailed description of a function
+sub function {
+    local ($m) = @_;
+
+    # Output the function name and description
+    >>
+        <font size="+1" face="arial,helvetica,sans serif">
+          <b>$(m.name)</b>
+        </font>
+        <dl>
+          <dt><code>$(m.fullname);</code></dt>
+          <dd>
+            <dl>
+              <dt>
+                <font face="arial,helvetica,sans serif">
+                  $(m.description)
+                </font>
+              </dt>
+    <<
+
+    # Process all parameters (one by one)
+    if ($m->params()) {
+        >>
+                  <dt>
+                    <font face="arial,helvetica,sans serif">
+                      <b>Parameters</b>
+                    </font>
+                  </dt>
+        <<
+        foreach $a ($m->params()) {
+            >>
+                      <dd>
+                        <code>$(a.name)</code> -
+                        <font face="arial,helvetica,sans serif">
+                          $(a.description)
+                        </font>
+                      </dd>
+            <<
+        }
+    }
+
+    # Check for a return value
+    if ($m->returnValue()) {
+        >>
+                  <dt>
+                    <font face="arial,helvetica,sans serif">
+                      <b>Return Value</b>
+                    </font>
+                  </dt>
+                  <dd>
+                    <font face="arial,helvetica,sans serif">
+                      $(m.returnValue)
+                    </font>
+                  </dd>
+        <<
+    }
+
+    # Dig in for exceptions
+    if ($m->exceptions()) {
+        >>
+                  <dt>
+                    <font face="arial,helvetica,sans serif">
+                      <b>Exceptions</b>
+                    </font>
+                  </dt>
+        <<
+        foreach $a ($m->exceptions()) {
+            >>
+                      <dd>
+                        <code>$(a.name)</code>
+                        <font face="arial,helvetica,sans serif">
+                          $(a.description)
+                        </font>
+                      </dd>
+            <<
+        }
+    }
+
+    # Close the list
+    >>
+            </dl>
+          </dd>
+        </dl>
+        <hr>
+    <<
+}
+
+# Write out the detailed description of a variable
+sub variable {
+    local ($m) = @_;
+
+    # Output the variable name and description
+    >>
+        <font size="+1" face="arial,helvetica,sans serif">
+          <b>$(m.name)</b>
+        </font>
+        <dl>
+          <dt><code>$(m.fullname);</code></dt>
+          <dd>
+            <dl>
+              <dt>
+                <font face="arial,helvetica,sans serif">
+                  $(m.description)
+                </font>
+              </dt>
+            </dl>
+          </dd>
+        </dl>
+        <hr>
+    <<
+}
diff --git a/connectors/util/.cvsignore b/connectors/util/.cvsignore
new file mode 100644
index 0000000..9ef9604
--- /dev/null
+++ b/connectors/util/.cvsignore
@@ -0,0 +1,2 @@
+build
+
diff --git a/connectors/util/README.txt b/connectors/util/README.txt
new file mode 100644
index 0000000..c41cb82
--- /dev/null
+++ b/connectors/util/README.txt
@@ -0,0 +1,8 @@
+This subpackage contains a set of common utilities copied out
+of tomcat 3.3.
+
+It probably doesn't belong in jakarta-tomcat-connectors, but perhaps
+in something like jakarta-tomcat-util, or something.... [seguin].
+
+To build, simply run ant.  The default target creates tomcat-util.jar
+int ./build/lib.
diff --git a/connectors/util/build.properties.sample b/connectors/util/build.properties.sample
new file mode 100644
index 0000000..c48fee3
--- /dev/null
+++ b/connectors/util/build.properties.sample
@@ -0,0 +1,32 @@
+# -------------------------------------------------------------------
+# build.properties.sample
+#
+# This is an example "build.properties" file, used to customize 
+# building Jakarta Tomcat Connectors Util for your local environment.  
+# Make any changes you need, and rename this file to 
+# "build.properties" 
+#
+# $Id$
+# -------------------------------------------------------------------
+
+
+# -------------------------------------------------------------------
+# CONFIGURATION OPTIONS
+# -------------------------------------------------------------------
+
+# 
+
+
+# -------------------------------------------------------------------
+# EXTERNAL DEPENDENCIES 
+# -------------------------------------------------------------------
+
+# ----- (optional) Java Secure Sockets Extension (JSSE), v1.0.2 or later -----
+jsse.home=${base.path}/jsse1.0.2
+jsse.lib=${jsse.home}/lib
+
+
+# ----- (optional) PureTLS Extension -----
+puretls.home=${base.path}/puretls-0.9b2
+puretls.lib=${puretls.home}/build
+puretls.jar=${puretls.lib}/puretls.jar
diff --git a/connectors/util/build.xml b/connectors/util/build.xml
new file mode 100644
index 0000000..66412bf
--- /dev/null
+++ b/connectors/util/build.xml
@@ -0,0 +1,121 @@
+<project name="tomcat-util" default="build-main" basedir=".">
+
+    <!-- ===================== Initialize Property Values =================== -->
+    <property file="${user.home}/build.properties"/>
+    <property file="build.properties"/>
+    <property file="../build.properties.default"/>
+    <property file="build.properties.sample"/>
+
+    <property name="tomcat-util.build" value="${basedir}/build"/>
+
+    <property name="jsse.home" location="${base.path}/jsse1.0.2"/>
+    <property name="jsse.lib" location="${jsse.home}/lib"/>
+    <property name="jsse.jar" location="${jsse.lib}/jsse.jar"/>
+    <property name="jnet.jar" location="${jsse.lib}/jnet.jar"/>
+    <property name="jcert.jar" location="${jsse.lib}/jcert.jar"/>
+
+    <property name="puretls.home" location="${base.path}/puretls"/>
+    <property name="puretls.lib" location="${puretls.home}"/>
+    <property name="puretls.jar" location="${puretls.lib}/puretls.jar"/>
+    
+    <property name="commons-logging.jar" location="../lib/commons-logging.jar" />
+    <property name="jmx.jar" location="../lib/mx4j.jar" />
+    <property name="tomcat-util.lib" value="${tomcat-util.build}/lib" />
+    <property name="tomcat-util.jar" value="${tomcat-util.lib}/tomcat-util.jar" />
+
+    <path id="compile.classpath">
+        <pathelement location="${jmx.jar}" />
+        <pathelement location="${jsse.jar}" />
+        <pathelement location="${java.home}/lib/jsse.jar" />
+        <pathelement location="${jnet.jar}" />
+        <pathelement location="${jcert.jar}" />
+        <pathelement location="${puretls.jar}" />
+        <pathelement location="${commons-logging.jar}" />
+        <pathelement location="${commons-modeler.jar}" />
+    </path>
+
+    <target name="detect">
+        <available property="jsse.present" classname="javax.net.ssl.SSLSocket"/>
+        <available property="jmx.present" file="${jmx.jar}"/>
+        <available property="puretls.present" file="${puretls.jar}"/>
+        <available property="commons-logging.present" file="${commons-logging.jar}"/>
+        <available property="modeler.present" file="${commons-modeler.jar}"/>
+	<available property="jdk1.4.present" classname="java.lang.CharSequence" />
+    </target>
+
+    <target name="build-prepare" depends="detect">
+        <mkdir dir="${tomcat-util.build}"/>
+	<mkdir dir="${tomcat-util.build}/classes"/>
+	<mkdir dir="${tomcat-util.lib}"/>
+    </target>
+
+    <target name="build-main" depends="build-prepare">
+        <echo message="----- Java-utils -----" />
+        <echo message="-- puretls.present = ${puretls.present}" />
+        <echo message="-- jsse.present = ${jsse.present} ${jsse.jar}"/>
+        <echo message="-- commons-logging = ${commons-logging.present}"/>
+        <echo message="-- jmx = ${jmx.present} ${jmx.jar}"/>
+        <echo message="-- modeler = ${modeler.present} ${commons-modeler.jar}"/>
+        <echo message="-- JDK14 = ${jdk1.4.present}"/>
+        <javac srcdir="java"
+	       destdir="${tomcat-util.build}/classes"
+	       deprecation="${compile.deprecation}"
+	       debug="${compile.debug}"
+	       optimize="off"
+	       verbose="off"
+	       excludes="**/CVS/**">
+            <classpath refid="compile.classpath"/>
+            <exclude name="**/util/net/jsse/*" unless="jsse.present"/>
+            <exclude name="**/util/log/CommonLogHandler.java" unless="commons-logging.present"/>
+            <exclude name="**/util/net/puretls/*" unless="puretls.present"/>
+            <exclude name="**/util/mx/*" unless="jmx.present"/>
+            <exclude name="**/util/threads/ThreadPoolMX*" unless="modeler.present"/>
+            <exclude name="**/util/compat/Jdk14Compat.java" unless="jdk1.4.present" />
+            <exclude name="**/util/net/jsse/JSSE14*" unless="jdk1.4.present" />
+            <exclude name="**/util/net/jsse/JSSEKeyManager.java" unless="jdk1.4.present" />
+        </javac>
+
+	<!-- Copy static resource files -->
+	<copy todir="${tomcat-util.build}/classes">
+	    <fileset dir="java">
+	    	<include name="**/*.properties"/>
+	    </fileset>
+        </copy>
+
+	<!-- Copy static resource files -->
+	<copy todir="${tomcat-util.build}/classes">
+	    <fileset dir="java">
+	    	<include name="**/*.properties"/>
+	    </fileset>
+        </copy>
+
+	<jar jarfile="${tomcat-util.jar}"
+             index="true"
+             basedir="${tomcat-util.build}/classes"
+             manifest="java/tomcat-util.manifest" >
+            <include name="org/apache/tomcat/util/**"/>
+        </jar>
+
+    </target>
+
+    <!-- ================ BUILD: Create Tomcat-Util Javadocs =================== -->
+    <target name="javadoc" unless="docs-uptodate">
+        <delete dir="${tomcat-util.build}/javadoc"/>
+	<mkdir dir="${tomcat-util.build}/javadoc"/>
+	<javadoc packagenames="org.apache.tomcat.util.*"
+               sourcepath="java"
+                  destdir="${tomcat-util.build}/javadoc"
+                   author="true"
+                  version="true"
+              windowtitle="Tomcat Utilities Documentation"
+                 doctitle="Tomcat Utilities"
+                   bottom="Copyright &#169; 2001 Apache Software Foundation.  All Rights Reserved.">
+            <classpath refid="compile.classpath"/>
+        </javadoc>
+    </target>
+
+
+    <target name="clean">
+        <delete dir="${tomcat-util.build}"/>
+    </target>
+</project>
diff --git a/connectors/util/java/org/apache/tomcat/util/IntrospectionUtils.java b/connectors/util/java/org/apache/tomcat/util/IntrospectionUtils.java
new file mode 100644
index 0000000..2a35272
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/IntrospectionUtils.java
@@ -0,0 +1,988 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+// Depends: JDK1.1
+
+/**
+ * Utils for introspection and reflection
+ */
+public final class IntrospectionUtils {
+
+    /**
+     * Call execute() - any ant-like task should work
+     */
+    public static void execute(Object proxy, String method) throws Exception {
+        Method executeM = null;
+        Class c = proxy.getClass();
+        Class params[] = new Class[0];
+        //	params[0]=args.getClass();
+        executeM = findMethod(c, method, params);
+        if (executeM == null) {
+            throw new RuntimeException("No execute in " + proxy.getClass());
+        }
+        executeM.invoke(proxy, (Object[]) null);//new Object[] { args });
+    }
+
+    /**
+     * Call void setAttribute( String ,Object )
+     */
+    public static void setAttribute(Object proxy, String n, Object v)
+            throws Exception {
+        if (proxy instanceof AttributeHolder) {
+            ((AttributeHolder) proxy).setAttribute(n, v);
+            return;
+        }
+
+        Method executeM = null;
+        Class c = proxy.getClass();
+        Class params[] = new Class[2];
+        params[0] = String.class;
+        params[1] = Object.class;
+        executeM = findMethod(c, "setAttribute", params);
+        if (executeM == null) {
+            System.out.println("No setAttribute in " + proxy.getClass());
+            return;
+        }
+        if (false)
+            System.out.println("Setting " + n + "=" + v + "  in " + proxy);
+        executeM.invoke(proxy, new Object[] { n, v });
+        return;
+    }
+
+    /**
+     * Call void getAttribute( String )
+     */
+    public static Object getAttribute(Object proxy, String n) throws Exception {
+        Method executeM = null;
+        Class c = proxy.getClass();
+        Class params[] = new Class[1];
+        params[0] = String.class;
+        executeM = findMethod(c, "getAttribute", params);
+        if (executeM == null) {
+            System.out.println("No getAttribute in " + proxy.getClass());
+            return null;
+        }
+        return executeM.invoke(proxy, new Object[] { n });
+    }
+
+    /**
+     * Construct a URLClassLoader. Will compile and work in JDK1.1 too.
+     */
+    public static ClassLoader getURLClassLoader(URL urls[], ClassLoader parent) {
+        try {
+            Class urlCL = Class.forName("java.net.URLClassLoader");
+            Class paramT[] = new Class[2];
+            paramT[0] = urls.getClass();
+            paramT[1] = ClassLoader.class;
+            Method m = findMethod(urlCL, "newInstance", paramT);
+            if (m == null)
+                return null;
+
+            ClassLoader cl = (ClassLoader) m.invoke(urlCL, new Object[] { urls,
+                    parent });
+            return cl;
+        } catch (ClassNotFoundException ex) {
+            // jdk1.1
+            return null;
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            return null;
+        }
+    }
+
+    public static String guessInstall(String installSysProp,
+            String homeSysProp, String jarName) {
+        return guessInstall(installSysProp, homeSysProp, jarName, null);
+    }
+
+    /**
+     * Guess a product install/home by analyzing the class path. It works for
+     * product using the pattern: lib/executable.jar or if executable.jar is
+     * included in classpath by a shell script. ( java -jar also works )
+     * 
+     * Insures both "install" and "home" System properties are set. If either or
+     * both System properties are unset, "install" and "home" will be set to the
+     * same value. This value will be the other System property that is set, or
+     * the guessed value if neither is set.
+     */
+    public static String guessInstall(String installSysProp,
+            String homeSysProp, String jarName, String classFile) {
+        String install = null;
+        String home = null;
+
+        if (installSysProp != null)
+            install = System.getProperty(installSysProp);
+
+        if (homeSysProp != null)
+            home = System.getProperty(homeSysProp);
+
+        if (install != null) {
+            if (home == null)
+                System.getProperties().put(homeSysProp, install);
+            return install;
+        }
+
+        // Find the directory where jarName.jar is located
+
+        String cpath = System.getProperty("java.class.path");
+        String pathSep = System.getProperty("path.separator");
+        StringTokenizer st = new StringTokenizer(cpath, pathSep);
+        while (st.hasMoreTokens()) {
+            String path = st.nextToken();
+            //	    log( "path " + path );
+            if (path.endsWith(jarName)) {
+                home = path.substring(0, path.length() - jarName.length());
+                try {
+                    if ("".equals(home)) {
+                        home = new File("./").getCanonicalPath();
+                    } else if (home.endsWith(File.separator)) {
+                        home = home.substring(0, home.length() - 1);
+                    }
+                    File f = new File(home);
+                    String parentDir = f.getParent();
+                    if (parentDir == null)
+                        parentDir = home; // unix style
+                    File f1 = new File(parentDir);
+                    install = f1.getCanonicalPath();
+                    if (installSysProp != null)
+                        System.getProperties().put(installSysProp, install);
+                    if (home == null && homeSysProp != null)
+                        System.getProperties().put(homeSysProp, install);
+                    return install;
+                } catch (Exception ex) {
+                    ex.printStackTrace();
+                }
+            } else {
+                String fname = path + (path.endsWith("/") ? "" : "/")
+                        + classFile;
+                if (new File(fname).exists()) {
+                    try {
+                        File f = new File(path);
+                        String parentDir = f.getParent();
+                        if (parentDir == null)
+                            parentDir = path; // unix style
+                        File f1 = new File(parentDir);
+                        install = f1.getCanonicalPath();
+                        if (installSysProp != null)
+                            System.getProperties().put(installSysProp, install);
+                        if (home == null && homeSysProp != null)
+                            System.getProperties().put(homeSysProp, install);
+                        return install;
+                    } catch (Exception ex) {
+                        ex.printStackTrace();
+                    }
+                }
+            }
+        }
+
+        // if install directory can't be found, use home as the default
+        if (home != null) {
+            System.getProperties().put(installSysProp, home);
+            return home;
+        }
+
+        return null;
+    }
+
+    /**
+     * Debug method, display the classpath
+     */
+    public static void displayClassPath(String msg, URL[] cp) {
+        System.out.println(msg);
+        for (int i = 0; i < cp.length; i++) {
+            System.out.println(cp[i].getFile());
+        }
+    }
+
+    public static String PATH_SEPARATOR = System.getProperty("path.separator");
+
+    /**
+     * Adds classpath entries from a vector of URL's to the "tc_path_add" System
+     * property. This System property lists the classpath entries common to web
+     * applications. This System property is currently used by Jasper when its
+     * JSP servlet compiles the Java file for a JSP.
+     */
+    public static String classPathAdd(URL urls[], String cp) {
+        if (urls == null)
+            return cp;
+
+        for (int i = 0; i < urls.length; i++) {
+            if (cp != null)
+                cp += PATH_SEPARATOR + urls[i].getFile();
+            else
+                cp = urls[i].getFile();
+        }
+        return cp;
+    }
+
+    /**
+     * Find a method with the right name If found, call the method ( if param is
+     * int or boolean we'll convert value to the right type before) - that means
+     * you can have setDebug(1).
+     */
+    public static void setProperty(Object o, String name, String value) {
+        if (dbg > 1)
+            d("setProperty(" + o.getClass() + " " + name + "=" + value + ")");
+
+        String setter = "set" + capitalize(name);
+
+        try {
+            Method methods[] = findMethods(o.getClass());
+            Method setPropertyMethod = null;
+
+            // First, the ideal case - a setFoo( String ) method
+            for (int i = 0; i < methods.length; i++) {
+                Class paramT[] = methods[i].getParameterTypes();
+                if (setter.equals(methods[i].getName()) && paramT.length == 1
+                        && "java.lang.String".equals(paramT[0].getName())) {
+
+                    methods[i].invoke(o, new Object[] { value });
+                    return;
+                }
+            }
+
+            // Try a setFoo ( int ) or ( boolean )
+            for (int i = 0; i < methods.length; i++) {
+                boolean ok = true;
+                if (setter.equals(methods[i].getName())
+                        && methods[i].getParameterTypes().length == 1) {
+
+                    // match - find the type and invoke it
+                    Class paramType = methods[i].getParameterTypes()[0];
+                    Object params[] = new Object[1];
+
+                    // Try a setFoo ( int )
+                    if ("java.lang.Integer".equals(paramType.getName())
+                            || "int".equals(paramType.getName())) {
+                        try {
+                            params[0] = new Integer(value);
+                        } catch (NumberFormatException ex) {
+                            ok = false;
+                        }
+                    // Try a setFoo ( long )
+                    }else if ("java.lang.Long".equals(paramType.getName())
+                                || "long".equals(paramType.getName())) {
+                            try {
+                                params[0] = new Long(value);
+                            } catch (NumberFormatException ex) {
+                                ok = false;
+                            }
+
+                        // Try a setFoo ( boolean )
+                    } else if ("java.lang.Boolean".equals(paramType.getName())
+                            || "boolean".equals(paramType.getName())) {
+                        params[0] = new Boolean(value);
+
+                        // Try a setFoo ( InetAddress )
+                    } else if ("java.net.InetAddress".equals(paramType
+                            .getName())) {
+                        try {
+                            params[0] = InetAddress.getByName(value);
+                        } catch (UnknownHostException exc) {
+                            d("Unable to resolve host name:" + value);
+                            ok = false;
+                        }
+
+                        // Unknown type
+                    } else {
+                        d("Unknown type " + paramType.getName());
+                    }
+
+                    if (ok) {
+                        methods[i].invoke(o, params);
+                        return;
+                    }
+                }
+
+                // save "setProperty" for later
+                if ("setProperty".equals(methods[i].getName())) {
+                    setPropertyMethod = methods[i];
+                }
+            }
+
+            // Ok, no setXXX found, try a setProperty("name", "value")
+            if (setPropertyMethod != null) {
+                Object params[] = new Object[2];
+                params[0] = name;
+                params[1] = value;
+                setPropertyMethod.invoke(o, params);
+            }
+
+        } catch (IllegalArgumentException ex2) {
+            System.err.println("IAE " + o + " " + name + " " + value);
+            ex2.printStackTrace();
+        } catch (SecurityException ex1) {
+            if (dbg > 0)
+                d("SecurityException for " + o.getClass() + " " + name + "="
+                        + value + ")");
+            if (dbg > 1)
+                ex1.printStackTrace();
+        } catch (IllegalAccessException iae) {
+            if (dbg > 0)
+                d("IllegalAccessException for " + o.getClass() + " " + name
+                        + "=" + value + ")");
+            if (dbg > 1)
+                iae.printStackTrace();
+        } catch (InvocationTargetException ie) {
+            if (dbg > 0)
+                d("InvocationTargetException for " + o.getClass() + " " + name
+                        + "=" + value + ")");
+            if (dbg > 1)
+                ie.printStackTrace();
+        }
+    }
+
+    public static Object getProperty(Object o, String name) {
+        String getter = "get" + capitalize(name);
+
+        try {
+            Method methods[] = findMethods(o.getClass());
+            Method getPropertyMethod = null;
+
+            // First, the ideal case - a getFoo() method
+            for (int i = 0; i < methods.length; i++) {
+                Class paramT[] = methods[i].getParameterTypes();
+                if (getter.equals(methods[i].getName()) && paramT.length == 0) {
+                    return methods[i].invoke(o, (Object[]) null);
+                }
+
+                if ("getProperty".equals(methods[i].getName())) {
+                    getPropertyMethod = methods[i];
+                }
+                if ("getAttribute".equals(methods[i].getName())) {
+                    getPropertyMethod = methods[i];
+                }
+            }
+
+            // Ok, no setXXX found, try a getProperty("name")
+            if (getPropertyMethod != null) {
+                Object params[] = new Object[1];
+                params[0] = name;
+                getPropertyMethod.invoke(o, params);
+            }
+
+        } catch (IllegalArgumentException ex2) {
+            System.err.println("IAE " + o + " " + name);
+            ex2.printStackTrace();
+        } catch (SecurityException ex1) {
+            if (dbg > 0)
+                d("SecurityException for " + o.getClass() + " " + name + ")");
+            if (dbg > 1)
+                ex1.printStackTrace();
+        } catch (IllegalAccessException iae) {
+            if (dbg > 0)
+                d("IllegalAccessException for " + o.getClass() + " " + name
+                        + ")");
+            if (dbg > 1)
+                iae.printStackTrace();
+        } catch (InvocationTargetException ie) {
+            if (dbg > 0)
+                d("InvocationTargetException for " + o.getClass() + " " + name
+                        + ")");
+            if (dbg > 1)
+                ie.printStackTrace();
+        }
+        return null;
+    }
+
+    /** 
+     */
+    public static void setProperty(Object o, String name) {
+        String setter = "set" + capitalize(name);
+        try {
+            Method methods[] = findMethods(o.getClass());
+            Method setPropertyMethod = null;
+            // find setFoo() method
+            for (int i = 0; i < methods.length; i++) {
+                Class paramT[] = methods[i].getParameterTypes();
+                if (setter.equals(methods[i].getName()) && paramT.length == 0) {
+                    methods[i].invoke(o, new Object[] {});
+                    return;
+                }
+            }
+        } catch (Exception ex1) {
+            if (dbg > 0)
+                d("Exception for " + o.getClass() + " " + name);
+            if (dbg > 1)
+                ex1.printStackTrace();
+        }
+    }
+
+    /**
+     * Replace ${NAME} with the property value
+     * 
+     * @deprecated Use the explicit method
+     */
+    public static String replaceProperties(String value, Object getter) {
+        if (getter instanceof Hashtable)
+            return replaceProperties(value, (Hashtable) getter, null);
+
+        if (getter instanceof PropertySource) {
+            PropertySource src[] = new PropertySource[] { (PropertySource) getter };
+            return replaceProperties(value, null, src);
+        }
+        return value;
+    }
+
+    /**
+     * Replace ${NAME} with the property value
+     */
+    public static String replaceProperties(String value, Hashtable staticProp,
+            PropertySource dynamicProp[]) {
+        StringBuffer sb = new StringBuffer();
+        int prev = 0;
+        // assert value!=nil
+        int pos;
+        while ((pos = value.indexOf("$", prev)) >= 0) {
+            if (pos > 0) {
+                sb.append(value.substring(prev, pos));
+            }
+            if (pos == (value.length() - 1)) {
+                sb.append('$');
+                prev = pos + 1;
+            } else if (value.charAt(pos + 1) != '{') {
+                sb.append(value.charAt(pos + 1));
+                prev = pos + 2; // XXX
+            } else {
+                int endName = value.indexOf('}', pos);
+                if (endName < 0) {
+                    sb.append(value.substring(pos));
+                    prev = value.length();
+                    continue;
+                }
+                String n = value.substring(pos + 2, endName);
+                String v = null;
+                if (staticProp != null) {
+                    v = (String) ((Hashtable) staticProp).get(n);
+                }
+                if (v == null && dynamicProp != null) {
+                    for (int i = 0; i < dynamicProp.length; i++) {
+                        v = dynamicProp[i].getProperty(n);
+                        if (v != null) {
+                            break;
+                        }
+                    }
+                }
+                if (v == null)
+                    v = "${" + n + "}";
+
+                sb.append(v);
+                prev = endName + 1;
+            }
+        }
+        if (prev < value.length())
+            sb.append(value.substring(prev));
+        return sb.toString();
+    }
+
+    /**
+     * Reverse of Introspector.decapitalize
+     */
+    public static String capitalize(String name) {
+        if (name == null || name.length() == 0) {
+            return name;
+        }
+        char chars[] = name.toCharArray();
+        chars[0] = Character.toUpperCase(chars[0]);
+        return new String(chars);
+    }
+
+    public static String unCapitalize(String name) {
+        if (name == null || name.length() == 0) {
+            return name;
+        }
+        char chars[] = name.toCharArray();
+        chars[0] = Character.toLowerCase(chars[0]);
+        return new String(chars);
+    }
+
+    // -------------------- Class path tools --------------------
+
+    /**
+     * Add all the jar files in a dir to the classpath, represented as a Vector
+     * of URLs.
+     */
+    public static void addToClassPath(Vector cpV, String dir) {
+        try {
+            String cpComp[] = getFilesByExt(dir, ".jar");
+            if (cpComp != null) {
+                int jarCount = cpComp.length;
+                for (int i = 0; i < jarCount; i++) {
+                    URL url = getURL(dir, cpComp[i]);
+                    if (url != null)
+                        cpV.addElement(url);
+                }
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    public static void addToolsJar(Vector v) {
+        try {
+            // Add tools.jar in any case
+            File f = new File(System.getProperty("java.home")
+                    + "/../lib/tools.jar");
+
+            if (!f.exists()) {
+                // On some systems java.home gets set to the root of jdk.
+                // That's a bug, but we can work around and be nice.
+                f = new File(System.getProperty("java.home") + "/lib/tools.jar");
+                if (f.exists()) {
+                    System.out.println("Detected strange java.home value "
+                            + System.getProperty("java.home")
+                            + ", it should point to jre");
+                }
+            }
+            URL url = new URL("file", "", f.getAbsolutePath());
+
+            v.addElement(url);
+        } catch (MalformedURLException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    /**
+     * Return all files with a given extension in a dir
+     */
+    public static String[] getFilesByExt(String ld, String ext) {
+        File dir = new File(ld);
+        String[] names = null;
+        final String lext = ext;
+        if (dir.isDirectory()) {
+            names = dir.list(new FilenameFilter() {
+                public boolean accept(File d, String name) {
+                    if (name.endsWith(lext)) {
+                        return true;
+                    }
+                    return false;
+                }
+            });
+        }
+        return names;
+    }
+
+    /**
+     * Construct a file url from a file, using a base dir
+     */
+    public static URL getURL(String base, String file) {
+        try {
+            File baseF = new File(base);
+            File f = new File(baseF, file);
+            String path = f.getCanonicalPath();
+            if (f.isDirectory()) {
+                path += "/";
+            }
+            if (!f.exists())
+                return null;
+            return new URL("file", "", path);
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * Add elements from the classpath <i>cp </i> to a Vector <i>jars </i> as
+     * file URLs (We use Vector for JDK 1.1 compat).
+     * <p>
+     * 
+     * @param jars The jar list
+     * @param cp a String classpath of directory or jar file elements
+     *   separated by path.separator delimiters.
+     * @throws IOException If an I/O error occurs
+     * @throws MalformedURLException Doh ;)
+     */
+    public static void addJarsFromClassPath(Vector jars, String cp)
+            throws IOException, MalformedURLException {
+        String sep = System.getProperty("path.separator");
+        String token;
+        StringTokenizer st;
+        if (cp != null) {
+            st = new StringTokenizer(cp, sep);
+            while (st.hasMoreTokens()) {
+                File f = new File(st.nextToken());
+                String path = f.getCanonicalPath();
+                if (f.isDirectory()) {
+                    path += "/";
+                }
+                URL url = new URL("file", "", path);
+                if (!jars.contains(url)) {
+                    jars.addElement(url);
+                }
+            }
+        }
+    }
+
+    /**
+     * Return a URL[] that can be used to construct a class loader
+     */
+    public static URL[] getClassPath(Vector v) {
+        URL[] urls = new URL[v.size()];
+        for (int i = 0; i < v.size(); i++) {
+            urls[i] = (URL) v.elementAt(i);
+        }
+        return urls;
+    }
+
+    /**
+     * Construct a URL classpath from files in a directory, a cpath property,
+     * and tools.jar.
+     */
+    public static URL[] getClassPath(String dir, String cpath,
+            String cpathProp, boolean addTools) throws IOException,
+            MalformedURLException {
+        Vector jarsV = new Vector();
+        if (dir != null) {
+            // Add dir/classes first, if it exists
+            URL url = getURL(dir, "classes");
+            if (url != null)
+                jarsV.addElement(url);
+            addToClassPath(jarsV, dir);
+        }
+
+        if (cpath != null)
+            addJarsFromClassPath(jarsV, cpath);
+
+        if (cpathProp != null) {
+            String cpath1 = System.getProperty(cpathProp);
+            addJarsFromClassPath(jarsV, cpath1);
+        }
+
+        if (addTools)
+            addToolsJar(jarsV);
+
+        return getClassPath(jarsV);
+    }
+
+    // -------------------- Mapping command line params to setters
+
+    public static boolean processArgs(Object proxy, String args[])
+            throws Exception {
+        String args0[] = null;
+        if (null != findMethod(proxy.getClass(), "getOptions1", new Class[] {})) {
+            args0 = (String[]) callMethod0(proxy, "getOptions1");
+        }
+
+        if (args0 == null) {
+            //args0=findVoidSetters(proxy.getClass());
+            args0 = findBooleanSetters(proxy.getClass());
+        }
+        Hashtable h = null;
+        if (null != findMethod(proxy.getClass(), "getOptionAliases",
+                new Class[] {})) {
+            h = (Hashtable) callMethod0(proxy, "getOptionAliases");
+        }
+        return processArgs(proxy, args, args0, null, h);
+    }
+
+    public static boolean processArgs(Object proxy, String args[],
+            String args0[], String args1[], Hashtable aliases) throws Exception {
+        for (int i = 0; i < args.length; i++) {
+            String arg = args[i];
+            if (arg.startsWith("-"))
+                arg = arg.substring(1);
+            if (aliases != null && aliases.get(arg) != null)
+                arg = (String) aliases.get(arg);
+
+            if (args0 != null) {
+                boolean set = false;
+                for (int j = 0; j < args0.length; j++) {
+                    if (args0[j].equalsIgnoreCase(arg)) {
+                        setProperty(proxy, args0[j], "true");
+                        set = true;
+                        break;
+                    }
+                }
+                if (set)
+                    continue;
+            }
+            if (args1 != null) {
+                for (int j = 0; j < args1.length; j++) {
+                    if (args1[j].equalsIgnoreCase(arg)) {
+                        i++;
+                        if (i >= args.length)
+                            return false;
+                        setProperty(proxy, arg, args[i]);
+                        break;
+                    }
+                }
+            } else {
+                // if args1 is not specified,assume all other options have param
+                i++;
+                if (i >= args.length)
+                    return false;
+                setProperty(proxy, arg, args[i]);
+            }
+
+        }
+        return true;
+    }
+
+    // -------------------- other utils --------------------
+    public static String[] findVoidSetters(Class c) {
+        Method m[] = findMethods(c);
+        if (m == null)
+            return null;
+        Vector v = new Vector();
+        for (int i = 0; i < m.length; i++) {
+            if (m[i].getName().startsWith("set")
+                    && m[i].getParameterTypes().length == 0) {
+                String arg = m[i].getName().substring(3);
+                v.addElement(unCapitalize(arg));
+            }
+        }
+        String s[] = new String[v.size()];
+        for (int i = 0; i < s.length; i++) {
+            s[i] = (String) v.elementAt(i);
+        }
+        return s;
+    }
+
+    public static String[] findBooleanSetters(Class c) {
+        Method m[] = findMethods(c);
+        if (m == null)
+            return null;
+        Vector v = new Vector();
+        for (int i = 0; i < m.length; i++) {
+            if (m[i].getName().startsWith("set")
+                    && m[i].getParameterTypes().length == 1
+                    && "boolean".equalsIgnoreCase(m[i].getParameterTypes()[0]
+                            .getName())) {
+                String arg = m[i].getName().substring(3);
+                v.addElement(unCapitalize(arg));
+            }
+        }
+        String s[] = new String[v.size()];
+        for (int i = 0; i < s.length; i++) {
+            s[i] = (String) v.elementAt(i);
+        }
+        return s;
+    }
+
+    static Hashtable objectMethods = new Hashtable();
+
+    public static Method[] findMethods(Class c) {
+        Method methods[] = (Method[]) objectMethods.get(c);
+        if (methods != null)
+            return methods;
+
+        methods = c.getMethods();
+        objectMethods.put(c, methods);
+        return methods;
+    }
+
+    public static Method findMethod(Class c, String name, Class params[]) {
+        Method methods[] = findMethods(c);
+        if (methods == null)
+            return null;
+        for (int i = 0; i < methods.length; i++) {
+            if (methods[i].getName().equals(name)) {
+                Class methodParams[] = methods[i].getParameterTypes();
+                if (methodParams == null)
+                    if (params == null || params.length == 0)
+                        return methods[i];
+                if (params == null)
+                    if (methodParams == null || methodParams.length == 0)
+                        return methods[i];
+                if (params.length != methodParams.length)
+                    continue;
+                boolean found = true;
+                for (int j = 0; j < params.length; j++) {
+                    if (params[j] != methodParams[j]) {
+                        found = false;
+                        break;
+                    }
+                }
+                if (found)
+                    return methods[i];
+            }
+        }
+        return null;
+    }
+
+    /** Test if the object implements a particular
+     *  method
+     */
+    public static boolean hasHook(Object obj, String methodN) {
+        try {
+            Method myMethods[] = findMethods(obj.getClass());
+            for (int i = 0; i < myMethods.length; i++) {
+                if (methodN.equals(myMethods[i].getName())) {
+                    // check if it's overriden
+                    Class declaring = myMethods[i].getDeclaringClass();
+                    Class parentOfDeclaring = declaring.getSuperclass();
+                    // this works only if the base class doesn't extend
+                    // another class.
+
+                    // if the method is declared in a top level class
+                    // like BaseInterceptor parent is Object, otherwise
+                    // parent is BaseInterceptor or an intermediate class
+                    if (!"java.lang.Object".equals(parentOfDeclaring.getName())) {
+                        return true;
+                    }
+                }
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+        return false;
+    }
+
+    public static void callMain(Class c, String args[]) throws Exception {
+        Class p[] = new Class[1];
+        p[0] = args.getClass();
+        Method m = c.getMethod("main", p);
+        m.invoke(c, new Object[] { args });
+    }
+
+    public static Object callMethod1(Object target, String methodN,
+            Object param1, String typeParam1, ClassLoader cl) throws Exception {
+        if (target == null || param1 == null) {
+            d("Assert: Illegal params " + target + " " + param1);
+        }
+        if (dbg > 0)
+            d("callMethod1 " + target.getClass().getName() + " "
+                    + param1.getClass().getName() + " " + typeParam1);
+
+        Class params[] = new Class[1];
+        if (typeParam1 == null)
+            params[0] = param1.getClass();
+        else
+            params[0] = cl.loadClass(typeParam1);
+        Method m = findMethod(target.getClass(), methodN, params);
+        if (m == null)
+            throw new NoSuchMethodException(target.getClass().getName() + " "
+                    + methodN);
+        return m.invoke(target, new Object[] { param1 });
+    }
+
+    public static Object callMethod0(Object target, String methodN)
+            throws Exception {
+        if (target == null) {
+            d("Assert: Illegal params " + target);
+            return null;
+        }
+        if (dbg > 0)
+            d("callMethod0 " + target.getClass().getName() + "." + methodN);
+
+        Class params[] = new Class[0];
+        Method m = findMethod(target.getClass(), methodN, params);
+        if (m == null)
+            throw new NoSuchMethodException(target.getClass().getName() + " "
+                    + methodN);
+        return m.invoke(target, emptyArray);
+    }
+
+    static Object[] emptyArray = new Object[] {};
+
+    public static Object callMethodN(Object target, String methodN,
+            Object params[], Class typeParams[]) throws Exception {
+        Method m = null;
+        m = findMethod(target.getClass(), methodN, typeParams);
+        if (m == null) {
+            d("Can't find method " + methodN + " in " + target + " CLASS "
+                    + target.getClass());
+            return null;
+        }
+        Object o = m.invoke(target, params);
+
+        if (dbg > 0) {
+            // debug
+            StringBuffer sb = new StringBuffer();
+            sb.append("" + target.getClass().getName() + "." + methodN + "( ");
+            for (int i = 0; i < params.length; i++) {
+                if (i > 0)
+                    sb.append(", ");
+                sb.append(params[i]);
+            }
+            sb.append(")");
+            d(sb.toString());
+        }
+        return o;
+    }
+
+    public static Object convert(String object, Class paramType) {
+        Object result = null;
+        if ("java.lang.String".equals(paramType.getName())) {
+            result = object;
+        } else if ("java.lang.Integer".equals(paramType.getName())
+                || "int".equals(paramType.getName())) {
+            try {
+                result = new Integer(object);
+            } catch (NumberFormatException ex) {
+            }
+            // Try a setFoo ( boolean )
+        } else if ("java.lang.Boolean".equals(paramType.getName())
+                || "boolean".equals(paramType.getName())) {
+            result = new Boolean(object);
+
+            // Try a setFoo ( InetAddress )
+        } else if ("java.net.InetAddress".equals(paramType
+                .getName())) {
+            try {
+                result = InetAddress.getByName(object);
+            } catch (UnknownHostException exc) {
+                d("Unable to resolve host name:" + object);
+            }
+
+            // Unknown type
+        } else {
+            d("Unknown type " + paramType.getName());
+        }
+        if (result == null) {
+            throw new IllegalArgumentException("Can't convert argument: " + object);
+        }
+        return result;
+    }
+    
+    // -------------------- Get property --------------------
+    // This provides a layer of abstraction
+
+    public static interface PropertySource {
+
+        public String getProperty(String key);
+
+    }
+
+    public static interface AttributeHolder {
+
+        public void setAttribute(String key, Object o);
+
+    }
+
+    // debug --------------------
+    static final int dbg = 0;
+
+    static void d(String s) {
+        System.out.println("IntrospectionUtils: " + s);
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/Ascii.java b/connectors/util/java/org/apache/tomcat/util/buf/Ascii.java
new file mode 100644
index 0000000..1c6750e
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/Ascii.java
@@ -0,0 +1,246 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.buf;
+
+/**
+ * This class implements some basic ASCII character handling functions.
+ *
+ * @author dac@eng.sun.com
+ * @author James Todd [gonzo@eng.sun.com]
+ */
+public final class Ascii {
+    /*
+     * Character translation tables.
+     */
+
+    private static final byte[] toUpper = new byte[256];
+    private static final byte[] toLower = new byte[256];
+
+    /*
+     * Character type tables.
+     */
+
+    private static final boolean[] isAlpha = new boolean[256];
+    private static final boolean[] isUpper = new boolean[256];
+    private static final boolean[] isLower = new boolean[256];
+    private static final boolean[] isWhite = new boolean[256];
+    private static final boolean[] isDigit = new boolean[256];
+
+    /*
+     * Initialize character translation and type tables.
+     */
+
+    static {
+        for (int i = 0; i < 256; i++) {
+            toUpper[i] = (byte)i;
+            toLower[i] = (byte)i;
+        }
+
+        for (int lc = 'a'; lc <= 'z'; lc++) {
+            int uc = lc + 'A' - 'a';
+
+            toUpper[lc] = (byte)uc;
+            toLower[uc] = (byte)lc;
+            isAlpha[lc] = true;
+            isAlpha[uc] = true;
+            isLower[lc] = true;
+            isUpper[uc] = true;
+        }
+
+        isWhite[ ' '] = true;
+        isWhite['\t'] = true;
+        isWhite['\r'] = true;
+        isWhite['\n'] = true;
+        isWhite['\f'] = true;
+        isWhite['\b'] = true;
+
+        for (int d = '0'; d <= '9'; d++) {
+            isDigit[d] = true;
+        }
+    }
+
+    /**
+     * Returns the upper case equivalent of the specified ASCII character.
+     */
+
+    public static int toUpper(int c) {
+        return toUpper[c & 0xff] & 0xff;
+    }
+
+    /**
+     * Returns the lower case equivalent of the specified ASCII character.
+     */
+
+    public static int toLower(int c) {
+        return toLower[c & 0xff] & 0xff;
+    }
+
+    /**
+     * Returns true if the specified ASCII character is upper or lower case.
+     */
+
+    public static boolean isAlpha(int c) {
+        return isAlpha[c & 0xff];
+    }
+
+    /**
+     * Returns true if the specified ASCII character is upper case.
+     */
+
+    public static boolean isUpper(int c) {
+        return isUpper[c & 0xff];
+    }
+
+    /**
+     * Returns true if the specified ASCII character is lower case.
+     */
+
+    public static boolean isLower(int c) {
+        return isLower[c & 0xff];
+    }
+
+    /**
+     * Returns true if the specified ASCII character is white space.
+     */
+
+    public static boolean isWhite(int c) {
+        return isWhite[c & 0xff];
+    }
+
+    /**
+     * Returns true if the specified ASCII character is a digit.
+     */
+
+    public static boolean isDigit(int c) {
+        return isDigit[c & 0xff];
+    }
+
+    /**
+     * Parses an unsigned integer from the specified subarray of bytes.
+     * @param b the bytes to parse
+     * @param off the start offset of the bytes
+     * @param len the length of the bytes
+     * @exception NumberFormatException if the integer format was invalid
+     */
+    public static int parseInt(byte[] b, int off, int len)
+        throws NumberFormatException
+    {
+        int c;
+
+        if (b == null || len <= 0 || !isDigit(c = b[off++])) {
+            throw new NumberFormatException();
+        }
+
+        int n = c - '0';
+
+        while (--len > 0) {
+            if (!isDigit(c = b[off++])) {
+                throw new NumberFormatException();
+            }
+            n = n * 10 + c - '0';
+        }
+
+        return n;
+    }
+
+    public static int parseInt(char[] b, int off, int len)
+        throws NumberFormatException
+    {
+        int c;
+
+        if (b == null || len <= 0 || !isDigit(c = b[off++])) {
+            throw new NumberFormatException();
+        }
+
+        int n = c - '0';
+
+        while (--len > 0) {
+            if (!isDigit(c = b[off++])) {
+                throw new NumberFormatException();
+            }
+            n = n * 10 + c - '0';
+        }
+
+        return n;
+    }
+
+    /**
+     * Parses an unsigned long from the specified subarray of bytes.
+     * @param b the bytes to parse
+     * @param off the start offset of the bytes
+     * @param len the length of the bytes
+     * @exception NumberFormatException if the long format was invalid
+     */
+    public static long parseLong(byte[] b, int off, int len)
+        throws NumberFormatException
+    {
+        int c;
+
+        if (b == null || len <= 0 || !isDigit(c = b[off++])) {
+            throw new NumberFormatException();
+        }
+
+        long n = c - '0';
+        long m;
+        
+        while (--len > 0) {
+            if (!isDigit(c = b[off++])) {
+                throw new NumberFormatException();
+            }
+            m = n * 10 + c - '0';
+
+            if (m < n) {
+                // Overflow
+                throw new NumberFormatException();
+            } else {
+                n = m;
+            }
+        }
+
+        return n;
+    }
+
+    public static long parseLong(char[] b, int off, int len)
+        throws NumberFormatException
+    {
+        int c;
+
+        if (b == null || len <= 0 || !isDigit(c = b[off++])) {
+            throw new NumberFormatException();
+        }
+
+        long n = c - '0';
+        long m;
+
+        while (--len > 0) {
+            if (!isDigit(c = b[off++])) {
+                throw new NumberFormatException();
+            }
+            m = n * 10 + c - '0';
+
+            if (m < n) {
+                // Overflow
+                throw new NumberFormatException();
+            } else {
+                n = m;
+            }
+        }
+
+        return n;
+    }
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/B2CConverter.java b/connectors/util/java/org/apache/tomcat/util/buf/B2CConverter.java
new file mode 100644
index 0000000..c403a7b
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/B2CConverter.java
@@ -0,0 +1,267 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+
+package org.apache.tomcat.util.buf;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+
+/** Efficient conversion of bytes  to character .
+ *  
+ *  This uses the standard JDK mechansim - a reader - but provides mechanisms
+ *  to recycle all the objects that are used. It is compatible with JDK1.1
+ *  and up,
+ *  ( nio is better, but it's not available even in 1.2 or 1.3 )
+ *
+ *  Not used in the current code, the performance gain is not very big
+ *  in the current case ( since String is created anyway ), but it will
+ *  be used in a later version or after the remaining optimizations.
+ */
+public class B2CConverter {
+    private IntermediateInputStream iis;
+    private ReadConvertor conv;
+    private String encoding;
+
+    protected B2CConverter() {
+    }
+    
+    /** Create a converter, with bytes going to a byte buffer
+     */
+    public B2CConverter(String encoding)
+	throws IOException
+    {
+	this.encoding=encoding;
+	reset();
+    }
+
+    
+    /** Reset the internal state, empty the buffers.
+     *  The encoding remain in effect, the internal buffers remain allocated.
+     */
+    public  void recycle() {
+	conv.recycle();
+    }
+
+    static final int BUFFER_SIZE=8192;
+    char result[]=new char[BUFFER_SIZE];
+
+    /** Convert a buffer of bytes into a chars
+     */
+    public  void convert( ByteChunk bb, CharChunk cb )
+	throws IOException
+    {
+	// Set the ByteChunk as input to the Intermediate reader
+	iis.setByteChunk( bb );
+	convert(cb);
+    }
+
+    private void convert(CharChunk cb)
+	throws IOException
+    {
+	try {
+	    // read from the reader
+	    while( true ) { // conv.ready() ) {
+		int cnt=conv.read( result, 0, BUFFER_SIZE );
+		if( cnt <= 0 ) {
+		    // End of stream ! - we may be in a bad state
+		    if( debug>0)
+			log( "EOF" );
+		    //		    reset();
+		    return;
+		}
+		if( debug > 1 )
+		    log("Converted: " + new String( result, 0, cnt ));
+
+		// XXX go directly
+		cb.append( result, 0, cnt );
+	    }
+	} catch( IOException ex) {
+	    if( debug>0)
+		log( "Reseting the converter " + ex.toString() );
+	    reset();
+	    throw ex;
+	}
+    }
+
+    public void reset()
+	throws IOException
+    {
+	// destroy the reader/iis
+	iis=new IntermediateInputStream();
+	conv=new ReadConvertor( iis, encoding );
+    }
+
+    private final int debug=0;
+    void log( String s ) {
+	System.out.println("B2CConverter: " + s );
+    }
+
+    // -------------------- Not used - the speed improvemnt is quite small
+
+    /*
+    private Hashtable decoders;
+    public static final boolean useNewString=false;
+    public static final boolean useSpecialDecoders=true;
+    private UTF8Decoder utfD;
+    // private char[] conversionBuff;
+    CharChunk conversionBuf;
+
+
+    private  static String decodeString(ByteChunk mb, String enc)
+	throws IOException
+    {
+	byte buff=mb.getBuffer();
+	int start=mb.getStart();
+	int end=mb.getEnd();
+	if( useNewString ) {
+	    if( enc==null) enc="UTF8";
+	    return new String( buff, start, end-start, enc );
+	}
+	B2CConverter b2c=null;
+	if( useSpecialDecoders &&
+	    (enc==null || "UTF8".equalsIgnoreCase(enc))) {
+	    if( utfD==null ) utfD=new UTF8Decoder();
+	    b2c=utfD;
+	}
+	if(decoders == null ) decoders=new Hashtable();
+	if( enc==null ) enc="UTF8";
+	b2c=(B2CConverter)decoders.get( enc );
+	if( b2c==null ) {
+	    if( useSpecialDecoders ) {
+		if( "UTF8".equalsIgnoreCase( enc ) ) {
+		    b2c=new UTF8Decoder();
+		}
+	    }
+	    if( b2c==null )
+		b2c=new B2CConverter( enc );
+	    decoders.put( enc, b2c );
+	}
+	if( conversionBuf==null ) conversionBuf=new CharChunk(1024);
+
+	try {
+	    conversionBuf.recycle();
+	    b2c.convert( this, conversionBuf );
+	    //System.out.println("XXX 1 " + conversionBuf );
+	    return conversionBuf.toString();
+	} catch( IOException ex ) {
+	    ex.printStackTrace();
+	    return null;
+	}
+    }
+
+    */
+}
+
+// -------------------- Private implementation --------------------
+
+
+
+/**
+ * 
+ */
+final class  ReadConvertor extends InputStreamReader {
+    // stream with flush() and close(). overriden.
+    private IntermediateInputStream iis;
+    
+    // Has a private, internal byte[8192]
+    
+    /** Create a converter.
+     */
+    public ReadConvertor( IntermediateInputStream in, String enc )
+	throws UnsupportedEncodingException
+    {
+	super( in, enc );
+	iis=in;
+    }
+    
+    /** Overriden - will do nothing but reset internal state.
+     */
+    public  final void close() throws IOException {
+	// NOTHING
+	// Calling super.close() would reset out and cb.
+    }
+    
+    public  final int read(char cbuf[], int off, int len)
+	throws IOException
+    {
+	// will do the conversion and call write on the output stream
+	return super.read( cbuf, off, len );
+    }
+    
+    /** Reset the buffer
+     */
+    public  final void recycle() {
+    }
+}
+
+
+/** Special output stream where close() is overriden, so super.close()
+    is never called.
+    
+    This allows recycling. It can also be disabled, so callbacks will
+    not be called if recycling the converter and if data was not flushed.
+*/
+final class IntermediateInputStream extends InputStream {
+    byte buf[];
+    int pos;
+    int len;
+    int end;
+    
+    public IntermediateInputStream() {
+    }
+    
+    public  final void close() throws IOException {
+	// shouldn't be called - we filter it out in writer
+	throw new IOException("close() called - shouldn't happen ");
+    }
+    
+    public  final  int read(byte cbuf[], int off, int len) throws IOException {
+	if( pos >= end ) return -1;
+	if (pos + len > end) {
+	    len = end - pos;
+	}
+	if (len <= 0) {
+	    return 0;
+	}
+	System.arraycopy(buf, pos, cbuf, off, len);
+	pos += len;
+	return len;
+    }
+    
+    public  final int read() throws IOException {
+	return (pos < end ) ? (buf[pos++] & 0xff) : -1;
+    }
+
+    // -------------------- Internal methods --------------------
+
+    void setBuffer( byte b[], int p, int l ) {
+	buf=b;
+	pos=p;
+	len=l;
+	end=pos+len;
+    }
+
+    void setByteChunk( ByteChunk mb ) {
+	buf=mb.getBytes();
+	pos=mb.getStart();
+	len=mb.getLength();
+	end=pos+len;
+    }
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/Base64.java b/connectors/util/java/org/apache/tomcat/util/buf/Base64.java
new file mode 100644
index 0000000..d3e00b1
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/Base64.java
@@ -0,0 +1,264 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+
+package org.apache.tomcat.util.buf;
+
+
+/**
+ * This class provides encode/decode for RFC 2045 Base64 as
+ * defined by RFC 2045, N. Freed and N. Borenstein.
+ * RFC 2045: Multipurpose Internet Mail Extensions (MIME)
+ * Part One: Format of Internet Message Bodies. Reference
+ * 1996 Available at: http://www.ietf.org/rfc/rfc2045.txt
+ * This class is used by XML Schema binary format validation
+ *
+ * @author Jeffrey Rodriguez
+ * @version $Revision$ $Date$
+ */
+
+public final class Base64 {
+
+
+    static private final int  BASELENGTH         = 255;
+    static private final int  LOOKUPLENGTH       = 63;
+    static private final int  TWENTYFOURBITGROUP = 24;
+    static private final int  EIGHTBIT           = 8;
+    static private final int  SIXTEENBIT         = 16;
+    static private final int  SIXBIT             = 6;
+    static private final int  FOURBYTE           = 4;
+
+
+    static private final byte PAD               = ( byte ) '=';
+    static private byte [] base64Alphabet       = new byte[BASELENGTH];
+    static private byte [] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
+
+    static {
+
+        for (int i = 0; i<BASELENGTH; i++ ) {
+            base64Alphabet[i] = -1;
+        }
+        for ( int i = 'Z'; i >= 'A'; i-- ) {
+            base64Alphabet[i] = (byte) (i-'A');
+        }
+        for ( int i = 'z'; i>= 'a'; i--) {
+            base64Alphabet[i] = (byte) ( i-'a' + 26);
+        }
+
+        for ( int i = '9'; i >= '0'; i--) {
+            base64Alphabet[i] = (byte) (i-'0' + 52);
+        }
+
+        base64Alphabet['+']  = 62;
+        base64Alphabet['/']  = 63;
+
+       for (int i = 0; i<=25; i++ )
+            lookUpBase64Alphabet[i] = (byte) ('A'+i );
+
+        for (int i = 26,  j = 0; i<=51; i++, j++ )
+            lookUpBase64Alphabet[i] = (byte) ('a'+ j );
+
+        for (int i = 52,  j = 0; i<=61; i++, j++ )
+            lookUpBase64Alphabet[i] = (byte) ('0' + j );
+
+    }
+
+
+    static boolean isBase64( byte octect ) {
+        //shall we ignore white space? JEFF??
+        return(octect == PAD || base64Alphabet[octect] != -1 );
+    }
+
+
+    static boolean isArrayByteBase64( byte[] arrayOctect ) {
+        int length = arrayOctect.length;
+        if ( length == 0 )
+            return false;
+        for ( int i=0; i < length; i++ ) {
+            if ( Base64.isBase64( arrayOctect[i] ) == false)
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * Encodes hex octects into Base64
+     *
+     * @param binaryData Array containing binaryData
+     * @return Encoded Base64 array
+     */
+    public static byte[] encode( byte[] binaryData ) {
+        int      lengthDataBits    = binaryData.length*EIGHTBIT;
+        int      fewerThan24bits   = lengthDataBits%TWENTYFOURBITGROUP;
+        int      numberTriplets    = lengthDataBits/TWENTYFOURBITGROUP;
+        byte     encodedData[]     = null;
+
+
+        if ( fewerThan24bits != 0 ) //data not divisible by 24 bit
+            encodedData = new byte[ (numberTriplets + 1 )*4  ];
+        else // 16 or 8 bit
+            encodedData = new byte[ numberTriplets*4 ];
+
+        byte k=0, l=0, b1=0,b2=0,b3=0;
+
+        int encodedIndex = 0;
+        int dataIndex   = 0;
+        int i           = 0;
+        for ( i = 0; i<numberTriplets; i++ ) {
+
+            dataIndex = i*3;
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex + 1];
+            b3 = binaryData[dataIndex + 2];
+
+            l  = (byte)(b2 & 0x0f);
+            k  = (byte)(b1 & 0x03);
+
+            encodedIndex = i*4;
+            encodedData[encodedIndex]   = lookUpBase64Alphabet[ b1 >>2 ];
+            encodedData[encodedIndex+1] = lookUpBase64Alphabet[(b2 >>4 ) |
+( k<<4 )];
+            encodedData[encodedIndex+2] = lookUpBase64Alphabet[ (l <<2 ) |
+( b3>>6)];
+            encodedData[encodedIndex+3] = lookUpBase64Alphabet[ b3 & 0x3f ];
+        }
+
+        // form integral number of 6-bit groups
+        dataIndex    = i*3;
+        encodedIndex = i*4;
+        if (fewerThan24bits == EIGHTBIT ) {
+            b1 = binaryData[dataIndex];
+            k = (byte) ( b1 &0x03 );
+            encodedData[encodedIndex]     = lookUpBase64Alphabet[ b1 >>2 ];
+            encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ k<<4 ];
+            encodedData[encodedIndex + 2] = PAD;
+            encodedData[encodedIndex + 3] = PAD;
+        } else if ( fewerThan24bits == SIXTEENBIT ) {
+
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex +1 ];
+            l = ( byte ) ( b2 &0x0f );
+            k = ( byte ) ( b1 &0x03 );
+            encodedData[encodedIndex]     = lookUpBase64Alphabet[ b1 >>2 ];
+            encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ (b2 >>4 )
+| ( k<<4 )];
+            encodedData[encodedIndex + 2] = lookUpBase64Alphabet[ l<<2 ];
+            encodedData[encodedIndex + 3] = PAD;
+        }
+        return encodedData;
+    }
+
+
+    /**
+     * Decodes Base64 data into octects
+     *
+     * @param base64Data Byte array containing Base64 data
+     * @return Array containind decoded data.
+     */
+    public byte[] decode( byte[] base64Data ) {
+        int      numberQuadruple    = base64Data.length/FOURBYTE;
+        byte     decodedData[]      = null;
+        byte     b1=0,b2=0,b3=0, b4=0, marker0=0, marker1=0;
+
+        // Throw away anything not in base64Data
+        // Adjust size
+
+        int encodedIndex = 0;
+        int dataIndex    = 0;
+        decodedData      = new byte[ numberQuadruple*3 + 1 ];
+
+        for (int i = 0; i<numberQuadruple; i++ ) {
+            dataIndex = i*4;
+            marker0   = base64Data[dataIndex +2];
+            marker1   = base64Data[dataIndex +3];
+
+            b1 = base64Alphabet[base64Data[dataIndex]];
+            b2 = base64Alphabet[base64Data[dataIndex +1]];
+
+            if ( marker0 != PAD && marker1 != PAD ) {     //No PAD e.g 3cQl
+                b3 = base64Alphabet[ marker0 ];
+                b4 = base64Alphabet[ marker1 ];
+
+                decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 ) ;
+                decodedData[encodedIndex+1] = (byte)(((b2 & 0xf)<<4 ) |(
+(b3>>2) & 0xf) );
+                decodedData[encodedIndex+2] = (byte)( b3<<6 | b4 );
+            } else if ( marker0 == PAD ) {               //Two PAD e.g. 3c[Pad][Pad]
+                decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 ) ;
+                decodedData[encodedIndex+1] = (byte)((b2 & 0xf)<<4 );
+                decodedData[encodedIndex+2] = (byte) 0;
+            } else if ( marker1 == PAD ) {              //One PAD e.g. 3cQ[Pad]
+                b3 = base64Alphabet[ marker0 ];
+
+                decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 );
+                decodedData[encodedIndex+1] = (byte)(((b2 & 0xf)<<4 ) |(
+(b3>>2) & 0xf) );
+                decodedData[encodedIndex+2] = (byte)( b3<<6);
+            }
+            encodedIndex += 3;
+        }
+        return decodedData;
+
+    }
+
+    static final int base64[]= {
+	64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
+	    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
+	    64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+	    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
+	    64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+	    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
+    };
+
+    public static String base64Decode( String orig ) {
+	char chars[]=orig.toCharArray();
+	StringBuffer sb=new StringBuffer();
+	int i=0;
+
+	int shift = 0;   // # of excess bits stored in accum
+	int acc = 0;
+	
+	for (i=0; i<chars.length; i++) {
+	    int v = base64[ chars[i] & 0xFF ];
+	    
+	    if ( v >= 64 ) {
+		if( chars[i] != '=' )
+		    System.out.println("Wrong char in base64: " + chars[i]);
+	    } else {
+		acc= ( acc << 6 ) | v;
+		shift += 6;
+		if ( shift >= 8 ) {
+		    shift -= 8;
+		    sb.append( (char) ((acc >> shift) & 0xff));
+		}
+	    }
+	}
+	return sb.toString();
+    }
+
+
+}
+
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/ByteChunk.java b/connectors/util/java/org/apache/tomcat/util/buf/ByteChunk.java
new file mode 100644
index 0000000..7c2f2b0
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/ByteChunk.java
@@ -0,0 +1,766 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.buf;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/*
+ * In a server it is very important to be able to operate on
+ * the original byte[] without converting everything to chars.
+ * Some protocols are ASCII only, and some allow different
+ * non-UNICODE encodings. The encoding is not known beforehand,
+ * and can even change during the execution of the protocol.
+ * ( for example a multipart message may have parts with different
+ *  encoding )
+ *
+ * For HTTP it is not very clear how the encoding of RequestURI
+ * and mime values can be determined, but it is a great advantage
+ * to be able to parse the request without converting to string.
+ */
+
+
+/**
+ * This class is used to represent a chunk of bytes, and
+ * utilities to manipulate byte[].
+ *
+ * The buffer can be modified and used for both input and output.
+ *
+ * @author dac@sun.com
+ * @author James Todd [gonzo@sun.com]
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ */
+public final class ByteChunk implements Cloneable, Serializable {
+
+    // Input interface, used when the buffer is emptied.
+    public static interface ByteInputChannel {
+        /** 
+         * Read new bytes ( usually the internal conversion buffer ).
+         * The implementation is allowed to ignore the parameters, 
+         * and mutate the chunk if it wishes to implement its own buffering.
+         */
+        public int realReadBytes(byte cbuf[], int off, int len)
+            throws IOException;
+    }
+    // Output interface, used when the buffer is filled.
+    public static interface ByteOutputChannel {
+        /** 
+         * Send the bytes ( usually the internal conversion buffer ).
+         * Expect 8k output if the buffer is full.
+         */
+        public void realWriteBytes(byte cbuf[], int off, int len)
+            throws IOException;
+    }
+
+    // --------------------
+
+    /** Default encoding used to convert to strings. It should be UTF8,
+	as most standards seem to converge, but the servlet API requires
+	8859_1, and this object is used mostly for servlets. 
+    */
+    public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1";
+        
+    // byte[]
+    private byte[] buff;
+
+    private int start=0;
+    private int end;
+
+    private String enc;
+
+    private boolean isSet=false; // XXX
+
+    // How much can it grow, when data is added
+    private int limit=-1;
+
+    private ByteInputChannel in = null;
+    private ByteOutputChannel out = null;
+
+    private boolean isOutput=false;
+    private boolean optimizedWrite=true;
+    
+    /**
+     * Creates a new, uninitialized ByteChunk object.
+     */
+    public ByteChunk() {
+    }
+
+    public ByteChunk( int initial ) {
+	allocate( initial, -1 );
+    }
+
+    //--------------------
+        public ByteChunk getClone() {
+	try {
+	    return (ByteChunk)this.clone();
+	} catch( Exception ex) {
+	    return null;
+	}
+    }
+
+    public boolean isNull() {
+	return ! isSet; // buff==null;
+    }
+    
+    /**
+     * Resets the message buff to an uninitialized state.
+     */
+    public void recycle() {
+	//	buff = null;
+	enc=null;
+	start=0;
+	end=0;
+	isSet=false;
+    }
+
+    public void reset() {
+	buff=null;
+    }
+
+    // -------------------- Setup --------------------
+
+    public void allocate( int initial, int limit  ) {
+	isOutput=true;
+	if( buff==null || buff.length < initial ) {
+	    buff=new byte[initial];
+	}    
+	this.limit=limit;
+	start=0;
+	end=0;
+	isSet=true;
+    }
+
+    /**
+     * Sets the message bytes to the specified subarray of bytes.
+     * 
+     * @param b the ascii bytes
+     * @param off the start offset of the bytes
+     * @param len the length of the bytes
+     */
+    public void setBytes(byte[] b, int off, int len) {
+	buff = b;
+	start = off;
+	end = start+ len;
+	isSet=true;
+    }
+
+    public void setOptimizedWrite(boolean optimizedWrite) {
+        this.optimizedWrite = optimizedWrite;
+    }
+
+    public void setEncoding( String enc ) {
+	this.enc=enc;
+    }
+
+    /**
+     * Returns the message bytes.
+     */
+    public byte[] getBytes() {
+	return getBuffer();
+    }
+
+    /**
+     * Returns the message bytes.
+     */
+    public byte[] getBuffer() {
+	return buff;
+    }
+
+    /**
+     * Returns the start offset of the bytes.
+     * For output this is the end of the buffer.
+     */
+    public int getStart() {
+	return start;
+    }
+
+    public int getOffset() {
+	return getStart();
+    }
+
+    public void setOffset(int off) {
+        if (end < off ) end=off;
+	start=off;
+    }
+
+    /**
+     * Returns the length of the bytes.
+     * XXX need to clean this up
+     */
+    public int getLength() {
+	return end-start;
+    }
+
+    /** Maximum amount of data in this buffer.
+     *
+     *  If -1 or not set, the buffer will grow undefinitely.
+     *  Can be smaller than the current buffer size ( which will not shrink ).
+     *  When the limit is reached, the buffer will be flushed ( if out is set )
+     *  or throw exception.
+     */
+    public void setLimit(int limit) {
+	this.limit=limit;
+    }
+    
+    public int getLimit() {
+	return limit;
+    }
+
+    /**
+     * When the buffer is empty, read the data from the input channel.
+     */
+    public void setByteInputChannel(ByteInputChannel in) {
+        this.in = in;
+    }
+
+    /** When the buffer is full, write the data to the output channel.
+     * 	Also used when large amount of data is appended.
+     *
+     *  If not set, the buffer will grow to the limit.
+     */
+    public void setByteOutputChannel(ByteOutputChannel out) {
+	this.out=out;
+    }
+
+    public int getEnd() {
+	return end;
+    }
+
+    public void setEnd( int i ) {
+	end=i;
+    }
+
+    // -------------------- Adding data to the buffer --------------------
+    public void append( char c )
+	throws IOException
+    {
+	append( (byte)c);
+    }
+
+    public void append( byte b )
+	throws IOException
+    {
+	makeSpace( 1 );
+
+	// couldn't make space
+	if( limit >0 && end >= limit ) {
+	    flushBuffer();
+	}
+	buff[end++]=b;
+    }
+
+    public void append( ByteChunk src )
+	throws IOException
+    {
+	append( src.getBytes(), src.getStart(), src.getLength());
+    }
+
+    /** Add data to the buffer
+     */
+    public void append( byte src[], int off, int len )
+	throws IOException
+    {
+	// will grow, up to limit
+	makeSpace( len );
+
+	// if we don't have limit: makeSpace can grow as it wants
+	if( limit < 0 ) {
+	    // assert: makeSpace made enough space
+	    System.arraycopy( src, off, buff, end, len );
+	    end+=len;
+	    return;
+	}
+
+        // Optimize on a common case.
+        // If the buffer is empty and the source is going to fill up all the
+        // space in buffer, may as well write it directly to the output,
+        // and avoid an extra copy
+        if ( optimizedWrite && len == limit && end == start) {
+            out.realWriteBytes( src, off, len );
+            return;
+        }
+	// if we have limit and we're below
+	if( len <= limit - end ) {
+	    // makeSpace will grow the buffer to the limit,
+	    // so we have space
+	    System.arraycopy( src, off, buff, end, len );
+	    end+=len;
+	    return;
+	}
+
+	// need more space than we can afford, need to flush
+	// buffer
+
+	// the buffer is already at ( or bigger than ) limit
+
+        // We chunk the data into slices fitting in the buffer limit, although
+        // if the data is written directly if it doesn't fit
+
+        int avail=limit-end;
+        System.arraycopy(src, off, buff, end, avail);
+        end += avail;
+
+        flushBuffer();
+
+        int remain = len - avail;
+
+        while (remain > (limit - end)) {
+            out.realWriteBytes( src, (off + len) - remain, limit - end );
+            remain = remain - (limit - end);
+        }
+
+        System.arraycopy(src, (off + len) - remain, buff, end, remain);
+        end += remain;
+
+    }
+
+
+    // -------------------- Removing data from the buffer --------------------
+
+    public int substract()
+        throws IOException {
+
+        if ((end - start) == 0) {
+            if (in == null)
+                return -1;
+            int n = in.realReadBytes( buff, 0, buff.length );
+            if (n < 0)
+                return -1;
+        }
+
+        return (buff[start++] & 0xFF);
+
+    }
+
+    public int substract(ByteChunk src)
+        throws IOException {
+
+        if ((end - start) == 0) {
+            if (in == null)
+                return -1;
+            int n = in.realReadBytes( buff, 0, buff.length );
+            if (n < 0)
+                return -1;
+        }
+
+        int len = getLength();
+        src.append(buff, start, len);
+        start = end;
+        return len;
+
+    }
+
+    public int substract( byte src[], int off, int len )
+        throws IOException {
+
+        if ((end - start) == 0) {
+            if (in == null)
+                return -1;
+            int n = in.realReadBytes( buff, 0, buff.length );
+            if (n < 0)
+                return -1;
+        }
+
+        int n = len;
+        if (len > getLength()) {
+            n = getLength();
+        }
+        System.arraycopy(buff, start, src, off, n);
+        start += n;
+        return n;
+
+    }
+
+
+    public void flushBuffer()
+	throws IOException
+    {
+	//assert out!=null
+	if( out==null ) {
+	    throw new IOException( "Buffer overflow, no sink " + limit + " " +
+				   buff.length  );
+	}
+	out.realWriteBytes( buff, start, end-start );
+	end=start;
+    }
+
+    /** Make space for len chars. If len is small, allocate
+     *	a reserve space too. Never grow bigger than limit.
+     */
+    private void makeSpace(int count)
+    {
+	byte[] tmp = null;
+
+	int newSize;
+	int desiredSize=end + count;
+
+	// Can't grow above the limit
+	if( limit > 0 &&
+	    desiredSize > limit) {
+	    desiredSize=limit;
+	}
+
+	if( buff==null ) {
+	    if( desiredSize < 256 ) desiredSize=256; // take a minimum
+	    buff=new byte[desiredSize];
+	}
+	
+	// limit < buf.length ( the buffer is already big )
+	// or we already have space XXX
+	if( desiredSize <= buff.length ) {
+	    return;
+	}
+	// grow in larger chunks
+	if( desiredSize < 2 * buff.length ) {
+	    newSize= buff.length * 2;
+	    if( limit >0 &&
+		newSize > limit ) newSize=limit;
+	    tmp=new byte[newSize];
+	} else {
+	    newSize= buff.length * 2 + count ;
+	    if( limit > 0 &&
+		newSize > limit ) newSize=limit;
+	    tmp=new byte[newSize];
+	}
+	
+	System.arraycopy(buff, start, tmp, 0, end-start);
+	buff = tmp;
+	tmp = null;
+	end=end-start;
+	start=0;
+    }
+    
+    // -------------------- Conversion and getters --------------------
+
+    public String toString() {
+	if (null == buff) {
+	    return null;
+	}
+	String strValue=null;
+	try {
+	    if( enc==null ) enc=DEFAULT_CHARACTER_ENCODING;
+	    return new String( buff, start, end-start, enc );
+	    /*
+	      Does not improve the speed too much on most systems,
+	      it's safer to use the "clasical" new String().
+
+	      Most overhead is in creating char[] and copying,
+	      the internal implementation of new String() is very close to
+	      what we do. The decoder is nice for large buffers and if
+	      we don't go to String ( so we can take advantage of reduced GC)
+
+	      // Method is commented out, in:
+	      return B2CConverter.decodeString( enc );
+	    */
+	} catch (java.io.IOException e) {
+	    return null;  // XXX 
+	}
+    }
+
+    public int getInt()
+    {
+	return Ascii.parseInt(buff, start,end-start);
+    }
+
+    public long getLong() {
+        return Ascii.parseLong(buff, start,end-start);
+    }
+
+
+    // -------------------- equals --------------------
+
+    /**
+     * Compares the message bytes to the specified String object.
+     * @param s the String to compare
+     * @return true if the comparison succeeded, false otherwise
+     */
+    public boolean equals(String s) {
+	// XXX ENCODING - this only works if encoding is UTF8-compat
+	// ( ok for tomcat, where we compare ascii - header names, etc )!!!
+	
+	byte[] b = buff;
+	int blen = end-start;
+	if (b == null || blen != s.length()) {
+	    return false;
+	}
+	int boff = start;
+	for (int i = 0; i < blen; i++) {
+	    if (b[boff++] != s.charAt(i)) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    /**
+     * Compares the message bytes to the specified String object.
+     * @param s the String to compare
+     * @return true if the comparison succeeded, false otherwise
+     */
+    public boolean equalsIgnoreCase(String s) {
+	byte[] b = buff;
+	int blen = end-start;
+	if (b == null || blen != s.length()) {
+	    return false;
+	}
+	int boff = start;
+	for (int i = 0; i < blen; i++) {
+	    if (Ascii.toLower(b[boff++]) != Ascii.toLower(s.charAt(i))) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    public boolean equals( ByteChunk bb ) {
+	return equals( bb.getBytes(), bb.getStart(), bb.getLength());
+    }
+    
+    public boolean equals( byte b2[], int off2, int len2) {
+	byte b1[]=buff;
+	if( b1==null && b2==null ) return true;
+
+	int len=end-start;
+	if ( len2 != len || b1==null || b2==null ) 
+	    return false;
+		
+	int off1 = start;
+
+	while ( len-- > 0) {
+	    if (b1[off1++] != b2[off2++]) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    public boolean equals( CharChunk cc ) {
+	return equals( cc.getChars(), cc.getStart(), cc.getLength());
+    }
+    
+    public boolean equals( char c2[], int off2, int len2) {
+	// XXX works only for enc compatible with ASCII/UTF !!!
+	byte b1[]=buff;
+	if( c2==null && b1==null ) return true;
+	
+	if (b1== null || c2==null || end-start != len2 ) {
+	    return false;
+	}
+	int off1 = start;
+	int len=end-start;
+	
+	while ( len-- > 0) {
+	    if ( (char)b1[off1++] != c2[off2++]) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * @param s the string
+     */
+    public boolean startsWith(String s) {
+	// Works only if enc==UTF
+	byte[] b = buff;
+	int blen = s.length();
+	if (b == null || blen > end-start) {
+	    return false;
+	}
+	int boff = start;
+	for (int i = 0; i < blen; i++) {
+	    if (b[boff++] != s.charAt(i)) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    /* Returns true if the message bytes start with the specified byte array */
+    public boolean startsWith(byte[] b2) {
+        byte[] b1 = buff;
+        if (b1 == null && b2 == null) {
+            return true;
+        }
+
+        int len = end - start;
+        if (b1 == null || b2 == null || b2.length > len) {
+            return false;
+        }
+        for (int i = start, j = 0; i < end && j < b2.length; ) {
+            if (b1[i++] != b2[j++]) 
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * @param s the string
+     * @param pos The position
+     */
+    public boolean startsWithIgnoreCase(String s, int pos) {
+	byte[] b = buff;
+	int len = s.length();
+	if (b == null || len+pos > end-start) {
+	    return false;
+	}
+	int off = start+pos;
+	for (int i = 0; i < len; i++) {
+	    if (Ascii.toLower( b[off++] ) != Ascii.toLower( s.charAt(i))) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    public int indexOf( String src, int srcOff, int srcLen, int myOff ) {
+	char first=src.charAt( srcOff );
+
+	// Look for first char 
+	int srcEnd = srcOff + srcLen;
+        
+	for( int i=myOff+start; i <= (end - srcLen); i++ ) {
+	    if( buff[i] != first ) continue;
+	    // found first char, now look for a match
+            int myPos=i+1;
+	    for( int srcPos=srcOff + 1; srcPos< srcEnd; ) {
+                if( buff[myPos++] != src.charAt( srcPos++ ))
+		    break;
+                if( srcPos==srcEnd ) return i-start; // found it
+	    }
+	}
+	return -1;
+    }
+
+    // -------------------- Hash code  --------------------
+
+    // normal hash. 
+    public int hash() {
+	return hashBytes( buff, start, end-start);
+    }
+
+    // hash ignoring case
+    public int hashIgnoreCase() {
+	return hashBytesIC( buff, start, end-start );
+    }
+
+    private static int hashBytes( byte buff[], int start, int bytesLen ) {
+	int max=start+bytesLen;
+	byte bb[]=buff;
+	int code=0;
+	for (int i = start; i < max ; i++) {
+	    code = code * 37 + bb[i];
+	}
+	return code;
+    }
+
+    private static int hashBytesIC( byte bytes[], int start,
+				    int bytesLen )
+    {
+	int max=start+bytesLen;
+	byte bb[]=bytes;
+	int code=0;
+	for (int i = start; i < max ; i++) {
+	    code = code * 37 + Ascii.toLower(bb[i]);
+	}
+	return code;
+    }
+
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * @param c the character
+     * @param starting The start position
+     */
+    public int indexOf(char c, int starting) {
+	int ret = indexOf( buff, start+starting, end, c);
+	return (ret >= start) ? ret - start : -1;
+    }
+
+    public static int  indexOf( byte bytes[], int off, int end, char qq )
+    {
+	// Works only for UTF 
+	while( off < end ) {
+	    byte b=bytes[off];
+	    if( b==qq )
+		return off;
+	    off++;
+	}
+	return -1;
+    }
+
+    /** Find a character, no side effects.
+     *  @return index of char if found, -1 if not
+     */
+    public static int findChar( byte buf[], int start, int end, char c ) {
+	byte b=(byte)c;
+	int offset = start;
+	while (offset < end) {
+	    if (buf[offset] == b) {
+		return offset;
+	    }
+	    offset++;
+	}
+	return -1;
+    }
+
+    /** Find a character, no side effects.
+     *  @return index of char if found, -1 if not
+     */
+    public static int findChars( byte buf[], int start, int end, byte c[] ) {
+	int clen=c.length;
+	int offset = start;
+	while (offset < end) {
+	    for( int i=0; i<clen; i++ ) 
+		if (buf[offset] == c[i]) {
+		    return offset;
+		}
+	    offset++;
+	}
+	return -1;
+    }
+
+    /** Find the first character != c 
+     *  @return index of char if found, -1 if not
+     */
+    public static int findNotChars( byte buf[], int start, int end, byte c[] )
+    {
+	int clen=c.length;
+	int offset = start;
+	boolean found;
+		
+	while (offset < end) {
+	    found=true;
+	    for( int i=0; i<clen; i++ ) {
+		if (buf[offset] == c[i]) {
+		    found=false;
+		    break;
+		}
+	    }
+	    if( found ) { // buf[offset] != c[0..len]
+		return offset;
+	    }
+	    offset++;
+	}
+	return -1;
+    }
+
+
+    
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/C2BConverter.java b/connectors/util/java/org/apache/tomcat/util/buf/C2BConverter.java
new file mode 100644
index 0000000..5b8be2a
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/C2BConverter.java
@@ -0,0 +1,256 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.buf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+
+/** Efficient conversion of character to bytes.
+ *  
+ *  This uses the standard JDK mechansim - a writer - but provides mechanisms
+ *  to recycle all the objects that are used. It is compatible with JDK1.1 and up,
+ *  ( nio is better, but it's not available even in 1.2 or 1.3 )
+ * 
+ */
+public final class C2BConverter {
+    private IntermediateOutputStream ios;
+    private WriteConvertor conv;
+    private ByteChunk bb;
+    private String enc;
+    
+    /** Create a converter, with bytes going to a byte buffer
+     */
+    public C2BConverter(ByteChunk output, String encoding) throws IOException {
+	this.bb=output;
+	ios=new IntermediateOutputStream( output );
+	conv=new WriteConvertor( ios, encoding );
+        this.enc=encoding;
+    }
+
+    /** Create a converter
+     */
+    public C2BConverter(String encoding) throws IOException {
+	this( new ByteChunk(1024), encoding );
+    }
+
+    public ByteChunk getByteChunk() {
+	return bb;
+    }
+
+    public String getEncoding() {
+        return enc;
+    }
+
+    public void setByteChunk(ByteChunk bb) {
+	this.bb=bb;
+	ios.setByteChunk( bb );
+    }
+
+    /** Reset the internal state, empty the buffers.
+     *  The encoding remain in effect, the internal buffers remain allocated.
+     */
+    public  final void recycle() {
+	conv.recycle();
+	bb.recycle();
+    }
+
+    /** Generate the bytes using the specified encoding
+     */
+    public  final void convert(char c[], int off, int len ) throws IOException {
+	conv.write( c, off, len );
+    }
+
+    /** Generate the bytes using the specified encoding
+     */
+    public  final void convert(String s ) throws IOException {
+	conv.write( s );
+    }
+
+    /** Generate the bytes using the specified encoding
+     */
+    public  final void convert(char c ) throws IOException {
+	conv.write( c );
+    }
+
+    /** Convert a message bytes chars to bytes
+     */
+    public final void convert(MessageBytes mb ) throws IOException {
+        int type=mb.getType();
+        if( type==MessageBytes.T_BYTES )
+            return;
+        ByteChunk orig=bb;
+        setByteChunk( mb.getByteChunk());
+        bb.recycle();
+        bb.allocate( 32, -1 );
+        
+        if( type==MessageBytes.T_STR ) {
+            convert( mb.getString() );
+            // System.out.println("XXX Converting " + mb.getString() );
+        } else if( type==MessageBytes.T_CHARS ) {
+            CharChunk charC=mb.getCharChunk();
+            convert( charC.getBuffer(),
+                                charC.getOffset(), charC.getLength());
+            //System.out.println("XXX Converting " + mb.getCharChunk() );
+        } else {
+            System.out.println("XXX unknowon type " + type );
+        }
+        flushBuffer();
+        //System.out.println("C2B: XXX " + bb.getBuffer() + bb.getLength()); 
+        setByteChunk(orig);
+    }
+
+    /** Flush any internal buffers into the ByteOutput or the internal
+     *  byte[]
+     */
+    public  final void flushBuffer() throws IOException {
+	conv.flush();
+    }
+
+}
+
+// -------------------- Private implementation --------------------
+
+
+
+/**
+ *  Special writer class, where close() is overritten. The default implementation
+ *  would set byteOutputter to null, and the writter can't be recycled. 
+ *
+ *  Note that the flush method will empty the internal buffers _and_ call
+ *  flush on the output stream - that's why we use an intermediary output stream
+ *  that overrides flush(). The idea is to  have full control: flushing the
+ *  char->byte converter should be independent of flushing the OutputStream.
+ * 
+ *  When a WriteConverter is created, it'll allocate one or 2 byte buffers,
+ *  with a 8k size that can't be changed ( at least in JDK1.1 -> 1.4 ). It would
+ *  also allocate a ByteOutputter or equivalent - again some internal buffers.
+ *
+ *  It is essential to keep  this object around and reuse it. You can use either
+ *  pools or per thread data - but given that in most cases a converter will be
+ *  needed for every thread and most of the time only 1 ( or 2 ) encodings will
+ *  be used, it is far better to keep it per thread and eliminate the pool 
+ *  overhead too.
+ * 
+ */
+ final class	WriteConvertor extends OutputStreamWriter {
+    // stream with flush() and close(). overriden.
+    private IntermediateOutputStream ios;
+    
+    // Has a private, internal byte[8192]
+    
+    /** Create a converter.
+     */
+    public WriteConvertor( IntermediateOutputStream out, String enc )
+	throws UnsupportedEncodingException
+    {
+	super( out, enc );
+	ios=out;
+    }
+    
+    /** Overriden - will do nothing but reset internal state.
+     */
+    public  final void close() throws IOException {
+	// NOTHING
+	// Calling super.close() would reset out and cb.
+    }
+    
+    /**
+     *  Flush the characters only
+     */ 
+    public  final void flush() throws IOException {
+	// Will flushBuffer and out()
+	// flushBuffer put any remaining chars in the byte[] 
+	super.flush();
+    }
+    
+    public  final void write(char cbuf[], int off, int len) throws IOException {
+	// will do the conversion and call write on the output stream
+	super.write( cbuf, off, len );
+    }
+    
+    /** Reset the buffer
+     */
+    public  final void recycle() {
+	ios.disable();
+	try {
+	    //	    System.out.println("Reseting writer");
+	    flush();
+	} catch( Exception ex ) {
+	    ex.printStackTrace();
+	}
+	ios.enable();
+    }
+    
+}
+
+
+/** Special output stream where close() is overriden, so super.close()
+    is never called.
+    
+    This allows recycling. It can also be disabled, so callbacks will
+    not be called if recycling the converter and if data was not flushed.
+*/
+final class IntermediateOutputStream extends OutputStream {
+    private ByteChunk tbuff;
+    private boolean enabled=true;
+    
+    public IntermediateOutputStream(ByteChunk tbuff) {
+	    this.tbuff=tbuff;
+    }
+    
+    public  final void close() throws IOException {
+	// shouldn't be called - we filter it out in writer
+	throw new IOException("close() called - shouldn't happen ");
+    }
+    
+    public  final void flush() throws IOException {
+	// nothing - write will go directly to the buffer,
+	// we don't keep any state
+    }
+    
+    public  final  void write(byte cbuf[], int off, int len) throws IOException {
+	// will do the conversion and call write on the output stream
+	if( enabled ) {
+	    tbuff.append( cbuf, off, len );
+	}
+    }
+    
+    public  final void write( int i ) throws IOException {
+	throw new IOException("write( int ) called - shouldn't happen ");
+    }
+
+    // -------------------- Internal methods --------------------
+
+    void setByteChunk( ByteChunk bb ) {
+	tbuff=bb;
+    }
+    
+    /** Temporary disable - this is used to recycle the converter without
+     *  generating an output if the buffers were not flushed
+     */
+    final void disable() {
+	enabled=false;
+    }
+
+    /** Reenable - used to recycle the converter
+     */
+    final void enable() {
+	enabled=true;
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/CharChunk.java b/connectors/util/java/org/apache/tomcat/util/buf/CharChunk.java
new file mode 100644
index 0000000..b60f40c
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/CharChunk.java
@@ -0,0 +1,692 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.buf;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * Utilities to manipluate char chunks. While String is
+ * the easiest way to manipulate chars ( search, substrings, etc),
+ * it is known to not be the most efficient solution - Strings are
+ * designed as imutable and secure objects.
+ * 
+ * @author dac@sun.com
+ * @author James Todd [gonzo@sun.com]
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ */
+public final class CharChunk implements Cloneable, Serializable {
+
+    // Input interface, used when the buffer is emptied.
+    public static interface CharInputChannel {
+        /** 
+         * Read new bytes ( usually the internal conversion buffer ).
+         * The implementation is allowed to ignore the parameters, 
+         * and mutate the chunk if it wishes to implement its own buffering.
+         */
+        public int realReadChars(char cbuf[], int off, int len)
+            throws IOException;
+    }
+    /**
+     *  When we need more space we'll either
+     *  grow the buffer ( up to the limit ) or send it to a channel.
+     */
+    public static interface CharOutputChannel {
+	/** Send the bytes ( usually the internal conversion buffer ).
+	 *  Expect 8k output if the buffer is full.
+	 */
+        public void realWriteChars(char cbuf[], int off, int len)
+            throws IOException;
+    }
+    
+    // -------------------- 
+    // char[]
+    private char buff[];
+
+    private int start;
+    private int end;
+
+    private boolean isSet=false;  // XXX 
+
+    private boolean isOutput=false;
+
+    // -1: grow undefinitely
+    // maximum amount to be cached
+    private int limit=-1;
+
+    private CharInputChannel in = null;
+    private CharOutputChannel out = null;
+    
+    private boolean optimizedWrite=true;
+
+    /**
+     * Creates a new, uninitialized CharChunk object.
+     */
+    public CharChunk() {
+    }
+
+    public CharChunk(int size) {
+	allocate( size, -1 );
+    }
+
+    // --------------------
+    
+    public CharChunk getClone() {
+	try {
+	    return (CharChunk)this.clone();
+	} catch( Exception ex) {
+	    return null;
+	}
+    }
+
+    public boolean isNull() {
+	if( end > 0 ) return false;
+	return !isSet; //XXX 
+    }
+    
+    /**
+     * Resets the message bytes to an uninitialized state.
+     */
+    public void recycle() {
+	//	buff=null;
+	isSet=false; // XXX
+	start=0;
+	end=0;
+    }
+
+    public void reset() {
+	buff=null;
+    }
+
+    // -------------------- Setup --------------------
+
+    public void allocate( int initial, int limit  ) {
+	isOutput=true;
+	if( buff==null || buff.length < initial ) {
+	    buff=new char[initial];
+	}
+	this.limit=limit;
+	start=0;
+	end=0;
+	isOutput=true;
+	isSet=true;
+    }
+
+
+    public void setOptimizedWrite(boolean optimizedWrite) {
+        this.optimizedWrite = optimizedWrite;
+    }
+
+    public void setChars( char[] c, int off, int len ) {
+	recycle();
+	isSet=true;
+	buff=c;
+	start=off;
+	end=start + len;
+	limit=end;
+    }
+
+    /** Maximum amount of data in this buffer.
+     *
+     *  If -1 or not set, the buffer will grow undefinitely.
+     *  Can be smaller than the current buffer size ( which will not shrink ).
+     *  When the limit is reached, the buffer will be flushed ( if out is set )
+     *  or throw exception.
+     */
+    public void setLimit(int limit) {
+	this.limit=limit;
+    }
+    
+    public int getLimit() {
+	return limit;
+    }
+
+    /**
+     * When the buffer is empty, read the data from the input channel.
+     */
+    public void setCharInputChannel(CharInputChannel in) {
+        this.in = in;
+    }
+
+    /** When the buffer is full, write the data to the output channel.
+     * 	Also used when large amount of data is appended.
+     *
+     *  If not set, the buffer will grow to the limit.
+     */
+    public void setCharOutputChannel(CharOutputChannel out) {
+	this.out=out;
+    }
+
+    // compat 
+    public char[] getChars()
+    {
+	return getBuffer();
+    }
+    
+    public char[] getBuffer()
+    {
+	return buff;
+    }
+    
+    /**
+     * Returns the start offset of the bytes.
+     * For output this is the end of the buffer.
+     */
+    public int getStart() {
+	return start;
+    }
+    
+    public int getOffset() {
+	return getStart();
+    }
+
+    /**
+     * Returns the start offset of the bytes.
+     */
+    public void setOffset(int off) {
+	start=off;
+    }
+
+    /**
+     * Returns the length of the bytes.
+     */
+    public int getLength() {
+	return end-start;
+    }
+
+
+    public int getEnd() {
+	return end;
+    }
+
+    public void setEnd( int i ) {
+	end=i;
+    }
+
+    // -------------------- Adding data --------------------
+    
+    public void append( char b )
+	throws IOException
+    {
+	makeSpace( 1 );
+
+	// couldn't make space
+	if( limit >0 && end >= limit ) {
+	    flushBuffer();
+	}
+	buff[end++]=b;
+    }
+    
+    public void append( CharChunk src )
+	throws IOException
+    {
+	append( src.getBuffer(), src.getOffset(), src.getLength());
+    }
+
+    /** Add data to the buffer
+     */
+    public void append( char src[], int off, int len )
+	throws IOException
+    {
+	// will grow, up to limit
+	makeSpace( len );
+
+	// if we don't have limit: makeSpace can grow as it wants
+	if( limit < 0 ) {
+	    // assert: makeSpace made enough space
+	    System.arraycopy( src, off, buff, end, len );
+	    end+=len;
+	    return;
+	}
+
+        // Optimize on a common case.
+        // If the source is going to fill up all the space in buffer, may
+        // as well write it directly to the output, and avoid an extra copy
+        if ( optimizedWrite && len == limit && end == start) {
+            out.realWriteChars( src, off, len );
+            return;
+        }
+	
+	// if we have limit and we're below
+	if( len <= limit - end ) {
+	    // makeSpace will grow the buffer to the limit,
+	    // so we have space
+	    System.arraycopy( src, off, buff, end, len );
+            
+	    end+=len;
+	    return;
+	}
+
+	// need more space than we can afford, need to flush
+	// buffer
+
+	// the buffer is already at ( or bigger than ) limit
+	
+	// Optimization:
+	// If len-avail < length ( i.e. after we fill the buffer with
+	// what we can, the remaining will fit in the buffer ) we'll just
+	// copy the first part, flush, then copy the second part - 1 write
+	// and still have some space for more. We'll still have 2 writes, but
+	// we write more on the first.
+
+	if( len + end < 2 * limit ) {
+	    /* If the request length exceeds the size of the output buffer,
+	       flush the output buffer and then write the data directly.
+	       We can't avoid 2 writes, but we can write more on the second
+	    */
+	    int avail=limit-end;
+	    System.arraycopy(src, off, buff, end, avail);
+	    end += avail;
+	    
+	    flushBuffer();
+	    
+	    System.arraycopy(src, off+avail, buff, end, len - avail);
+	    end+= len - avail;
+	    
+	} else {	// len > buf.length + avail
+	    // long write - flush the buffer and write the rest
+	    // directly from source
+	    flushBuffer();
+	    
+	    out.realWriteChars( src, off, len );
+	}
+    }
+
+
+    /** Add data to the buffer
+     */
+    public void append( StringBuffer sb )
+	throws IOException
+    {
+	int len=sb.length();
+
+	// will grow, up to limit
+	makeSpace( len );
+
+	// if we don't have limit: makeSpace can grow as it wants
+	if( limit < 0 ) {
+	    // assert: makeSpace made enough space
+	    sb.getChars(0, len, buff, end );
+	    end+=len;
+	    return;
+	}
+
+	int off=0;
+	int sbOff = off;
+	int sbEnd = off + len;
+	while (sbOff < sbEnd) {
+	    int d = min(limit - end, sbEnd - sbOff);
+	    sb.getChars( sbOff, sbOff+d, buff, end);
+	    sbOff += d;
+	    end += d;
+	    if (end >= limit)
+		flushBuffer();
+	}
+    }
+
+    /** Append a string to the buffer
+     */
+    public void append(String s) throws IOException {
+        append(s, 0, s.length());
+    }
+    
+    /** Append a string to the buffer
+     */
+    public void append(String s, int off, int len) throws IOException {
+	if (s==null) return;
+	
+	// will grow, up to limit
+	makeSpace( len );
+
+	// if we don't have limit: makeSpace can grow as it wants
+	if( limit < 0 ) {
+	    // assert: makeSpace made enough space
+	    s.getChars(off, off+len, buff, end );
+	    end+=len;
+	    return;
+	}
+
+	int sOff = off;
+	int sEnd = off + len;
+	while (sOff < sEnd) {
+	    int d = min(limit - end, sEnd - sOff);
+	    s.getChars( sOff, sOff+d, buff, end);
+	    sOff += d;
+	    end += d;
+	    if (end >= limit)
+		flushBuffer();
+	}
+    }
+    
+    // -------------------- Removing data from the buffer --------------------
+
+    public int substract()
+        throws IOException {
+
+        if ((end - start) == 0) {
+            if (in == null)
+                return -1;
+            int n = in.realReadChars(buff, end, buff.length - end);
+            if (n < 0)
+                return -1;
+        }
+
+        return (buff[start++]);
+
+    }
+
+    public int substract(CharChunk src)
+        throws IOException {
+
+        if ((end - start) == 0) {
+            if (in == null)
+                return -1;
+            int n = in.realReadChars( buff, end, buff.length - end);
+            if (n < 0)
+                return -1;
+        }
+
+        int len = getLength();
+        src.append(buff, start, len);
+        start = end;
+        return len;
+
+    }
+
+    public int substract( char src[], int off, int len )
+        throws IOException {
+
+        if ((end - start) == 0) {
+            if (in == null)
+                return -1;
+            int n = in.realReadChars( buff, end, buff.length - end);
+            if (n < 0)
+                return -1;
+        }
+
+        int n = len;
+        if (len > getLength()) {
+            n = getLength();
+        }
+        System.arraycopy(buff, start, src, off, n);
+        start += n;
+        return n;
+
+    }
+
+
+    public void flushBuffer()
+	throws IOException
+    {
+	//assert out!=null
+	if( out==null ) {
+	    throw new IOException( "Buffer overflow, no sink " + limit + " " +
+				   buff.length  );
+	}
+	out.realWriteChars( buff, start, end - start );
+	end=start;
+    }
+
+    /** Make space for len chars. If len is small, allocate
+     *	a reserve space too. Never grow bigger than limit.
+     */
+    private void makeSpace(int count)
+    {
+	char[] tmp = null;
+
+	int newSize;
+	int desiredSize=end + count;
+
+	// Can't grow above the limit
+	if( limit > 0 &&
+	    desiredSize > limit) {
+	    desiredSize=limit;
+	}
+
+	if( buff==null ) {
+	    if( desiredSize < 256 ) desiredSize=256; // take a minimum
+	    buff=new char[desiredSize];
+	}
+
+	// limit < buf.length ( the buffer is already big )
+	// or we already have space XXX
+	if( desiredSize <= buff.length) {
+	    return;
+	}
+	// grow in larger chunks
+	if( desiredSize < 2 * buff.length ) {
+	    newSize= buff.length * 2;
+	    if( limit >0 &&
+		newSize > limit ) newSize=limit;
+	    tmp=new char[newSize];
+	} else {
+	    newSize= buff.length * 2 + count ;
+	    if( limit > 0 &&
+		newSize > limit ) newSize=limit;
+	    tmp=new char[newSize];
+	}
+	
+	System.arraycopy(buff, start, tmp, start, end-start);
+	buff = tmp;
+	tmp = null;
+    }
+    
+    // -------------------- Conversion and getters --------------------
+
+    public String toString() {
+	if( buff==null ) return null;
+	return new String( buff, start, end-start);
+    }
+
+    public int getInt()
+    {
+	return Ascii.parseInt(buff, start,
+				end-start);
+    }
+    
+    // -------------------- equals --------------------
+
+    /**
+     * Compares the message bytes to the specified String object.
+     * @param s the String to compare
+     * @return true if the comparison succeeded, false otherwise
+     */
+    public boolean equals(String s) {
+	char[] c = buff;
+	int len = end-start;
+	if (c == null || len != s.length()) {
+	    return false;
+	}
+	int off = start;
+	for (int i = 0; i < len; i++) {
+	    if (c[off++] != s.charAt(i)) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    /**
+     * Compares the message bytes to the specified String object.
+     * @param s the String to compare
+     * @return true if the comparison succeeded, false otherwise
+     */
+    public boolean equalsIgnoreCase(String s) {
+	char[] c = buff;
+	int len = end-start;
+	if (c == null || len != s.length()) {
+	    return false;
+	}
+	int off = start;
+	for (int i = 0; i < len; i++) {
+	    if (Ascii.toLower( c[off++] ) != Ascii.toLower( s.charAt(i))) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    public boolean equals(CharChunk cc) {
+	return equals( cc.getChars(), cc.getOffset(), cc.getLength());
+    }
+
+    public boolean equals(char b2[], int off2, int len2) {
+	char b1[]=buff;
+	if( b1==null && b2==null ) return true;
+	
+	if (b1== null || b2==null || end-start != len2) {
+	    return false;
+	}
+	int off1 = start;
+	int len=end-start;
+	while ( len-- > 0) {
+	    if (b1[off1++] != b2[off2++]) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    public boolean equals(byte b2[], int off2, int len2) {
+	char b1[]=buff;
+	if( b2==null && b1==null ) return true;
+
+	if (b1== null || b2==null || end-start != len2) {
+	    return false;
+	}
+	int off1 = start;
+	int len=end-start;
+	
+	while ( len-- > 0) {
+	    if ( b1[off1++] != (char)b2[off2++]) {
+		return false;
+	    }
+	}
+	return true;
+    }
+    
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * @param s the string
+     */
+    public boolean startsWith(String s) {
+	char[] c = buff;
+	int len = s.length();
+	if (c == null || len > end-start) {
+	    return false;
+	}
+	int off = start;
+	for (int i = 0; i < len; i++) {
+	    if (c[off++] != s.charAt(i)) {
+		return false;
+	    }
+	}
+	return true;
+    }
+    
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * @param s the string
+     */
+    public boolean startsWithIgnoreCase(String s, int pos) {
+	char[] c = buff;
+	int len = s.length();
+	if (c == null || len+pos > end-start) {
+	    return false;
+	}
+	int off = start+pos;
+	for (int i = 0; i < len; i++) {
+	    if (Ascii.toLower( c[off++] ) != Ascii.toLower( s.charAt(i))) {
+		return false;
+	    }
+	}
+	return true;
+    }
+    
+
+    // -------------------- Hash code  --------------------
+
+    // normal hash. 
+    public int hash() {
+	int code=0;
+	for (int i = start; i < start + end-start; i++) {
+	    code = code * 37 + buff[i];
+	}
+	return code;
+    }
+
+    // hash ignoring case
+    public int hashIgnoreCase() {
+	int code=0;
+	for (int i = start; i < end; i++) {
+	    code = code * 37 + Ascii.toLower(buff[i]);
+	}
+	return code;
+    }
+
+    public int indexOf(char c) {
+	return indexOf( c, start);
+    }
+    
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * @param c the character
+     */
+    public int indexOf(char c, int starting) {
+	int ret = indexOf( buff, start+starting, end, c );
+	return (ret >= start) ? ret - start : -1;
+    }
+
+    public static int indexOf( char chars[], int off, int cend, char qq )
+    {
+	while( off < cend ) {
+	    char b=chars[off];
+	    if( b==qq )
+		return off;
+	    off++;
+	}
+	return -1;
+    }
+    
+
+    public int indexOf( String src, int srcOff, int srcLen, int myOff ) {
+	char first=src.charAt( srcOff );
+
+	// Look for first char 
+	int srcEnd = srcOff + srcLen;
+        
+	for( int i=myOff+start; i <= (end - srcLen); i++ ) {
+	    if( buff[i] != first ) continue;
+	    // found first char, now look for a match
+            int myPos=i+1;
+	    for( int srcPos=srcOff + 1; srcPos< srcEnd; ) {
+                if( buff[myPos++] != src.charAt( srcPos++ ))
+		    break;
+                if( srcPos==srcEnd ) return i-start; // found it
+	    }
+	}
+	return -1;
+    }
+
+    // -------------------- utils
+    private int min(int a, int b) {
+	if (a < b) return a;
+	return b;
+    }
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/DateTool.java b/connectors/util/java/org/apache/tomcat/util/buf/DateTool.java
new file mode 100644
index 0000000..4599963
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/DateTool.java
@@ -0,0 +1,164 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.buf;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ *  Common place for date utils.
+ *
+ * @deprecated Will be replaced with a more efficient impl, based on
+ * FastDateFormat, with an API using less objects.
+ * @author dac@eng.sun.com
+ * @author Jason Hunter [jch@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Costin Manolache
+ */
+public class DateTool {
+
+    /** US locale - all HTTP dates are in english
+     */
+    private final static Locale LOCALE_US = Locale.US;
+
+    /** GMT timezone - all HTTP dates are on GMT
+     */
+    public final static TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");
+
+    /** format for RFC 1123 date string -- "Sun, 06 Nov 1994 08:49:37 GMT"
+     */
+    public final static String RFC1123_PATTERN =
+        "EEE, dd MMM yyyy HH:mm:ss z";
+
+    // format for RFC 1036 date string -- "Sunday, 06-Nov-94 08:49:37 GMT"
+    public final static String rfc1036Pattern =
+        "EEEEEEEEE, dd-MMM-yy HH:mm:ss z";
+
+    // format for C asctime() date string -- "Sun Nov  6 08:49:37 1994"
+    public final static String asctimePattern =
+        "EEE MMM d HH:mm:ss yyyy";
+
+    /** Pattern used for old cookies
+     */
+    private final static String OLD_COOKIE_PATTERN = "EEE, dd-MMM-yyyy HH:mm:ss z";
+
+    /** DateFormat to be used to format dates. Called from MessageBytes
+     */
+    private final static DateFormat rfc1123Format =
+	new SimpleDateFormat(RFC1123_PATTERN, LOCALE_US);
+    
+    /** DateFormat to be used to format old netscape cookies
+	Called from ServerCookie
+     */
+    private final static DateFormat oldCookieFormat =
+	new SimpleDateFormat(OLD_COOKIE_PATTERN, LOCALE_US);
+    
+    private final static DateFormat rfc1036Format =
+	new SimpleDateFormat(rfc1036Pattern, LOCALE_US);
+    
+    private final static DateFormat asctimeFormat =
+	new SimpleDateFormat(asctimePattern, LOCALE_US);
+    
+    static {
+	rfc1123Format.setTimeZone(GMT_ZONE);
+	oldCookieFormat.setTimeZone(GMT_ZONE);
+	rfc1036Format.setTimeZone(GMT_ZONE);
+	asctimeFormat.setTimeZone(GMT_ZONE);
+    }
+ 
+    private static String rfc1123DS;
+    private static long   rfc1123Sec;
+
+    private static StringManager sm =
+        StringManager.getManager("org.apache.tomcat.util.buf.res");
+
+    // Called from MessageBytes.getTime()
+    static long parseDate( MessageBytes value ) {
+     	return parseDate( value.toString());
+    }
+
+    // Called from MessageBytes.setTime
+    /** 
+     */
+    public static String format1123( Date d ) {
+	String dstr=null;
+	synchronized(rfc1123Format) {
+	    dstr = format1123(d, rfc1123Format);
+	}
+	return dstr;
+    } 
+
+    public static String format1123( Date d,DateFormat df ) {
+        long dt = d.getTime() / 1000;
+        if ((rfc1123DS != null) && (dt == rfc1123Sec))
+            return rfc1123DS;
+        rfc1123DS  = df.format( d );
+        rfc1123Sec = dt;
+        return rfc1123DS;
+    } 
+
+
+    // Called from ServerCookie
+    /** 
+     */
+    public static void formatOldCookie( Date d, StringBuffer sb,
+					  FieldPosition fp )
+    {
+	synchronized(oldCookieFormat) {
+	    oldCookieFormat.format( d, sb, fp );
+	}
+    }
+
+    // Called from ServerCookie
+    public static String formatOldCookie( Date d )
+    {
+	String ocf=null;
+	synchronized(oldCookieFormat) {
+	    ocf= oldCookieFormat.format( d );
+	}
+	return ocf;
+    }
+
+    
+    /** Called from HttpServletRequest.getDateHeader().
+	Not efficient - but not very used.
+     */
+    public static long parseDate( String dateString ) {
+	DateFormat [] format = {rfc1123Format,rfc1036Format,asctimeFormat};
+	return parseDate(dateString,format);
+    }
+    public static long parseDate( String dateString, DateFormat []format ) {
+	Date date=null;
+	for(int i=0; i < format.length; i++) {
+	    try {
+		date = format[i].parse(dateString);
+		return date.getTime();
+	    } catch (ParseException e) { }
+	    catch (StringIndexOutOfBoundsException e) { }
+	}
+	String msg = sm.getString("httpDate.pe", dateString);
+	throw new IllegalArgumentException(msg);
+    }
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/HexUtils.java b/connectors/util/java/org/apache/tomcat/util/buf/HexUtils.java
new file mode 100644
index 0000000..2ad9ae0
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/HexUtils.java
@@ -0,0 +1,193 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.buf;
+
+import java.io.ByteArrayOutputStream;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Library of utility methods useful in dealing with converting byte arrays
+ * to and from strings of hexadecimal digits.
+ * Code from Ajp11, from Apache's JServ.
+ *
+ * @author Craig R. McClanahan
+ */
+
+public final class HexUtils {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     *  Table for HEX to DEC byte translation.
+     */
+    public static final int[] DEC = {
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        00, 01, 02, 03, 04, 05, 06, 07,  8,  9, -1, -1, -1, -1, -1, -1,
+        -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    };
+
+
+    /**
+     * Table for DEC to HEX byte translation.
+     */
+    public static final byte[] HEX = 
+    { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', 
+      (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', 
+      (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' };
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static StringManager sm =
+	StringManager.getManager("org.apache.tomcat.util.buf.res");
+
+
+    // --------------------------------------------------------- Static Methods
+
+
+    /**
+     * Convert a String of hexadecimal digits into the corresponding
+     * byte array by encoding each two hexadecimal digits as a byte.
+     *
+     * @param digits Hexadecimal digits representation
+     *
+     * @exception IllegalArgumentException if an invalid hexadecimal digit
+     *  is found, or the input string contains an odd number of hexadecimal
+     *  digits
+     */
+    public static byte[] convert(String digits) {
+
+	ByteArrayOutputStream baos = new ByteArrayOutputStream();
+	for (int i = 0; i < digits.length(); i += 2) {
+	    char c1 = digits.charAt(i);
+	    if ((i+1) >= digits.length())
+		throw new IllegalArgumentException
+		    (sm.getString("hexUtil.odd"));
+	    char c2 = digits.charAt(i + 1);
+	    byte b = 0;
+	    if ((c1 >= '0') && (c1 <= '9'))
+		b += ((c1 - '0') * 16);
+	    else if ((c1 >= 'a') && (c1 <= 'f'))
+		b += ((c1 - 'a' + 10) * 16);
+	    else if ((c1 >= 'A') && (c1 <= 'F'))
+		b += ((c1 - 'A' + 10) * 16);
+	    else
+		throw new IllegalArgumentException
+		    (sm.getString("hexUtil.bad"));
+	    if ((c2 >= '0') && (c2 <= '9'))
+		b += (c2 - '0');
+	    else if ((c2 >= 'a') && (c2 <= 'f'))
+		b += (c2 - 'a' + 10);
+	    else if ((c2 >= 'A') && (c2 <= 'F'))
+		b += (c2 - 'A' + 10);
+	    else
+		throw new IllegalArgumentException
+		    (sm.getString("hexUtil.bad"));
+	    baos.write(b);
+	}
+	return (baos.toByteArray());
+
+    }
+
+
+    /**
+     * Convert a byte array into a printable format containing a
+     * String of hexadecimal digit characters (two per byte).
+     *
+     * @param bytes Byte array representation
+     */
+    public static String convert(byte bytes[]) {
+
+	StringBuffer sb = new StringBuffer(bytes.length * 2);
+	for (int i = 0; i < bytes.length; i++) {
+	    sb.append(convertDigit((int) (bytes[i] >> 4)));
+	    sb.append(convertDigit((int) (bytes[i] & 0x0f)));
+	}
+	return (sb.toString());
+
+    }
+
+    /**
+     * Convert 4 hex digits to an int, and return the number of converted
+     * bytes.
+     *
+     * @param hex Byte array containing exactly four hexadecimal digits
+     *
+     * @exception IllegalArgumentException if an invalid hexadecimal digit
+     *  is included
+     */
+    public static int convert2Int( byte[] hex ) {
+	// Code from Ajp11, from Apache's JServ
+    
+	// assert b.length==4
+	// assert valid data
+	int len;
+	if(hex.length < 4 ) return 0;
+	if( DEC[hex[0]]<0 )
+	    throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
+	len = DEC[hex[0]];
+	len = len << 4;
+	if( DEC[hex[1]]<0 )
+	    throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
+	len += DEC[hex[1]];
+	len = len << 4;
+	if( DEC[hex[2]]<0 )
+	    throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
+	len += DEC[hex[2]];
+	len = len << 4;
+	if( DEC[hex[3]]<0 )
+	    throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
+	len += DEC[hex[3]];
+	return len;
+    }
+
+
+
+    /**
+     * [Private] Convert the specified value (0 .. 15) to the corresponding
+     * hexadecimal digit.
+     *
+     * @param value Value to be converted
+     */
+    private static char convertDigit(int value) {
+
+	value &= 0x0f;
+	if (value >= 10)
+	    return ((char) (value - 10 + 'a'));
+	else
+	    return ((char) (value + '0'));
+
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/MessageBytes.java b/connectors/util/java/org/apache/tomcat/util/buf/MessageBytes.java
new file mode 100644
index 0000000..2cb09da
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/MessageBytes.java
@@ -0,0 +1,668 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.buf;
+
+import java.text.*;
+import java.util.*;
+import java.io.Serializable;
+import java.io.IOException;
+
+/**
+ * This class is used to represent a subarray of bytes in an HTTP message.
+ * It represents all request/response elements. The byte/char conversions are
+ * delayed and cached. Everything is recyclable.
+ *
+ * The object can represent a byte[], a char[], or a (sub) String. All
+ * operations can be made in case sensitive mode or not.
+ *
+ * @author dac@eng.sun.com
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Costin Manolache
+ */
+public final class MessageBytes implements Cloneable, Serializable {
+    // primary type ( whatever is set as original value )
+    private int type = T_NULL;
+
+    public static final int T_NULL = 0;
+    /** getType() is T_STR if the the object used to create the MessageBytes
+        was a String */
+    public static final int T_STR  = 1;
+    /** getType() is T_STR if the the object used to create the MessageBytes
+        was a byte[] */ 
+    public static final int T_BYTES = 2;
+    /** getType() is T_STR if the the object used to create the MessageBytes
+        was a char[] */ 
+    public static final int T_CHARS = 3;
+
+    private int hashCode=0;
+    // did we computed the hashcode ? 
+    private boolean hasHashCode=false;
+
+    // Is the represented object case sensitive ?
+    private boolean caseSensitive=true;
+
+    // Internal objects to represent array + offset, and specific methods
+    private ByteChunk byteC=new ByteChunk();
+    private CharChunk charC=new CharChunk();
+    
+    // String
+    private String strValue;
+    // true if a String value was computed. Probably not needed,
+    // strValue!=null is the same
+    private boolean hasStrValue=false;
+
+    /**
+     * Creates a new, uninitialized MessageBytes object.
+     * @deprecated Use static newInstance() in order to allow
+     *   future hooks.
+     */
+    public MessageBytes() {
+    }
+
+    /** Construct a new MessageBytes instance
+     */
+    public static MessageBytes newInstance() {
+	return factory.newInstance();
+    }
+
+    /** Configure the case sensitivity
+     */
+    public void setCaseSenitive( boolean b ) {
+	caseSensitive=b;
+    }
+
+    public MessageBytes getClone() {
+	try {
+	    return (MessageBytes)this.clone();
+	} catch( Exception ex) {
+	    return null;
+	}
+    }
+
+    public boolean isNull() {
+//		should we check also hasStrValue ???
+		return byteC.isNull() && charC.isNull() && ! hasStrValue;
+	// bytes==null && strValue==null;
+    }
+    
+    /**
+     * Resets the message bytes to an uninitialized (NULL) state.
+     */
+    public void recycle() {
+	type=T_NULL;
+	byteC.recycle();
+	charC.recycle();
+
+	strValue=null;
+	caseSensitive=true;
+
+	hasStrValue=false;
+	hasHashCode=false;
+	hasIntValue=false;
+    hasLongValue=false;
+	hasDateValue=false;	
+    }
+
+
+    /**
+     * Sets the content to the specified subarray of bytes.
+     *
+     * @param b the ascii bytes
+     * @param off the start offset of the bytes
+     * @param len the length of the bytes
+     */
+    public void setBytes(byte[] b, int off, int len) {
+	recycle(); // a new value is set, cached values must reset
+	byteC.setBytes( b, off, len );
+	type=T_BYTES;
+    }
+
+    /** Set the encoding. If the object was constructed from bytes[]. any
+     *  previous conversion is reset.
+     *  If no encoding is set, we'll use 8859-1.
+     */
+    public void setEncoding( String enc ) {
+	if( !byteC.isNull() ) {
+	    // if the encoding changes we need to reset the converion results
+	    charC.recycle();
+	    hasStrValue=false;
+	}
+	byteC.setEncoding(enc);
+    }
+
+    /** Sets the content to be a char[]
+     */
+    public void setChars( char[] c, int off, int len ) {
+	recycle();
+	charC.setChars( c, off, len );
+	type=T_CHARS;
+    }
+
+    /** Remove the cached string value. Use it after a conversion on the
+     *	byte[] or after the encoding is changed
+     *  XXX Is this needed ?
+     */
+    public void resetStringValue() {
+	if( type != T_STR ) {
+	    // If this was cread as a byte[] or char[], we remove
+	    // the old string value
+	    hasStrValue=false;
+	    strValue=null;
+	}
+    }
+
+    /** Set the content to be a string
+     */
+    public void setString( String s ) {
+	recycle();
+        if (s == null)
+            return;
+	strValue=s;
+	hasStrValue=true;
+	type=T_STR;
+    }
+
+    // -------------------- Conversion and getters --------------------
+
+    /** Compute the string value
+     */
+    public String toString() {
+	if( hasStrValue ) return strValue;
+	hasStrValue=true;
+	
+	switch (type) {
+	case T_CHARS:
+	    strValue=charC.toString();
+	    return strValue;
+	case T_BYTES:
+	    strValue=byteC.toString();
+	    return strValue;
+	}
+	return null;
+    }
+
+    //----------------------------------------
+    /** Return the type of the original content. Can be
+     *  T_STR, T_BYTES, T_CHARS or T_NULL
+     */
+    public int getType() {
+	return type;
+    }
+    
+    /**
+     * Returns the byte chunk, representing the byte[] and offset/length.
+     * Valid only if T_BYTES or after a conversion was made.
+     */
+    public ByteChunk getByteChunk() {
+	return byteC;
+    }
+
+    /**
+     * Returns the char chunk, representing the char[] and offset/length.
+     * Valid only if T_CHARS or after a conversion was made.
+     */
+    public CharChunk getCharChunk() {
+	return charC;
+    }
+
+    /**
+     * Returns the string value.
+     * Valid only if T_STR or after a conversion was made.
+     */
+    public String getString() {
+	return strValue;
+    }
+
+    /** Unimplemented yet. Do a char->byte conversion.
+     */
+    public void toBytes() {
+        if( ! byteC.isNull() ) {
+            type=T_BYTES;
+            return;
+        }
+        toString();
+        type=T_BYTES;
+        byte bb[] = strValue.getBytes();
+        byteC.setBytes(bb, 0, bb.length);
+    }
+
+    /** Convert to char[] and fill the CharChunk.
+     *  XXX Not optimized - it converts to String first.
+     */
+    public void toChars() {
+	if( ! charC.isNull() ) {
+            type=T_CHARS;
+	    return;
+	}
+	// inefficient
+	toString();
+        type=T_CHARS;
+	char cc[]=strValue.toCharArray();
+	charC.setChars(cc, 0, cc.length);
+    }
+    
+
+    /**
+     * Returns the length of the original buffer.
+     * Note that the length in bytes may be different from the length
+     * in chars.
+     */
+    public int getLength() {
+	if(type==T_BYTES)
+	    return byteC.getLength();
+	if(type==T_CHARS) {
+	    return charC.getLength();
+	}
+	if(type==T_STR)
+	    return strValue.length();
+	toString();
+	if( strValue==null ) return 0;
+	return strValue.length();
+    }
+
+    // -------------------- equals --------------------
+
+    /**
+     * Compares the message bytes to the specified String object.
+     * @param s the String to compare
+     * @return true if the comparison succeeded, false otherwise
+     */
+    public boolean equals(String s) {
+	if( ! caseSensitive )
+	    return equalsIgnoreCase( s );
+	switch (type) {
+	case T_STR:
+	    if( strValue==null && s!=null) return false;
+	    return strValue.equals( s );
+	case T_CHARS:
+	    return charC.equals( s );
+	case T_BYTES:
+	    return byteC.equals( s );
+	default:
+	    return false;
+	}
+    }
+
+    /**
+     * Compares the message bytes to the specified String object.
+     * @param s the String to compare
+     * @return true if the comparison succeeded, false otherwise
+     */
+    public boolean equalsIgnoreCase(String s) {
+	switch (type) {
+	case T_STR:
+	    if( strValue==null && s!=null) return false;
+	    return strValue.equalsIgnoreCase( s );
+	case T_CHARS:
+	    return charC.equalsIgnoreCase( s );
+	case T_BYTES:
+	    return byteC.equalsIgnoreCase( s );
+	default:
+	    return false;
+	}
+    }
+
+    public boolean equals(MessageBytes mb) {
+	switch (type) {
+	case T_STR:
+	    return mb.equals( strValue );
+	}
+
+	if( mb.type != T_CHARS &&
+	    mb.type!= T_BYTES ) {
+	    // it's a string or int/date string value
+	    return equals( mb.toString() );
+	}
+
+	// mb is either CHARS or BYTES.
+	// this is either CHARS or BYTES
+	// Deal with the 4 cases ( in fact 3, one is simetric)
+	
+	if( mb.type == T_CHARS && type==T_CHARS ) {
+	    return charC.equals( mb.charC );
+	} 
+	if( mb.type==T_BYTES && type== T_BYTES ) {
+	    return byteC.equals( mb.byteC );
+	}
+	if( mb.type== T_CHARS && type== T_BYTES ) {
+	    return byteC.equals( mb.charC );
+	}
+	if( mb.type== T_BYTES && type== T_CHARS ) {
+	    return mb.byteC.equals( charC );
+	}
+	// can't happen
+	return true;
+    }
+
+    
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * @param s the string
+     */
+    public boolean startsWith(String s) {
+	switch (type) {
+	case T_STR:
+	    return strValue.startsWith( s );
+	case T_CHARS:
+	    return charC.startsWith( s );
+	case T_BYTES:
+	    return byteC.startsWith( s );
+	default:
+	    return false;
+	}
+    }
+
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * @param s the string
+     * @param pos The start position
+     */
+    public boolean startsWithIgnoreCase(String s, int pos) {
+	switch (type) {
+	case T_STR:
+	    if( strValue==null ) return false;
+	    if( strValue.length() < pos + s.length() ) return false;
+	    
+	    for( int i=0; i<s.length(); i++ ) {
+		if( Ascii.toLower( s.charAt( i ) ) !=
+		    Ascii.toLower( strValue.charAt( pos + i ))) {
+		    return false;
+		}
+	    }
+	    return true;
+	case T_CHARS:
+	    return charC.startsWithIgnoreCase( s, pos );
+	case T_BYTES:
+	    return byteC.startsWithIgnoreCase( s, pos );
+	default:
+	    return false;
+	}
+    }
+
+    
+
+    // -------------------- Hash code  --------------------
+    public  int hashCode() {
+	if( hasHashCode ) return hashCode;
+	int code = 0;
+
+	if( caseSensitive ) 
+	    code=hash(); 
+	else
+	    code=hashIgnoreCase();
+	hashCode=code;
+	hasHashCode=true;
+	return code;
+    }
+
+    // normal hash. 
+    private int hash() {
+	int code=0;
+	switch (type) {
+	case T_STR:
+	    // We need to use the same hash function
+	    for (int i = 0; i < strValue.length(); i++) {
+		code = code * 37 + strValue.charAt( i );
+	    }
+	    return code;
+	case T_CHARS:
+	    return charC.hash();
+	case T_BYTES:
+	    return byteC.hash();
+	default:
+	    return 0;
+	}
+    }
+
+    // hash ignoring case
+    private int hashIgnoreCase() {
+	int code=0;
+	switch (type) {
+	case T_STR:
+	    for (int i = 0; i < strValue.length(); i++) {
+		code = code * 37 + Ascii.toLower(strValue.charAt( i ));
+	    }
+	    return code;
+	case T_CHARS:
+	    return charC.hashIgnoreCase();
+	case T_BYTES:
+	    return byteC.hashIgnoreCase();
+	default:
+	    return 0;
+	}
+    }
+
+    public int indexOf(char c) {
+	return indexOf( c, 0);
+    }
+
+    // Inefficient initial implementation. Will be replaced on the next
+    // round of tune-up
+    public int indexOf(String s, int starting) {
+	toString();
+	return strValue.indexOf( s, starting );
+    }
+    
+    // Inefficient initial implementation. Will be replaced on the next
+    // round of tune-up
+    public int indexOf(String s) {
+	return indexOf( s, 0 );
+    }
+    
+    public int indexOfIgnoreCase(String s, int starting) {
+	toString();
+	String upper=strValue.toUpperCase();
+	String sU=s.toUpperCase();
+	return upper.indexOf( sU, starting );
+    }
+    
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * @param c the character
+     * @param starting The start position
+     */
+    public int indexOf(char c, int starting) {
+	switch (type) {
+	case T_STR:
+	    return strValue.indexOf( c, starting );
+	case T_CHARS:
+	    return charC.indexOf( c, starting);
+	case T_BYTES:
+	    return byteC.indexOf( c, starting );
+	default:
+	    return -1;
+	}
+    }
+
+    /** Copy the src into this MessageBytes, allocating more space if
+     *  needed
+     */
+    public void duplicate( MessageBytes src ) throws IOException
+    {
+	switch( src.getType() ) {
+	case MessageBytes.T_BYTES:
+	    type=T_BYTES;
+	    ByteChunk bc=src.getByteChunk();
+	    byteC.allocate( 2 * bc.getLength(), -1 );
+	    byteC.append( bc );
+	    break;
+	case MessageBytes.T_CHARS:
+	    type=T_CHARS;
+	    CharChunk cc=src.getCharChunk();
+	    charC.allocate( 2 * cc.getLength(), -1 );
+	    charC.append( cc );
+	    break;
+	case MessageBytes.T_STR:
+	    type=T_STR;
+	    String sc=src.getString();
+	    this.setString( sc );
+	    break;
+	}
+    }
+
+    // -------------------- Deprecated code --------------------
+    // efficient int, long and date
+    // XXX used only for headers - shouldn't be
+    // stored here.
+    private int intValue;
+    private boolean hasIntValue=false;
+    private long longValue;
+    private boolean hasLongValue=false;
+    private Date dateValue;
+    private boolean hasDateValue=false;
+    
+    /**
+     *  @deprecated The buffer are general purpose, caching for headers should
+     *  be done in headers. The second parameter allows us to pass a date format
+     * instance to avoid synchronization problems.
+     */
+    public void setTime(long t, DateFormat df) {
+	// XXX replace it with a byte[] tool
+	recycle();
+	if( dateValue==null)
+	    dateValue=new Date(t);
+	else
+	    dateValue.setTime(t);
+	if( df==null )
+	    strValue=DateTool.format1123(dateValue);
+	else
+	    strValue=DateTool.format1123(dateValue,df);
+	hasStrValue=true;
+	hasDateValue=true;
+	type=T_STR;   
+    }
+
+    public void setTime(long t) {
+	setTime( t, null );
+    }
+
+    /** Set the buffer to the representation of an int
+     */
+    public void setInt(int i) {
+	recycle();
+        byteC.allocate(16, 32);
+        int current = i;
+        byte[] buf = byteC.getBuffer();
+        int start = 0;
+        int end = 0;
+        if (i == 0) {
+            buf[end++] = (byte) '0';
+        }
+        if (i < 0) {
+            current = -i;
+            buf[end++] = (byte) '-';
+        }
+        while (current > 0) {
+            int digit = current % 10;
+            current = current / 10;
+            buf[end++] = HexUtils.HEX[digit];
+        }
+        byteC.setEnd(end);
+        // Inverting buffer
+        end--;
+        if (i < 0) {
+            start++;
+        }
+        while (end > start) {
+            byte temp = buf[start];
+            buf[start] = buf[end];
+            buf[end] = temp;
+            start++;
+            end--;
+        }
+        intValue=i;
+        hasIntValue=true;
+        type=T_BYTES;
+    }
+
+
+    /**
+     *  @deprecated The buffer are general purpose, caching for headers should
+     *  be done in headers
+     */
+    public  long getTime()
+    {
+     	if( hasDateValue ) {
+	    if( dateValue==null) return -1;
+	    return dateValue.getTime();
+     	}
+	
+     	long l=DateTool.parseDate( this );
+     	if( dateValue==null)
+     	    dateValue=new Date(l);
+     	else
+     	    dateValue.setTime(l);
+     	hasDateValue=true;
+     	return l;
+    }
+    
+
+    // Used for headers conversion
+    /** Convert the buffer to an int, cache the value
+     */ 
+    public int getInt() 
+    {
+	if( hasIntValue )
+	    return intValue;
+	
+	switch (type) {
+	case T_BYTES:
+	    intValue=byteC.getInt();
+	    break;
+	default:
+	    intValue=Integer.parseInt(toString());
+	}
+	hasIntValue=true;
+	return intValue;
+    }
+
+    // Used for headers conversion
+    /** Convert the buffer to an long, cache the value
+     */ 
+    public long getLong() {
+        if( hasLongValue )
+            return longValue;
+        
+        switch (type) {
+        case T_BYTES:
+            longValue=byteC.getLong();
+            break;
+        default:
+            longValue=Long.parseLong(toString());
+        }
+
+        hasLongValue=true;
+        return longValue;
+
+     }
+
+    // -------------------- Future may be different --------------------
+    
+    private static MessageBytesFactory factory=new MessageBytesFactory();
+
+    public static void setFactory( MessageBytesFactory mbf ) {
+	factory=mbf;
+    }
+    
+    public static class MessageBytesFactory {
+	protected MessageBytesFactory() {
+	}
+	public MessageBytes newInstance() {
+	    return new MessageBytes();
+	}
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/TimeStamp.java b/connectors/util/java/org/apache/tomcat/util/buf/TimeStamp.java
new file mode 100644
index 0000000..90bab82
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/TimeStamp.java
@@ -0,0 +1,155 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.buf;
+
+import java.io.Serializable;
+
+// XXX Shouldn't be here - has nothing to do with buffers.
+
+/**
+ * Main tool for object expiry. 
+ * Marks creation and access time of an "expirable" object,
+ * and extra properties like "id", "valid", etc.
+ *
+ * Used for objects that expire - originally Sessions, but 
+ * also Contexts, Servlets, cache - or any other object that
+ * expires.
+ * 
+ * @author Costin Manolache
+ */
+public final class TimeStamp implements  Serializable {
+    private long creationTime = 0L;
+    private long lastAccessedTime = creationTime;
+    private long thisAccessedTime = creationTime;
+    private boolean isNew = true;
+    private long maxInactiveInterval = -1;
+    private boolean isValid = false;
+    MessageBytes name;
+    int id=-1;
+    
+    Object parent;
+    
+    public TimeStamp() {
+    }
+
+    // -------------------- Active methods --------------------
+
+    /**
+     *  Access notification. This method takes a time parameter in order
+     *  to allow callers to efficiently manage expensive calls to
+     *  System.currentTimeMillis() 
+     */
+    public void touch(long time) {
+	this.lastAccessedTime = this.thisAccessedTime;
+	this.thisAccessedTime = time;
+	this.isNew=false;
+    }
+
+    // -------------------- Property access --------------------
+
+    /** Return the "name" of the timestamp. This can be used
+     *  to associate unique identifier with each timestamped object.
+     *  The name is a MessageBytes - i.e. a modifiable byte[] or char[]. 
+     */
+    public MessageBytes getName() {
+	if( name==null ) name=MessageBytes.newInstance();//lazy
+	return name;
+    }
+
+    /** Each object can have an unique id, similar with name but
+     *  providing faster access ( array vs. hashtable lookup )
+     */
+    public int getId() {
+	return id;
+    }
+
+    public void setId( int id ) {
+	this.id=id;
+    }
+    
+    /** Returns the owner of this stamp ( the object that is
+     *  time-stamped ).
+     *  For a 
+     */
+    public void setParent( Object o ) {
+	parent=o;
+    }
+
+    public Object getParent() {
+	return parent;
+    }
+
+    public void setCreationTime(long time) {
+	this.creationTime = time;
+	this.lastAccessedTime = time;
+	this.thisAccessedTime = time;
+    }
+
+
+    public long getLastAccessedTime() {
+	return lastAccessedTime;
+    }
+
+    public long getThisAccessedTime() {
+        return thisAccessedTime;
+    }
+
+    /** Inactive interval in millis - the time is computed
+     *  in millis, convert to secs in the upper layer
+     */
+    public long getMaxInactiveInterval() {
+	return maxInactiveInterval;
+    }
+
+    public void setMaxInactiveInterval(long interval) {
+	maxInactiveInterval = interval;
+    }
+
+    public boolean isValid() {
+	return isValid;
+    }
+
+    public void setValid(boolean isValid) {
+	this.isValid = isValid;
+    }
+
+    public boolean isNew() {
+	return isNew;
+    }
+
+    public void setNew(boolean isNew) {
+	this.isNew = isNew;
+    }
+
+    public long getCreationTime() {
+	return creationTime;
+    }
+
+    // -------------------- Maintainance --------------------
+
+    public void recycle() {
+	creationTime = 0L;
+	lastAccessedTime = 0L;
+	maxInactiveInterval = -1;
+	isNew = true;
+	isValid = false;
+	id=-1;
+	if( name!=null) name.recycle();
+    }
+
+}
+
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/UDecoder.java b/connectors/util/java/org/apache/tomcat/util/buf/UDecoder.java
new file mode 100644
index 0000000..391d16f
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/UDecoder.java
@@ -0,0 +1,272 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.buf;
+
+import java.io.CharConversionException;
+import java.io.IOException;
+
+/** 
+ *  All URL decoding happens here. This way we can reuse, review, optimize
+ *  without adding complexity to the buffers.
+ *
+ *  The conversion will modify the original buffer.
+ * 
+ *  @author Costin Manolache
+ */
+public final class UDecoder {
+    
+    public UDecoder() 
+    {
+    }
+
+    /** URLDecode, will modify the source.  Includes converting
+     *  '+' to ' '.
+     */
+    public void convert( ByteChunk mb )
+        throws IOException
+    {
+        convert(mb, true);
+    }
+
+    /** URLDecode, will modify the source.
+     */
+    public void convert( ByteChunk mb, boolean query )
+	throws IOException
+    {
+	int start=mb.getOffset();
+	byte buff[]=mb.getBytes();
+	int end=mb.getEnd();
+
+	int idx= ByteChunk.indexOf( buff, start, end, '%' );
+        int idx2=-1;
+        if( query )
+            idx2= ByteChunk.indexOf( buff, start, end, '+' );
+	if( idx<0 && idx2<0 ) {
+	    return;
+	}
+
+	// idx will be the smallest positive inxes ( first % or + )
+	if( idx2 >= 0 && idx2 < idx ) idx=idx2;
+	if( idx < 0 ) idx=idx2;
+
+	for( int j=idx; j<end; j++, idx++ ) {
+	    if( buff[ j ] == '+' && query) {
+		buff[idx]= (byte)' ' ;
+	    } else if( buff[ j ] != '%' ) {
+		buff[idx]= buff[j];
+	    } else {
+		// read next 2 digits
+		if( j+2 >= end ) {
+		    throw new CharConversionException("EOF");
+		}
+		byte b1= buff[j+1];
+		byte b2=buff[j+2];
+		if( !isHexDigit( b1 ) || ! isHexDigit(b2 ))
+		    throw new CharConversionException( "isHexDigit");
+		
+		j+=2;
+		int res=x2c( b1, b2 );
+		buff[idx]=(byte)res;
+	    }
+	}
+
+	mb.setEnd( idx );
+	
+	return;
+    }
+
+    // -------------------- Additional methods --------------------
+    // XXX What do we do about charset ????
+
+    /** In-buffer processing - the buffer will be modified
+     *  Includes converting  '+' to ' '.
+     */
+    public void convert( CharChunk mb )
+	throws IOException
+    {
+        convert(mb, true);
+    }
+
+    /** In-buffer processing - the buffer will be modified
+     */
+    public void convert( CharChunk mb, boolean query )
+	throws IOException
+    {
+	//	log( "Converting a char chunk ");
+	int start=mb.getOffset();
+	char buff[]=mb.getBuffer();
+	int cend=mb.getEnd();
+
+	int idx= CharChunk.indexOf( buff, start, cend, '%' );
+        int idx2=-1;
+        if( query )
+            idx2= CharChunk.indexOf( buff, start, cend, '+' );
+	if( idx<0 && idx2<0 ) {
+	    return;
+	}
+	
+	if( idx2 >= 0 && idx2 < idx ) idx=idx2; 
+	if( idx < 0 ) idx=idx2;
+
+	for( int j=idx; j<cend; j++, idx++ ) {
+	    if( buff[ j ] == '+' && query ) {
+		buff[idx]=( ' ' );
+	    } else if( buff[ j ] != '%' ) {
+		buff[idx]=buff[j];
+	    } else {
+		// read next 2 digits
+		if( j+2 >= cend ) {
+		    // invalid
+		    throw new CharConversionException("EOF");
+		}
+		char b1= buff[j+1];
+		char b2=buff[j+2];
+		if( !isHexDigit( b1 ) || ! isHexDigit(b2 ))
+		    throw new CharConversionException("isHexDigit");
+		
+		j+=2;
+		int res=x2c( b1, b2 );
+		buff[idx]=(char)res;
+	    }
+	}
+	mb.setEnd( idx );
+    }
+
+    /** URLDecode, will modify the source
+     *  Includes converting  '+' to ' '.
+     */
+    public void convert(MessageBytes mb)
+	throws IOException
+    {
+        convert(mb, true);
+    }
+
+    /** URLDecode, will modify the source
+     */
+    public void convert(MessageBytes mb, boolean query)
+	throws IOException
+    {
+	
+	switch (mb.getType()) {
+	case MessageBytes.T_STR:
+	    String strValue=mb.toString();
+	    if( strValue==null ) return;
+	    mb.setString( convert( strValue, query ));
+	    break;
+	case MessageBytes.T_CHARS:
+	    CharChunk charC=mb.getCharChunk();
+	    convert( charC, query );
+	    break;
+	case MessageBytes.T_BYTES:
+	    ByteChunk bytesC=mb.getByteChunk();
+	    convert( bytesC, query );
+	    break;
+	}
+    }
+
+    // XXX Old code, needs to be replaced !!!!
+    // 
+    public final String convert(String str)
+    {
+        return convert(str, true);
+    }
+
+    public final String convert(String str, boolean query)
+    {
+        if (str == null)  return  null;
+	
+	if( (!query || str.indexOf( '+' ) < 0) && str.indexOf( '%' ) < 0 )
+	    return str;
+	
+        StringBuffer dec = new StringBuffer();    // decoded string output
+        int strPos = 0;
+        int strLen = str.length();
+
+        dec.ensureCapacity(str.length());
+        while (strPos < strLen) {
+            int laPos;        // lookahead position
+
+            // look ahead to next URLencoded metacharacter, if any
+            for (laPos = strPos; laPos < strLen; laPos++) {
+                char laChar = str.charAt(laPos);
+                if ((laChar == '+' && query) || (laChar == '%')) {
+                    break;
+                }
+            }
+
+            // if there were non-metacharacters, copy them all as a block
+            if (laPos > strPos) {
+                dec.append(str.substring(strPos,laPos));
+                strPos = laPos;
+            }
+
+            // shortcut out of here if we're at the end of the string
+            if (strPos >= strLen) {
+                break;
+            }
+
+            // process next metacharacter
+            char metaChar = str.charAt(strPos);
+            if (metaChar == '+') {
+                dec.append(' ');
+                strPos++;
+                continue;
+            } else if (metaChar == '%') {
+		// We throw the original exception - the super will deal with
+		// it
+		//                try {
+		dec.append((char)Integer.
+			   parseInt(str.substring(strPos + 1, strPos + 3),16));
+                strPos += 3;
+            }
+        }
+
+        return dec.toString();
+    }
+
+
+
+    private static boolean isHexDigit( int c ) {
+	return ( ( c>='0' && c<='9' ) ||
+		 ( c>='a' && c<='f' ) ||
+		 ( c>='A' && c<='F' ));
+    }
+    
+    private static int x2c( byte b1, byte b2 ) {
+	int digit= (b1>='A') ? ( (b1 & 0xDF)-'A') + 10 :
+	    (b1 -'0');
+	digit*=16;
+	digit +=(b2>='A') ? ( (b2 & 0xDF)-'A') + 10 :
+	    (b2 -'0');
+	return digit;
+    }
+
+    private static int x2c( char b1, char b2 ) {
+	int digit= (b1>='A') ? ( (b1 & 0xDF)-'A') + 10 :
+	    (b1 -'0');
+	digit*=16;
+	digit +=(b2>='A') ? ( (b2 & 0xDF)-'A') + 10 :
+	    (b2 -'0');
+	return digit;
+    }
+
+    private final static int debug=0;
+    private static void log( String s ) {
+	System.out.println("URLDecoder: " + s );
+    }
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/UEncoder.java b/connectors/util/java/org/apache/tomcat/util/buf/UEncoder.java
new file mode 100644
index 0000000..ebf44b2
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/UEncoder.java
@@ -0,0 +1,177 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.buf;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.BitSet;
+
+/** Efficient implementation for encoders.
+ *  This class is not thread safe - you need one encoder per thread.
+ *  The encoder will save and recycle the internal objects, avoiding
+ *  garbage.
+ * 
+ *  You can add extra characters that you want preserved, for example
+ *  while encoding a URL you can add "/".
+ *
+ *  @author Costin Manolache
+ */
+public final class UEncoder {
+
+    // Not static - the set may differ ( it's better than adding
+    // an extra check for "/", "+", etc
+    private BitSet safeChars=null;
+    private C2BConverter c2b=null;
+    private ByteChunk bb=null;
+
+    private String encoding="UTF8";
+    private static final int debug=0;
+    
+    public UEncoder() {
+	initSafeChars();
+    }
+
+    public void setEncoding( String s ) {
+	encoding=s;
+    }
+
+    public void addSafeCharacter( char c ) {
+	safeChars.set( c );
+    }
+
+
+    /** URL Encode string, using a specified encoding.
+     *
+     * @param buf The writer
+     * @param s string to be encoded
+     * @throws IOException If an I/O error occurs
+     */
+    public void urlEncode( Writer buf, String s )
+	throws IOException
+    {
+	if( c2b==null ) {
+	    bb=new ByteChunk(16); // small enough.
+	    c2b=new C2BConverter( bb, encoding );
+	}
+
+	for (int i = 0; i < s.length(); i++) {
+	    int c = (int) s.charAt(i);
+	    if( safeChars.get( c ) ) {
+		if( debug > 0 ) log("Safe: " + (char)c);
+		buf.write((char)c);
+	    } else {
+		if( debug > 0 ) log("Unsafe:  " + (char)c);
+		c2b.convert( (char)c );
+		
+		// "surrogate" - UTF is _not_ 16 bit, but 21 !!!!
+		// ( while UCS is 31 ). Amazing...
+		if (c >= 0xD800 && c <= 0xDBFF) {
+		    if ( (i+1) < s.length()) {
+			int d = (int) s.charAt(i+1);
+			if (d >= 0xDC00 && d <= 0xDFFF) {
+			    if( debug > 0 ) log("Unsafe:  " + c);
+			    c2b.convert( (char)d);
+			    i++;
+			}
+		    }
+		}
+
+		c2b.flushBuffer();
+		
+		urlEncode( buf, bb.getBuffer(), bb.getOffset(),
+			   bb.getLength() );
+		bb.recycle();
+	    }
+	}
+    }
+
+    /**
+     */
+    public void urlEncode( Writer buf, byte bytes[], int off, int len)
+	throws IOException
+    {
+	for( int j=off; j< len; j++ ) {
+	    buf.write( '%' );
+	    char ch = Character.forDigit((bytes[j] >> 4) & 0xF, 16);
+	    if( debug > 0 ) log("Encode:  " + ch);
+	    buf.write(ch);
+	    ch = Character.forDigit(bytes[j] & 0xF, 16);
+	    if( debug > 0 ) log("Encode:  " + ch);
+	    buf.write(ch);
+	}
+    }
+    
+    /**
+     * Utility funtion to re-encode the URL.
+     * Still has problems with charset, since UEncoder mostly
+     * ignores it.
+     */
+    public String encodeURL(String uri) {
+	String outUri=null;
+	try {
+	    // XXX optimize - recycle, etc
+	    CharArrayWriter out = new CharArrayWriter();
+	    urlEncode(out, uri);
+	    outUri=out.toString();
+	} catch (IOException iex) {
+	}
+	return outUri;
+    }
+    
+
+    // -------------------- Internal implementation --------------------
+    
+    // 
+    private void init() {
+	
+    }
+    
+    private void initSafeChars() {
+	safeChars=new BitSet(128);
+	int i;
+	for (i = 'a'; i <= 'z'; i++) {
+	    safeChars.set(i);
+	}
+	for (i = 'A'; i <= 'Z'; i++) {
+	    safeChars.set(i);
+	}
+	for (i = '0'; i <= '9'; i++) {
+	    safeChars.set(i);
+	}
+	//safe
+	safeChars.set('$');
+	safeChars.set('-');
+	safeChars.set('_');
+	safeChars.set('.');
+
+	// Dangerous: someone may treat this as " "
+	// RFC1738 does allow it, it's not reserved
+	//    safeChars.set('+');
+	//extra
+	safeChars.set('!');
+	safeChars.set('*');
+	safeChars.set('\'');
+	safeChars.set('(');
+	safeChars.set(')');
+	safeChars.set(',');	
+    }
+
+    private static void log( String s ) {
+	System.out.println("Encoder: " + s );
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/UTF8Decoder.java b/connectors/util/java/org/apache/tomcat/util/buf/UTF8Decoder.java
new file mode 100644
index 0000000..afd2292
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/UTF8Decoder.java
@@ -0,0 +1,143 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.buf;
+
+import java.io.IOException;
+
+/**
+ * Moved from ByteChunk - code to convert from UTF8 bytes to chars.
+ * Not used in the current tomcat3.3 : the performance gain is not very
+ * big if the String is created, only if we avoid that and work only
+ * on char[]. Until than, it's better to be safe. ( I tested this code
+ * with 2 and 3 bytes chars, and it works fine in xerces )
+ * 
+ * Cut from xerces' UTF8Reader.copyMultiByteCharData() 
+ *
+ * @author Costin Manolache
+ * @author ( Xml-Xerces )
+ */
+public final class UTF8Decoder extends B2CConverter {
+    // may have state !!
+    
+    public UTF8Decoder() {
+
+    }
+    
+    public void recycle() {
+    }
+
+    public void convert(ByteChunk mb, CharChunk cb )
+	throws IOException
+    {
+	int bytesOff=mb.getOffset();
+	int bytesLen=mb.getLength();
+	byte bytes[]=mb.getBytes();
+	
+	int j=bytesOff;
+	int end=j+bytesLen;
+
+	while( j< end ) {
+	    int b0=0xff & bytes[j];
+
+	    if( (b0 & 0x80) == 0 ) {
+		cb.append((char)b0);
+		j++;
+		continue;
+	    }
+	    
+	    // 2 byte ?
+	    if( j++ >= end ) {
+		// ok, just ignore - we could throw exception
+		throw new IOException( "Conversion error - EOF " );
+	    }
+	    int b1=0xff & bytes[j];
+	    
+	    // ok, let's the fun begin - we're handling UTF8
+	    if ((0xe0 & b0) == 0xc0) { // 110yyyyy 10xxxxxx (0x80 to 0x7ff)
+		int ch = ((0x1f & b0)<<6) + (0x3f & b1);
+		if(debug>0)
+		    log("Convert " + b0 + " " + b1 + " " + ch + ((char)ch));
+		
+		cb.append((char)ch);
+		j++;
+		continue;
+	    }
+	    
+	    if( j++ >= end ) 
+		return ;
+	    int b2=0xff & bytes[j];
+	    
+	    if( (b0 & 0xf0 ) == 0xe0 ) {
+		if ((b0 == 0xED && b1 >= 0xA0) ||
+		    (b0 == 0xEF && b1 == 0xBF && b2 >= 0xBE)) {
+		    if(debug>0)
+			log("Error " + b0 + " " + b1+ " " + b2 );
+
+		    throw new IOException( "Conversion error 2"); 
+		}
+
+		int ch = ((0x0f & b0)<<12) + ((0x3f & b1)<<6) + (0x3f & b2);
+		cb.append((char)ch);
+		if(debug>0)
+		    log("Convert " + b0 + " " + b1+ " " + b2 + " " + ch +
+			((char)ch));
+		j++;
+		continue;
+	    }
+
+	    if( j++ >= end ) 
+		return ;
+	    int b3=0xff & bytes[j];
+
+	    if (( 0xf8 & b0 ) == 0xf0 ) {
+		if (b0 > 0xF4 || (b0 == 0xF4 && b1 >= 0x90)) {
+		    if(debug>0)
+			log("Convert " + b0 + " " + b1+ " " + b2 + " " + b3);
+		    throw new IOException( "Conversion error ");
+		}
+		int ch = ((0x0f & b0)<<18) + ((0x3f & b1)<<12) +
+		    ((0x3f & b2)<<6) + (0x3f & b3);
+
+		if(debug>0)
+		    log("Convert " + b0 + " " + b1+ " " + b2 + " " + b3 + " " +
+			ch + ((char)ch));
+
+		if (ch < 0x10000) {
+		    cb.append( (char)ch );
+		} else {
+		    cb.append((char)(((ch-0x00010000)>>10)+
+						   0xd800));
+		    cb.append((char)(((ch-0x00010000)&0x3ff)+
+						   0xdc00));
+		}
+		j++;
+		continue;
+	    } else {
+		// XXX Throw conversion exception !!!
+		if(debug>0)
+		    log("Convert " + b0 + " " + b1+ " " + b2 + " " + b3);
+		throw new IOException( "Conversion error 4" );
+	    }
+	}
+    }
+
+    private static int debug=1;
+    void log(String s ) {
+	System.out.println("UTF8Decoder: " + s );
+    }
+    
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/package.html b/connectors/util/java/org/apache/tomcat/util/buf/package.html
new file mode 100644
index 0000000..0806d3c
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/package.html
@@ -0,0 +1,22 @@
+<html><body>
+<H1>Buffers and Encodings</h1>
+
+This package contains buffers and utils to perform encoding/decoding of buffers. That includes byte to char
+conversions, URL encodings, etc. 
+
+<p>
+Encoding is a critical operation for performance. There are few tricks in this package - the C2B and 
+B2C converters are caching a ISReader/OSWriter and keep everything allocated to do the conversions 
+in any VM without any garbage.
+
+<p>
+This package must accomodate future extensions and additional converters ( most imporant: the nio.charset, 
+which should be detected and used if available ). Also, we do have one hand-written UTF8Decoder, and 
+other tuned encoders could be added.
+
+<p>
+My benchmarks ( I'm costin :-) show only small differences between C2B, B2C and hand-written codders/decoders,
+so UTF8Decoder may be disabled. 
+
+<p>
+</body></html>
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/res/LocalStrings.properties b/connectors/util/java/org/apache/tomcat/util/buf/res/LocalStrings.properties
new file mode 100644
index 0000000..b73ad24
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/res/LocalStrings.properties
@@ -0,0 +1,3 @@
+hexUtil.bad=Bad hexadecimal digit
+hexUtil.odd=Odd number of hexadecimal digits
+httpDate.pe=invalid date format: {0}
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/res/LocalStrings_es.properties b/connectors/util/java/org/apache/tomcat/util/buf/res/LocalStrings_es.properties
new file mode 100644
index 0000000..064ad76
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/res/LocalStrings_es.properties
@@ -0,0 +1,4 @@
+hexUtil.bad=Dígito hexadecimal incorrecto
+hexUtil.odd=Número de dígitos hexadecimales incorrecto
+httpDate.pe=formato de fecha no válido: {0}
+
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/res/LocalStrings_fr.properties b/connectors/util/java/org/apache/tomcat/util/buf/res/LocalStrings_fr.properties
new file mode 100644
index 0000000..8930be1
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/res/LocalStrings_fr.properties
@@ -0,0 +1,3 @@
+hexUtil.bad=Mauvais digit hexadécimal
+hexUtil.odd=Nombre impair de digits hexadécimaux
+httpDate.pe=Format de date invalide: {0}
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/res/LocalStrings_ja.properties b/connectors/util/java/org/apache/tomcat/util/buf/res/LocalStrings_ja.properties
new file mode 100644
index 0000000..8c03dec
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/buf/res/LocalStrings_ja.properties
@@ -0,0 +1,3 @@
+hexUtil.bad=\u7121\u52b9\u306a16\u9032\u6570\u5024\u3067\u3059
+hexUtil.odd=\u5947\u6570\u6841\u306e16\u9032\u6570\u5024\u3067\u3059
+httpDate.pe=\u7121\u52b9\u306a\u65e5\u4ed8\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3067\u3059: {0}
diff --git a/connectors/util/java/org/apache/tomcat/util/collections/EmptyEnumeration.java b/connectors/util/java/org/apache/tomcat/util/collections/EmptyEnumeration.java
new file mode 100644
index 0000000..7154488
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/EmptyEnumeration.java
@@ -0,0 +1,41 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.collections;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+public class EmptyEnumeration implements Enumeration {
+
+    static EmptyEnumeration staticInstance=new EmptyEnumeration();
+
+    public EmptyEnumeration() {
+    }
+
+    public static Enumeration getEmptyEnumeration() {
+	return staticInstance;
+    }
+    
+    public Object nextElement( ) {
+	throw new NoSuchElementException( "EmptyEnumeration");
+    }
+
+    public boolean hasMoreElements() {
+	return false;
+    }
+    
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/collections/LRUCache.java b/connectors/util/java/org/apache/tomcat/util/collections/LRUCache.java
new file mode 100644
index 0000000..f0896dc
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/LRUCache.java
@@ -0,0 +1,150 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.collections;
+
+import java.util.Hashtable;
+
+/**
+ * This class implements a Generic LRU Cache
+ *
+ *
+ * @author Ignacio J. Ortega
+ *
+ */
+
+public class LRUCache
+{
+    class CacheNode
+    {
+
+        CacheNode prev;
+        CacheNode next;
+        Object value;
+        Object key;
+
+        CacheNode()
+        {
+        }
+    }
+
+
+    public LRUCache(int i)
+    {
+        currentSize = 0;
+        cacheSize = i;
+        nodes = new Hashtable(i);
+    }
+
+    public Object get(Object key)
+    {
+        CacheNode node = (CacheNode)nodes.get(key);
+        if(node != null)
+        {
+            moveToHead(node);
+            return node.value;
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+    public void put(Object key, Object value)
+    {
+        CacheNode node = (CacheNode)nodes.get(key);
+        if(node == null)
+        {
+            if(currentSize >= cacheSize)
+            {
+                if(last != null)
+                    nodes.remove(last.key);
+                removeLast();
+            }
+            else
+            {
+                currentSize++;
+            }
+            node = new CacheNode();
+        }
+        node.value = value;
+        node.key = key;
+        moveToHead(node);
+        nodes.put(key, node);
+    }
+
+    public Object remove(Object key) {
+        CacheNode node = (CacheNode)nodes.get(key);
+        if (node != null) {
+            if (node.prev != null) {
+                node.prev.next = node.next;
+            }
+            if (node.next != null) {
+                node.next.prev = node.prev;
+            }
+            if (last == node)
+                last = node.prev;
+            if (first == node)
+                first = node.next;
+        }
+        return node;
+    }
+
+    public void clear()
+    {
+        first = null;
+        last = null;
+    }
+
+    private void removeLast()
+    {
+        if(last != null)
+        {
+            if(last.prev != null)
+                last.prev.next = null;
+            else
+                first = null;
+            last = last.prev;
+        }
+    }
+
+    private void moveToHead(CacheNode node)
+    {
+        if(node == first)
+            return;
+        if(node.prev != null)
+            node.prev.next = node.next;
+        if(node.next != null)
+            node.next.prev = node.prev;
+        if(last == node)
+            last = node.prev;
+        if(first != null)
+        {
+            node.next = first;
+            first.prev = node;
+        }
+        first = node;
+        node.prev = null;
+        if(last == null)
+            last = first;
+    }
+
+    private int cacheSize;
+    private Hashtable nodes;
+    private int currentSize;
+    private CacheNode first;
+    private CacheNode last;
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/collections/MultiMap.java b/connectors/util/java/org/apache/tomcat/util/collections/MultiMap.java
new file mode 100644
index 0000000..3e60dbd
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/MultiMap.java
@@ -0,0 +1,235 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.collections;
+
+import org.apache.tomcat.util.buf.MessageBytes;
+
+// Originally MimeHeaders
+
+/**
+ * An efficient representation for certain type of map. The keys 
+ * can have a single or multi values, but most of the time there are
+ * single values.
+ *
+ * The data is of "MessageBytes" type, meaning bytes[] that can be
+ * converted to Strings ( if needed, and encoding is lazy-binded ).
+ *
+ * This is a base class for MimeHeaders, Parameters and Cookies.
+ *
+ * Data structures: each field is a single-valued key/value.
+ * The fields are allocated when needed, and are recycled.
+ * The current implementation does linear search, in future we'll
+ * also use the hashkey.
+ * 
+ * @author dac@eng.sun.com
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Costin Manolache
+ */
+public class MultiMap {
+
+    protected Field[] fields;
+    // fields in use
+    protected int count;
+
+    /**
+     * 
+     */
+    public MultiMap(int initial_size) {
+	fields=new Field[initial_size];
+    }
+
+    /**
+     * Clears all header fields.
+     */
+    public void recycle() {
+	for (int i = 0; i < count; i++) {
+	    fields[i].recycle();
+	}
+	count = 0;
+    }
+
+    // -------------------- Idx access to headers ----------
+    // This allows external iterators.
+    
+    /**
+     * Returns the current number of header fields.
+     */
+    public int size() {
+	return count;
+    }
+
+    /**
+     * Returns the Nth header name
+     * This may be used to iterate through all header fields.
+     *
+     * An exception is thrown if the index is not valid ( <0 or >size )
+     */
+    public MessageBytes getName(int n) {
+	// n >= 0 && n < count ? headers[n].getName() : null
+	return fields[n].name;
+    }
+
+    /**
+     * Returns the Nth header value
+     * This may be used to iterate through all header fields.
+     */
+    public MessageBytes getValue(int n) {
+	return fields[n].value;
+    }
+
+    /** Find the index of a field with the given name.
+     */
+    public int find( String name, int starting ) {
+	// We can use a hash - but it's not clear how much
+	// benefit you can get - there is an  overhead 
+	// and the number of headers is small (4-5 ?)
+	// Another problem is that we'll pay the overhead
+	// of constructing the hashtable
+
+	// A custom search tree may be better
+        for (int i = starting; i < count; i++) {
+	    if (fields[i].name.equals(name)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /** Find the index of a field with the given name.
+     */
+    public int findIgnoreCase( String name, int starting ) {
+	// We can use a hash - but it's not clear how much
+	// benefit you can get - there is an  overhead 
+	// and the number of headers is small (4-5 ?)
+	// Another problem is that we'll pay the overhead
+	// of constructing the hashtable
+
+	// A custom search tree may be better
+        for (int i = starting; i < count; i++) {
+	    if (fields[i].name.equalsIgnoreCase(name)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Removes the field at the specified position.  
+     *
+     * MultiMap will preserve the order of field add unless remove()
+     * is called. This is not thread-safe, and will invalidate all
+     * iterators. 
+     *
+     * This is not a frequent operation for Headers and Parameters -
+     * there are better ways ( like adding a "isValid" field )
+     */
+    public void remove( int i ) {
+	// reset and swap with last header
+	Field mh = fields[i];
+	// reset the field
+	mh.recycle();
+	
+	fields[i] = fields[count - 1];
+	fields[count - 1] = mh;
+	count--;
+    }
+
+    /** Create a new, unitialized entry. 
+     */
+    public int addField() {
+	int len = fields.length;
+	int pos=count;
+	if (count >= len) {
+	    // expand header list array
+	    Field tmp[] = new Field[pos * 2];
+	    System.arraycopy(fields, 0, tmp, 0, len);
+	    fields = tmp;
+	}
+	if (fields[pos] == null) {
+	    fields[pos] = new Field();
+	}
+	count++;
+	return pos;
+    }
+
+    public MessageBytes get( String name) {
+        for (int i = 0; i < count; i++) {
+	    if (fields[i].name.equals(name)) {
+		return fields[i].value;
+	    }
+	}
+        return null;
+    }
+
+    public int findFirst( String name ) {
+        for (int i = 0; i < count; i++) {
+	    if (fields[i].name.equals(name)) {
+		return i;
+	    }
+	}
+        return -1;
+    }
+
+    public int findNext( int startPos ) {
+	int next= fields[startPos].nextPos;
+	if( next != MultiMap.NEED_NEXT ) {
+	    return next;
+	}
+
+	// next==NEED_NEXT, we never searched for this header
+	MessageBytes name=fields[startPos].name;
+        for (int i = startPos; i < count; i++) {
+	    if (fields[i].name.equals(name)) {
+		// cache the search result
+		fields[startPos].nextPos=i;
+		return i;
+	    }
+	}
+	fields[startPos].nextPos= MultiMap.LAST;
+        return -1;
+    }
+
+    // workaround for JDK1.1.8/solaris
+    static final int NEED_NEXT=-2;
+    static final int LAST=-1;
+
+    // -------------------- Internal representation --------------------
+    final class Field {
+	MessageBytes name;
+	MessageBytes value;
+
+	// Extra info for speed
+	
+	//  multiple fields with same name - a linked list will
+	// speed up multiple name enumerations and search.
+	int nextPos;
+
+	// hashkey
+	int hash;
+	Field nextSameHash;
+
+	Field() {
+	    nextPos=MultiMap.NEED_NEXT;
+	}
+	
+	void recycle() {
+	    name.recycle();
+	    value.recycle();
+	    nextPos=MultiMap.NEED_NEXT;
+	}
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/collections/MultiMapNamesEnumeration.java b/connectors/util/java/org/apache/tomcat/util/collections/MultiMapNamesEnumeration.java
new file mode 100644
index 0000000..aa9620d
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/MultiMapNamesEnumeration.java
@@ -0,0 +1,80 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.collections;
+
+import java.util.Enumeration;
+
+/** Enumerate the distinct header names.
+    Each nextElement() is O(n) ( a comparation is
+    done with all previous elements ).
+
+    This is less frequesnt than add() -
+    we want to keep add O(1).
+*/
+public final class MultiMapNamesEnumeration implements Enumeration {
+    int pos;
+    int size;
+    String next;
+    MultiMap headers;
+
+    // toString and unique options are not implemented -
+    // we allways to toString and unique.
+    
+    /** Create a new multi-map enumeration.
+     * @param  headers the collection to enumerate 
+     * @param  toString convert each name to string 
+     * @param  unique return only unique names
+     */
+    MultiMapNamesEnumeration(MultiMap headers, boolean toString,
+			     boolean unique) {
+	this.headers=headers;
+	pos=0;
+	size = headers.size();
+	findNext();
+    }
+
+    private void findNext() {
+	next=null;
+	for(  ; pos< size; pos++ ) {
+	    next=headers.getName( pos ).toString();
+	    for( int j=0; j<pos ; j++ ) {
+		if( headers.getName( j ).equalsIgnoreCase( next )) {
+		    // duplicate.
+		    next=null;
+		    break;
+		}
+	    }
+	    if( next!=null ) {
+		// it's not a duplicate
+		break;
+	    }
+	}
+	// next time findNext is called it will try the
+	// next element
+	pos++;
+    }
+    
+    public boolean hasMoreElements() {
+	return next!=null;
+    }
+
+    public Object nextElement() {
+	String current=next;
+	findNext();
+	return current;
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/collections/MultiMapValuesEnumeration.java b/connectors/util/java/org/apache/tomcat/util/collections/MultiMapValuesEnumeration.java
new file mode 100644
index 0000000..eff02f9
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/MultiMapValuesEnumeration.java
@@ -0,0 +1,63 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.collections;
+
+import java.util.Enumeration;
+
+import org.apache.tomcat.util.buf.MessageBytes;
+
+/** Enumerate the values for a (possibly ) multiple
+ *    value element.
+ */
+class MultiMapValuesEnumeration implements Enumeration {
+    int pos;
+    int size;
+    MessageBytes next;
+    MultiMap headers;
+    String name;
+
+    MultiMapValuesEnumeration(MultiMap headers, String name,
+			      boolean toString) {
+        this.name=name;
+	this.headers=headers;
+	pos=0;
+	size = headers.size();
+	findNext();
+    }
+
+    private void findNext() {
+	next=null;
+	for( ; pos< size; pos++ ) {
+	    MessageBytes n1=headers.getName( pos );
+	    if( n1.equalsIgnoreCase( name )) {
+		next=headers.getValue( pos );
+		break;
+	    }
+	}
+	pos++;
+    }
+    
+    public boolean hasMoreElements() {
+	return next!=null;
+    }
+
+    public Object nextElement() {
+	MessageBytes current=next;
+	findNext();
+	return current.toString();
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/collections/Queue.java b/connectors/util/java/org/apache/tomcat/util/collections/Queue.java
new file mode 100644
index 0000000..86a049c
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/Queue.java
@@ -0,0 +1,102 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.collections;
+
+import java.util.Vector;
+
+/**
+ * A simple FIFO queue class which causes the calling thread to wait
+ * if the queue is empty and notifies threads that are waiting when it
+ * is not empty.
+ *
+ * @author Anil V (akv@eng.sun.com)
+ */
+public class Queue {
+    private Vector vector = new Vector();
+    private boolean stopWaiting=false;
+    private boolean waiting=false;
+    
+    /** 
+     * Put the object into the queue.
+     * 
+     * @param	object		the object to be appended to the
+     * 				queue. 
+     */
+    public synchronized void put(Object object) {
+	vector.addElement(object);
+	notify();
+    }
+
+    /** Break the pull(), allowing the calling thread to exit
+     */
+    public synchronized void stop() {
+	stopWaiting=true;
+	// just a hack to stop waiting 
+	if( waiting ) notify();
+    }
+    
+    /**
+     * Pull the first object out of the queue. Wait if the queue is
+     * empty.
+     */
+    public synchronized Object pull() {
+	while (isEmpty()) {
+	    try {
+		waiting=true;
+		wait();
+	    } catch (InterruptedException ex) {
+	    }
+	    waiting=false;
+	    if( stopWaiting ) return null;
+	}
+	return get();
+    }
+
+    /**
+     * Get the first object out of the queue. Return null if the queue
+     * is empty. 
+     */
+    public synchronized Object get() {
+	Object object = peek();
+	if (object != null)
+	    vector.removeElementAt(0);
+	return object;
+    }
+
+    /**
+     * Peek to see if something is available.
+     */
+    public Object peek() {
+	if (isEmpty())
+	    return null;
+	return vector.elementAt(0);
+    }
+    
+    /**
+     * Is the queue empty?
+     */
+    public boolean isEmpty() {
+	return vector.isEmpty();
+    }
+
+    /**
+     * How many elements are there in this queue?
+     */
+    public int size() {
+	return vector.size();
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/collections/SimpleHashtable.java b/connectors/util/java/org/apache/tomcat/util/collections/SimpleHashtable.java
new file mode 100644
index 0000000..f765c5b
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/SimpleHashtable.java
@@ -0,0 +1,318 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.collections;
+
+import java.util.Enumeration;
+
+/* **************************************** Stolen from Crimson ******************** */
+/* From Crimson/Parser - in a perfect world we'll just have a common set of
+   utilities, and all apache project will just use those.
+
+*/
+
+// can't be replaced using a Java 2 "Collections" API
+// since this package must also run on JDK 1.1
+
+
+/**
+ * This class implements a special purpose hashtable.  It works like a
+ * normal <code>java.util.Hashtable</code> except that: <OL>
+ *
+ *	<LI> Keys to "get" are strings which are known to be interned,
+ *	so that "==" is used instead of "String.equals".  (Interning
+ *	could be document-relative instead of global.)
+ *
+ *	<LI> It's not synchronized, since it's to be used only by
+ *	one thread at a time.
+ *
+ *	<LI> The keys () enumerator allocates no memory, with live
+ *	updates to the data disallowed.
+ *
+ *	<LI> It's got fewer bells and whistles:  fixed threshold and
+ *	load factor, no JDK 1.2 collection support, only keys can be
+ *	enumerated, things can't be removed, simpler inheritance; more.
+ *
+ *	</OL>
+ *
+ * <P> The overall result is that it's less expensive to use these in
+ * performance-critical locations, in terms both of CPU and memory,
+ * than <code>java.util.Hashtable</code> instances.  In this package
+ * it makes a significant difference when normalizing attributes,
+ * which is done for each start-element construct.
+ *
+ */
+public final class SimpleHashtable implements Enumeration
+{
+    // entries ...
+    private Entry		table[];
+
+    // currently enumerated key
+    private Entry		current = null;
+    private int			currentBucket = 0;
+
+    // number of elements in hashtable
+    private int			count;
+    private int			threshold;
+
+    private static final float	loadFactor = 0.75f;
+
+
+    /**
+     * Constructs a new, empty hashtable with the specified initial 
+     * capacity.
+     *
+     * @param      initialCapacity   the initial capacity of the hashtable.
+     */
+    public SimpleHashtable(int initialCapacity) {
+	if (initialCapacity < 0)
+	    throw new IllegalArgumentException("Illegal Capacity: "+
+                                               initialCapacity);
+        if (initialCapacity==0)
+            initialCapacity = 1;
+	table = new Entry[initialCapacity];
+	threshold = (int)(initialCapacity * loadFactor);
+    }
+
+    /**
+     * Constructs a new, empty hashtable with a default capacity.
+     */
+    public SimpleHashtable() {
+	this(11);
+    }
+
+    /**
+     */
+    public void clear ()
+    {
+	count = 0;
+	currentBucket = 0;
+	current = null;
+	for (int i = 0; i < table.length; i++)
+	    table [i] = null;
+    }
+
+    /**
+     * Returns the number of keys in this hashtable.
+     *
+     * @return  the number of keys in this hashtable.
+     */
+    public int size() {
+	return count;
+    }
+
+    /**
+     * Returns an enumeration of the keys in this hashtable.
+     *
+     * @return  an enumeration of the keys in this hashtable.
+     * @see     Enumeration
+     */
+    public Enumeration keys() {
+	currentBucket = 0;
+	current = null;
+	hasMoreElements();
+	return this;
+    }
+
+    /**
+     * Used to view this as an enumeration; returns true if there
+     * are more keys to be enumerated.
+     */
+    public boolean hasMoreElements ()
+    {
+	if (current != null)
+	    return true;
+	while (currentBucket < table.length) {
+	    current = table [currentBucket++];
+	    if (current != null)
+		return true;
+	}
+	return false;
+    }
+
+    /**
+     * Used to view this as an enumeration; returns the next key
+     * in the enumeration.
+     */
+    public Object nextElement ()
+    {
+	Object retval;
+
+	if (current == null)
+	    throw new IllegalStateException ();
+	retval = current.key;
+	current = current.next;
+	// Advance to the next position ( we may call next after next,
+	// without hasMore )
+	hasMoreElements();
+	return retval;
+    }
+
+
+    /**
+     * Returns the value to which the specified key is mapped in this hashtable.
+     */
+    public Object getInterned (String key) {
+	Entry tab[] = table;
+	int hash = key.hashCode();
+	int index = (hash & 0x7FFFFFFF) % tab.length;
+	for (Entry e = tab[index] ; e != null ; e = e.next) {
+	    if ((e.hash == hash) && (e.key == key))
+		return e.value;
+	}
+	return null;
+    }
+
+    /**
+     * Returns the value to which the specified key is mapped in this
+     * hashtable ... the key isn't necessarily interned, though.
+     */
+    public Object get(String key) {
+	Entry tab[] = table;
+	int hash = key.hashCode();
+	int index = (hash & 0x7FFFFFFF) % tab.length;
+	for (Entry e = tab[index] ; e != null ; e = e.next) {
+	    if ((e.hash == hash) && e.key.equals(key))
+		return e.value;
+	}
+	return null;
+    }
+
+    /**
+     * Increases the capacity of and internally reorganizes this 
+     * hashtable, in order to accommodate and access its entries more 
+     * efficiently.  This method is called automatically when the 
+     * number of keys in the hashtable exceeds this hashtable's capacity 
+     * and load factor. 
+     */
+    private void rehash() {
+	int oldCapacity = table.length;
+	Entry oldMap[] = table;
+
+	int newCapacity = oldCapacity * 2 + 1;
+	Entry newMap[] = new Entry[newCapacity];
+
+	threshold = (int)(newCapacity * loadFactor);
+	table = newMap;
+
+	/*
+	System.out.pr intln("rehash old=" + oldCapacity
+		+ ", new=" + newCapacity
+		+ ", thresh=" + threshold
+		+ ", count=" + count);
+	*/
+
+	for (int i = oldCapacity ; i-- > 0 ;) {
+	    for (Entry old = oldMap[i] ; old != null ; ) {
+		Entry e = old;
+		old = old.next;
+
+		int index = (e.hash & 0x7FFFFFFF) % newCapacity;
+		e.next = newMap[index];
+		newMap[index] = e;
+	    }
+	}
+    }
+
+    /**
+     * Maps the specified <code>key</code> to the specified 
+     * <code>value</code> in this hashtable. Neither the key nor the 
+     * value can be <code>null</code>. 
+     *
+     * <P>The value can be retrieved by calling the <code>get</code> method 
+     * with a key that is equal to the original key. 
+     */
+    public Object put(Object key, Object value) {
+	// Make sure the value is not null
+	if (value == null) {
+	    throw new NullPointerException();
+	}
+
+	// Makes sure the key is not already in the hashtable.
+	Entry tab[] = table;
+	int hash = key.hashCode();
+	int index = (hash & 0x7FFFFFFF) % tab.length;
+	for (Entry e = tab[index] ; e != null ; e = e.next) {
+	    // if ((e.hash == hash) && e.key.equals(key)) {
+	    if ((e.hash == hash) && (e.key == key)) {
+		Object old = e.value;
+		e.value = value;
+		return old;
+	    }
+	}
+
+	if (count >= threshold) {
+	    // Rehash the table if the threshold is exceeded
+	    rehash();
+
+            tab = table;
+            index = (hash & 0x7FFFFFFF) % tab.length;
+	} 
+
+	// Creates the new entry.
+	Entry e = new Entry(hash, key, value, tab[index]);
+	tab[index] = e;
+	count++;
+	return null;
+    }
+
+    public Object remove(Object key) {
+	Entry tab[] = table;
+	Entry prev=null;
+	int hash = key.hashCode();
+	int index = (hash & 0x7FFFFFFF) % tab.length;
+	if( dL > 0 ) d("Idx " + index +  " " + tab[index] );
+	for (Entry e = tab[index] ; e != null ; prev=e, e = e.next) {
+	    if( dL > 0 ) d("> " + prev + " " + e.next + " " + e + " " + e.key);
+	    if ((e.hash == hash) && e.key.equals(key)) {
+		if( prev!=null ) {
+		    prev.next=e.next;
+		} else {
+		    tab[index]=e.next;
+		}
+		if( dL > 0 ) d("Removing from list " + tab[index] + " " + prev +
+			       " " + e.value);
+		count--;
+		Object res=e.value;
+		e.value=null;
+		return res;
+	    }
+	}
+	return null;
+    }
+
+    /**
+     * Hashtable collision list.
+     */
+    private static class Entry {
+	int	hash;
+	Object	key;
+	Object	value;
+	Entry	next;
+
+	protected Entry(int hash, Object key, Object value, Entry next) {
+	    this.hash = hash;
+	    this.key = key;
+	    this.value = value;
+	    this.next = next;
+	}
+    }
+
+    private static final int dL=0;
+    private void d(String s ) {
+	System.err.println( "SimpleHashtable: " + s );
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/collections/SimplePool.java b/connectors/util/java/org/apache/tomcat/util/collections/SimplePool.java
new file mode 100644
index 0000000..34a97eb
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/SimplePool.java
@@ -0,0 +1,121 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.collections;
+
+/**
+ * Simple object pool. Based on ThreadPool and few other classes
+ *
+ * The pool will ignore overflow and return null if empty.
+ *
+ * @author Gal Shachor
+ * @author Costin Manolache
+ */
+public final class SimplePool  {
+    /*
+     * Where the threads are held.
+     */
+    private Object pool[];
+
+    private int max;
+    private int last;
+    private int current=-1;
+    
+    private Object lock;
+    public static final int DEFAULT_SIZE=32;
+    static final int debug=0;
+    
+    public SimplePool() {
+	this(DEFAULT_SIZE,DEFAULT_SIZE);
+    }
+
+    public SimplePool(int size) {
+	this(size, size);
+    }
+
+    public SimplePool(int size, int max) {
+	this.max=max;
+	pool=new Object[size];
+	this.last=size-1;
+	lock=new Object();
+    }
+
+    public  void set(Object o) {
+	put(o);
+    }
+
+    /**
+     * Add the object to the pool, silent nothing if the pool is full
+     */
+    public  void put(Object o) {
+	synchronized( lock ) {
+	    if( current < last ) {
+		current++;
+		pool[current] = o;
+            } else if( current < max ) {
+		// realocate
+		int newSize=pool.length*2;
+		if( newSize > max ) newSize=max+1;
+		Object tmp[]=new Object[newSize];
+		last=newSize-1;
+		System.arraycopy( pool, 0, tmp, 0, pool.length);
+		pool=tmp;
+		current++;
+		pool[current] = o;
+	    }
+	    if( debug > 0 ) log("put " + o + " " + current + " " + max );
+	}
+    }
+
+    /**
+     * Get an object from the pool, null if the pool is empty.
+     */
+    public  Object get() {
+	Object item = null;
+	synchronized( lock ) {
+	    if( current >= 0 ) {
+		item = pool[current];
+		pool[current] = null;
+		current -= 1;
+	    }
+	    if( debug > 0 ) 
+		log("get " + item + " " + current + " " + max);
+	}
+	return item;
+    }
+
+    /**
+     * Return the size of the pool
+     */
+    public int getMax() {
+	return max;
+    }
+
+    /**
+     * Number of object in the pool
+     */
+    public int getCount() {
+	return current+1;
+    }
+
+
+    public void shutdown() {
+    }
+    
+    private void log( String s ) {
+	System.out.println("SimplePool: " + s );
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/collections/package.html b/connectors/util/java/org/apache/tomcat/util/collections/package.html
new file mode 100644
index 0000000..88c6bdb
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/collections/package.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+<title>util.collections</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+<h1>Specialized collections</h1>
+
+This package includes a number of special collections, tunned for server-side
+applications. 
+
+The utils are not tomcat specific, but use MessageBytes and few other 
+top-level utils.
+
+</body>
+</html>
diff --git a/connectors/util/java/org/apache/tomcat/util/compat/Jdk14Compat.java b/connectors/util/java/org/apache/tomcat/util/compat/Jdk14Compat.java
new file mode 100644
index 0000000..b67bca3
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/compat/Jdk14Compat.java
@@ -0,0 +1,120 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.compat;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ *  See JdkCompat. This is an extension of that class for Jdk1.4 support.
+ *
+ * @author Tim Funk
+ * @author Remy Maucherat
+ */
+public class Jdk14Compat extends JdkCompat {
+    // -------------------------------------------------------------- Constants
+
+    // ------------------------------------------------------- Static Variables
+    static Log logger = LogFactory.getLog(Jdk14Compat.class);
+
+    // ----------------------------------------------------------- Constructors
+    /**
+     *  Default no-arg constructor
+     */
+    protected Jdk14Compat() {
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     *  Return the URI for the given file. Originally created for
+     *  o.a.c.loader.WebappClassLoader
+     *
+     * @param file The file to wrap into URI
+     * @return A URI as a URL
+     * @throws MalformedURLException Doh ;)
+     */
+    public URL getURI(File file)
+        throws MalformedURLException {
+
+        File realFile = file;
+        try {
+            realFile = realFile.getCanonicalFile();
+        } catch (IOException e) {
+            // Ignore
+        }
+
+        return realFile.toURI().toURL();
+    }
+
+
+    /**
+     *  Return the maximum amount of memory the JVM will attempt to use.
+     */
+    public long getMaxMemory() {
+        return Runtime.getRuntime().maxMemory();
+    }
+
+
+    /**
+     * Print out a partial servlet stack trace (truncating at the last 
+     * occurrence of javax.servlet.).
+     */
+    public String getPartialServletStackTrace(Throwable t) {
+        StringBuffer trace = new StringBuffer();
+        trace.append(t.toString()).append('\n');
+        StackTraceElement[] elements = t.getStackTrace();
+        int pos = elements.length;
+        for (int i = 0; i < elements.length; i++) {
+            if ((elements[i].getClassName().startsWith
+                 ("org.apache.catalina.core.ApplicationFilterChain"))
+                && (elements[i].getMethodName().equals("internalDoFilter"))) {
+                pos = i;
+            }
+        }
+        for (int i = 0; i < pos; i++) {
+            if (!(elements[i].getClassName().startsWith
+                  ("org.apache.catalina.core."))) {
+                trace.append('\t').append(elements[i].toString()).append('\n');
+            }
+        }
+        return trace.toString();
+    }
+
+    public  String [] split(String path, String pat) {
+        return path.split(pat);
+    }
+
+
+    /**
+     * Chains the <tt>wrapped</tt> throwable to the <tt>wrapper</tt> throwable.
+     *
+     * @param wrapper The wrapper throwable 
+     * @param wrapped The throwable to be wrapped
+     */
+    public void chainException(Throwable wrapper, Throwable wrapped) {
+        wrapper.initCause(wrapped);
+    }
+
+ }
diff --git a/connectors/util/java/org/apache/tomcat/util/compat/JdkCompat.java b/connectors/util/java/org/apache/tomcat/util/compat/JdkCompat.java
new file mode 100644
index 0000000..754cbad
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/compat/JdkCompat.java
@@ -0,0 +1,227 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.compat;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.util.Vector;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ *  General-purpose utility to provide backward-compatibility and JDK
+ *  independence. This allow use of JDK1.3 ( or higher ) facilities if
+ *  available, while maintaining the code compatible with older VMs.
+ *
+ *  The goal is to make backward-compatiblity reasonably easy.
+ *
+ *  The base class supports JDK1.3 behavior.
+ *
+ *  @author Tim Funk
+ */
+public class JdkCompat {
+
+    // ------------------------------------------------------- Static Variables
+
+    /**
+     * class providing java2 support
+     */
+    static final String JAVA14_SUPPORT =
+        "org.apache.tomcat.util.compat.Jdk14Compat";
+    /**
+     *  Commons logger wrapper
+     */
+    static Log logger = LogFactory.getLog(JdkCompat.class);
+
+    /** Return java version as a string
+     */
+    public static String getJavaVersion() {
+        return javaVersion;
+    }
+
+    public static boolean isJava2() {
+        return java2;
+    } 
+   
+    public static boolean isJava14() {
+        return java14;
+    }
+
+    public static boolean isJava15() {
+        return java15;
+    }
+
+    // -------------------- Implementation --------------------
+    
+    // from ant
+    public static final String JAVA_1_0 = "1.0";
+    public static final String JAVA_1_1 = "1.1";
+    public static final String JAVA_1_2 = "1.2";
+    public static final String JAVA_1_3 = "1.3";
+    public static final String JAVA_1_4 = "1.4";
+    public static final String JAVA_1_5 = "1.5";
+
+    static String javaVersion;
+    static boolean java2=false;
+    static boolean java14=false;
+    static boolean java15=false;
+    static JdkCompat jdkCompat;
+    
+    static {
+        init();
+    }
+
+    private static void init() {
+        try {
+            javaVersion = JAVA_1_0;
+            Class.forName("java.lang.Void");
+            javaVersion = JAVA_1_1;
+            Class.forName("java.lang.ThreadLocal");
+            java2=true;
+            javaVersion = JAVA_1_2;
+            Class.forName("java.lang.StrictMath");
+            javaVersion = JAVA_1_3;
+            Class.forName("java.lang.CharSequence");
+            javaVersion = JAVA_1_4;
+            java14=true;
+            Class.forName("java.lang.Appendable");
+            javaVersion = JAVA_1_5;
+            java15=true;
+        } catch (ClassNotFoundException cnfe) {
+            // swallow as we've hit the max class version that we have
+        }
+        if( java14 ) {
+            try {
+                Class c=Class.forName(JAVA14_SUPPORT);
+                jdkCompat=(JdkCompat)c.newInstance();
+            } catch( Exception ex ) {
+                jdkCompat=new JdkCompat();
+            }
+        } else {
+            jdkCompat=new JdkCompat();
+            // Install jar handler if none installed
+        }
+    }
+
+    // ----------------------------------------------------------- Constructors
+    /**
+     *  Default no-arg constructor
+     */
+    protected JdkCompat() {
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+    /**
+     * Get a compatibiliy helper class.
+     */
+    public static JdkCompat getJdkCompat() {
+        return jdkCompat;
+    }
+
+    /**
+     *  Return the URI for the given file. Originally created for
+     *  o.a.c.loader.WebappClassLoader
+     *
+     * @param file The file to wrap into URI
+     * @return A URI as a URL
+     * @throws MalformedURLException Doh ;)
+     */
+    public URL getURI(File file)
+        throws MalformedURLException {
+
+        File realFile = file;
+        try {
+            realFile = realFile.getCanonicalFile();
+        } catch (IOException e) {
+            // Ignore
+        }
+
+        return realFile.toURL();
+    }
+
+
+    /**
+     *  Return the maximum amount of memory the JVM will attempt to use.
+     */
+    public long getMaxMemory() {
+        return (-1L);
+    }
+
+
+    /**
+     * Print out a partial servlet stack trace (truncating at the last 
+     * occurrence of javax.servlet.).
+     */
+    public String getPartialServletStackTrace(Throwable t) {
+        StringWriter stackTrace = new StringWriter();
+        t.printStackTrace(new PrintWriter(stackTrace));
+        String st = stackTrace.toString();
+        int i = st.lastIndexOf
+            ("org.apache.catalina.core.ApplicationFilterChain.internalDoFilter");
+        if (i > -1) {
+            return st.substring(0, i - 4);
+        } else {
+            return st;
+        }
+    }
+
+    /**
+     * Splits a string into it's components.
+     * @param path String to split
+     * @param pat Pattern to split at
+     * @return the components of the path
+     */
+    public  String [] split(String path, String pat) {
+        Vector comps = new Vector();
+        int pos = path.indexOf(pat);
+        int start = 0;
+        while( pos >= 0 ) {
+            if(pos > start ) {
+                String comp = path.substring(start,pos);
+                comps.add(comp);
+            }
+            start = pos + pat.length();
+            pos = path.indexOf(pat,start);
+        }
+        if( start < path.length()) {
+            comps.add(path.substring(start));
+        }
+        String [] result = new String[comps.size()];
+        for(int i=0; i < comps.size(); i++) {
+            result[i] = (String)comps.elementAt(i);
+        }
+        return result;
+    }
+
+
+    /**
+     * Chains the <tt>wrapped</tt> throwable to the <tt>wrapper</tt> throwable.
+     *
+     * @param wrapper The wrapper throwable 
+     * @param wrapped The throwable to be wrapped
+     */
+    public void chainException(Throwable wrapper, Throwable wrapped) {
+        // do nothing
+    }
+
+ }
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/AbstractObjectCreationFactory.java b/connectors/util/java/org/apache/tomcat/util/digester/AbstractObjectCreationFactory.java
new file mode 100644
index 0000000..2535901
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/AbstractObjectCreationFactory.java
@@ -0,0 +1,78 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+package org.apache.tomcat.util.digester;
+
+
+import org.xml.sax.Attributes;
+
+
+/**
+ * <p>Abstract base class for <code>ObjectCreationFactory</code>
+ * implementations.</p>
+ */
+abstract public class AbstractObjectCreationFactory implements ObjectCreationFactory {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The associated <code>Digester</code> instance that was set up by
+     * {@link FactoryCreateRule} upon initialization.
+     */
+    protected Digester digester = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Factory method called by {@link FactoryCreateRule} to supply an
+     * object based on the element's attributes.
+     *
+     * @param attributes the element's attributes
+     *
+     * @throws Exception any exception thrown will be propagated upwards
+     */
+    public abstract Object createObject(Attributes attributes) throws Exception;
+
+
+    /**
+     * <p>Returns the {@link Digester} that was set by the
+     * {@link FactoryCreateRule} upon initialization.
+     */
+    public Digester getDigester() {
+
+        return (this.digester);
+
+    }
+
+
+    /**
+     * <p>Set the {@link Digester} to allow the implementation to do logging,
+     * classloading based on the digester's classloader, etc.
+     *
+     * @param digester parent Digester object
+     */
+    public void setDigester(Digester digester) {
+
+        this.digester = digester;
+
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/AbstractRulesImpl.java b/connectors/util/java/org/apache/tomcat/util/digester/AbstractRulesImpl.java
new file mode 100644
index 0000000..8d2bbd2
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/AbstractRulesImpl.java
@@ -0,0 +1,166 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+
+import java.util.List;
+
+
+/**
+ * <p><code>AbstractRuleImpl</code> provides basic services for <code>Rules</code> implementations.
+ * Extending this class should make it easier to create a <code>Rules</code> implementation.</p>
+ * 
+ * <p><code>AbstractRuleImpl</code> manages the <code>Digester</code> 
+ * and <code>namespaceUri</code> properties.
+ * If the subclass overrides {@link #registerRule} (rather than {@link #add}),
+ * then the <code>Digester</code> and <code>namespaceURI</code> of the <code>Rule</code>
+ * will be set correctly before it is passed to <code>registerRule</code>.
+ * The subclass can then perform whatever it needs to do to register the rule.</p>
+ *
+ * @since 1.5
+ */
+
+abstract public class AbstractRulesImpl implements Rules {
+
+    // ------------------------------------------------------------- Fields
+    
+    /** Digester using this <code>Rules</code> implementation */
+    private Digester digester;
+    /** Namespace uri to assoicate with subsequent <code>Rule</code>'s */
+    private String namespaceURI;
+    
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the Digester instance with which this Rules instance is
+     * associated.
+     */
+    public Digester getDigester() {
+        return digester;
+    }
+
+    /**
+     * Set the Digester instance with which this Rules instance is associated.
+     *
+     * @param digester The newly associated Digester instance
+     */
+    public void setDigester(Digester digester) {
+        this.digester = digester;
+    }
+
+    /**
+     * Return the namespace URI that will be applied to all subsequently
+     * added <code>Rule</code> objects.
+     */
+    public String getNamespaceURI() {
+        return namespaceURI;
+    }
+
+    /**
+     * Set the namespace URI that will be applied to all subsequently
+     * added <code>Rule</code> objects.
+     *
+     * @param namespaceURI Namespace URI that must match on all
+     *  subsequently added rules, or <code>null</code> for matching
+     *  regardless of the current namespace URI
+     */
+    public void setNamespaceURI(String namespaceURI) {
+        this.namespaceURI = namespaceURI;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Registers a new Rule instance matching the specified pattern.
+     * This implementation sets the <code>Digester</code> and the
+     * <code>namespaceURI</code> on the <code>Rule</code> before calling {@link #registerRule}.
+     *
+     * @param pattern Nesting pattern to be matched for this Rule
+     * @param rule Rule instance to be registered
+     */
+    public void add(String pattern, Rule rule) {
+        // set up rule
+        if (this.digester != null) {
+            rule.setDigester(this.digester);
+        }
+        
+        if (this.namespaceURI != null) {
+            rule.setNamespaceURI(this.namespaceURI);
+        }
+        
+        registerRule(pattern, rule);
+        
+    }
+    
+    /** 
+     * Register rule at given pattern.
+     * The the Digester and namespaceURI properties of the given <code>Rule</code>
+     * can be assumed to have been set properly before this method is called.
+     *
+     * @param pattern Nesting pattern to be matched for this Rule
+     * @param rule Rule instance to be registered
+     */ 
+    abstract protected void registerRule(String pattern, Rule rule);
+
+    /**
+     * Clear all existing Rule instance registrations.
+     */
+    abstract public void clear();
+
+
+    /**
+     * Return a List of all registered Rule instances that match the specified
+     * nesting pattern, or a zero-length List if there are no matches.  If more
+     * than one Rule instance matches, they <strong>must</strong> be returned
+     * in the order originally registered through the <code>add()</code>
+     * method.
+     *
+     * @param pattern Nesting pattern to be matched
+     *
+     * @deprecated Call match(namespaceURI,pattern) instead.
+     */
+    public List match(String pattern) {
+        return match(namespaceURI, pattern);
+    }
+
+
+    /**
+     * Return a List of all registered Rule instances that match the specified
+     * nesting pattern, or a zero-length List if there are no matches.  If more
+     * than one Rule instance matches, they <strong>must</strong> be returned
+     * in the order originally registered through the <code>add()</code>
+     * method.
+     *
+     * @param namespaceURI Namespace URI for which to select matching rules,
+     *  or <code>null</code> to match regardless of namespace URI
+     * @param pattern Nesting pattern to be matched
+     */
+    abstract public List match(String namespaceURI, String pattern);
+
+
+    /**
+     * Return a List of all registered Rule instances, or a zero-length List
+     * if there are no registered Rule instances.  If more than one Rule
+     * instance has been registered, they <strong>must</strong> be returned
+     * in the order originally registered through the <code>add()</code>
+     * method.
+     */
+    abstract public List rules();
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/ArrayStack.java b/connectors/util/java/org/apache/tomcat/util/digester/ArrayStack.java
new file mode 100644
index 0000000..ac908eb
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/ArrayStack.java
@@ -0,0 +1,168 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.digester;
+
+import java.util.ArrayList;
+import java.util.EmptyStackException;
+
+/**
+ * <p>Imported copy of the <code>ArrayStack</code> class from
+ * Commons Collections, which was the only direct dependency from Digester.</p>
+ *
+ * <p><strong>WARNNG</strong> - This class is public solely to allow it to be
+ * used from subpackages of <code>org.apache.commons.digester</code>.
+ * It should not be considered part of the public API of Commons Digester.
+ * If you want to use such a class yourself, you should use the one from
+ * Commons Collections directly.</p>
+ *
+ * <p>An implementation of the {@link java.util.Stack} API that is based on an
+ * <code>ArrayList</code> instead of a <code>Vector</code>, so it is not
+ * synchronized to protect against multi-threaded access.  The implementation
+ * is therefore operates faster in environments where you do not need to
+ * worry about multiple thread contention.</p>
+ *
+ * <p>Unlike <code>Stack</code>, <code>ArrayStack</code> accepts null entries.
+ * </p>
+ *
+ * @see java.util.Stack
+ * @since Digester 1.6 (from Commons Collections 1.0)
+ */
+public class ArrayStack extends ArrayList {
+
+    /** Ensure serialization compatibility */    
+    private static final long serialVersionUID = 2130079159931574599L;
+
+    /**
+     * Constructs a new empty <code>ArrayStack</code>. The initial size
+     * is controlled by <code>ArrayList</code> and is currently 10.
+     */
+    public ArrayStack() {
+        super();
+    }
+
+    /**
+     * Constructs a new empty <code>ArrayStack</code> with an initial size.
+     * 
+     * @param initialSize  the initial size to use
+     * @throws IllegalArgumentException  if the specified initial size
+     *  is negative
+     */
+    public ArrayStack(int initialSize) {
+        super(initialSize);
+    }
+
+    /**
+     * Return <code>true</code> if this stack is currently empty.
+     * <p>
+     * This method exists for compatibility with <code>java.util.Stack</code>.
+     * New users of this class should use <code>isEmpty</code> instead.
+     * 
+     * @return true if the stack is currently empty
+     */
+    public boolean empty() {
+        return isEmpty();
+    }
+
+    /**
+     * Returns the top item off of this stack without removing it.
+     *
+     * @return the top item on the stack
+     * @throws EmptyStackException  if the stack is empty
+     */
+    public Object peek() throws EmptyStackException {
+        int n = size();
+        if (n <= 0) {
+            throw new EmptyStackException();
+        } else {
+            return get(n - 1);
+        }
+    }
+
+    /**
+     * Returns the n'th item down (zero-relative) from the top of this
+     * stack without removing it.
+     *
+     * @param n  the number of items down to go
+     * @return the n'th item on the stack, zero relative
+     * @throws EmptyStackException  if there are not enough items on the
+     *  stack to satisfy this request
+     */
+    public Object peek(int n) throws EmptyStackException {
+        int m = (size() - n) - 1;
+        if (m < 0) {
+            throw new EmptyStackException();
+        } else {
+            return get(m);
+        }
+    }
+
+    /**
+     * Pops the top item off of this stack and return it.
+     *
+     * @return the top item on the stack
+     * @throws EmptyStackException  if the stack is empty
+     */
+    public Object pop() throws EmptyStackException {
+        int n = size();
+        if (n <= 0) {
+            throw new EmptyStackException();
+        } else {
+            return remove(n - 1);
+        }
+    }
+
+    /**
+     * Pushes a new item onto the top of this stack. The pushed item is also
+     * returned. This is equivalent to calling <code>add</code>.
+     *
+     * @param item  the item to be added
+     * @return the item just pushed
+     */
+    public Object push(Object item) {
+        add(item);
+        return item;
+    }
+
+
+    /**
+     * Returns the one-based position of the distance from the top that the
+     * specified object exists on this stack, where the top-most element is
+     * considered to be at distance <code>1</code>.  If the object is not
+     * present on the stack, return <code>-1</code> instead.  The
+     * <code>equals()</code> method is used to compare to the items
+     * in this stack.
+     *
+     * @param object  the object to be searched for
+     * @return the 1-based depth into the stack of the object, or -1 if not found
+     */
+    public int search(Object object) {
+        int i = size() - 1;        // Current index
+        int n = 1;                 // Current distance
+        while (i >= 0) {
+            Object current = get(i);
+            if ((object == null && current == null) ||
+                (object != null && object.equals(current))) {
+                return n;
+            }
+            i--;
+            n++;
+        }
+        return -1;
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/BeanPropertySetterRule.java b/connectors/util/java/org/apache/tomcat/util/digester/BeanPropertySetterRule.java
new file mode 100644
index 0000000..1f25eb8
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/BeanPropertySetterRule.java
@@ -0,0 +1,202 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+
+import org.apache.tomcat.util.IntrospectionUtils;
+
+
+/**
+ * <p> Rule implements sets a bean property on the top object
+ * to the body text.</p>
+ *
+ * <p> The property set:</p>
+ * <ul><li>can be specified when the rule is created</li>
+ * <li>or can match the current element when the rule is called.</li></ul>
+ *
+ * <p> Using the second method and the {@link ExtendedBaseRules} child match
+ * pattern, all the child elements can be automatically mapped to properties
+ * on the parent object.</p>
+ */
+
+public class BeanPropertySetterRule extends Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * <p>Construct rule that sets the given property from the body text.</p>
+     *
+     * @param digester associated <code>Digester</code>
+     * @param propertyName name of property to set
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #BeanPropertySetterRule(String propertyName)} instead.
+     */
+    public BeanPropertySetterRule(Digester digester, String propertyName) {
+
+        this(propertyName);
+
+    }
+
+    /**
+     * <p>Construct rule that automatically sets a property from the body text.
+     *
+     * <p> This construct creates a rule that sets the property
+     * on the top object named the same as the current element.
+     *
+     * @param digester associated <code>Digester</code>
+     *     
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #BeanPropertySetterRule()} instead.
+     */
+    public BeanPropertySetterRule(Digester digester) {
+
+        this();
+
+    }
+
+    /**
+     * <p>Construct rule that sets the given property from the body text.</p>
+     *
+     * @param propertyName name of property to set
+     */
+    public BeanPropertySetterRule(String propertyName) {
+
+        this.propertyName = propertyName;
+
+    }
+
+    /**
+     * <p>Construct rule that automatically sets a property from the body text.
+     *
+     * <p> This construct creates a rule that sets the property
+     * on the top object named the same as the current element.
+     */
+    public BeanPropertySetterRule() {
+
+        this((String)null);
+
+    }
+    
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Set this property on the top object.
+     */
+    protected String propertyName = null;
+
+
+    /**
+     * The body text used to set the property.
+     */
+    protected String bodyText = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the body text of this element.
+     *
+     * @param namespace the namespace URI of the matching element, or an 
+     *   empty string if the parser is not namespace aware or the element has
+     *   no namespace
+     * @param name the local name if the parser is namespace aware, or just 
+     *   the element name otherwise
+     * @param text The text of the body of this element
+     */
+    public void body(String namespace, String name, String text)
+        throws Exception {
+
+        // log some debugging information
+        if (digester.log.isDebugEnabled()) {
+            digester.log.debug("[BeanPropertySetterRule]{" +
+                    digester.match + "} Called with text '" + text + "'");
+        }
+
+        bodyText = text.trim();
+
+    }
+
+
+    /**
+     * Process the end of this element.
+     *
+     * @param namespace the namespace URI of the matching element, or an 
+     *   empty string if the parser is not namespace aware or the element has
+     *   no namespace
+     * @param name the local name if the parser is namespace aware, or just 
+     *   the element name otherwise
+     *
+     * @exception NoSuchMethodException if the bean does not
+     *  have a writeable property of the specified name
+     */
+    public void end(String namespace, String name) throws Exception {
+
+        String property = propertyName;
+
+        if (property == null) {
+            // If we don't have a specific property name,
+            // use the element name.
+            property = name;
+        }
+
+        // Get a reference to the top object
+        Object top = digester.peek();
+
+        // log some debugging information
+        if (digester.log.isDebugEnabled()) {
+            digester.log.debug("[BeanPropertySetterRule]{" + digester.match +
+                    "} Set " + top.getClass().getName() + " property " +
+                               property + " with text " + bodyText);
+        }
+
+        // Set the property (with conversion as necessary)
+        IntrospectionUtils.setProperty(top, property, bodyText);
+
+    }
+
+
+    /**
+     * Clean up after parsing is complete.
+     */
+    public void finish() throws Exception {
+
+        bodyText = null;
+
+    }
+
+
+    /**
+     * Render a printable version of this Rule.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("BeanPropertySetterRule[");
+        sb.append("propertyName=");
+        sb.append(propertyName);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/CallMethodRule.java b/connectors/util/java/org/apache/tomcat/util/digester/CallMethodRule.java
new file mode 100644
index 0000000..67df857
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/CallMethodRule.java
@@ -0,0 +1,629 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.xml.sax.Attributes;
+
+
+/**
+ * <p>Rule implementation that calls a method on an object on the stack
+ * (normally the top/parent object), passing arguments collected from 
+ * subsequent <code>CallParamRule</code> rules or from the body of this
+ * element. </p>
+ *
+ * <p>By using {@link #CallMethodRule(String methodName)} 
+ * a method call can be made to a method which accepts no
+ * arguments.</p>
+ *
+ * <p>Incompatible method parameter types are converted 
+ * using <code>org.apache.commons.beanutils.ConvertUtils</code>.
+ * </p>
+ *
+ * <p>This rule now uses
+ * <a href="http://jakarta.apache.org/commons/beanutils/apidocs/org/apache/commons/beanutils/MethodUtils.html">
+ * org.apache.commons.beanutils.MethodUtils#invokeMethod
+ * </a> by default.
+ * This increases the kinds of methods successfully and allows primitives
+ * to be matched by passing in wrapper classes.
+ * There are rare cases when org.apache.commons.beanutils.MethodUtils#invokeExactMethod 
+ * (the old default) is required.
+ * This method is much stricter in its reflection.
+ * Setting the <code>UseExactMatch</code> to true reverts to the use of this 
+ * method.</p>
+ *
+ * <p>Note that the target method is invoked when the  <i>end</i> of
+ * the tag the CallMethodRule fired on is encountered, <i>not</i> when the
+ * last parameter becomes available. This implies that rules which fire on
+ * tags nested within the one associated with the CallMethodRule will 
+ * fire before the CallMethodRule invokes the target method. This behaviour is
+ * not configurable. </p>
+ *
+ * <p>Note also that if a CallMethodRule is expecting exactly one parameter
+ * and that parameter is not available (eg CallParamRule is used with an
+ * attribute name but the attribute does not exist) then the method will
+ * not be invoked. If a CallMethodRule is expecting more than one parameter,
+ * then it is always invoked, regardless of whether the parameters were
+ * available or not (missing parameters are passed as null values).</p>
+ */
+
+public class CallMethodRule extends Rule {
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a "call method" rule with the specified method name.  The
+     * parameter types (if any) default to java.lang.String.
+     *
+     * @param digester The associated Digester
+     * @param methodName Method name of the parent method to call
+     * @param paramCount The number of parameters to collect, or
+     *  zero for a single argument from the body of this element.
+     *
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #CallMethodRule(String methodName,int paramCount)} instead.
+     */
+    public CallMethodRule(Digester digester, String methodName,
+                          int paramCount) {
+
+        this(methodName, paramCount);
+
+    }
+
+
+    /**
+     * Construct a "call method" rule with the specified method name.
+     *
+     * @param digester The associated Digester
+     * @param methodName Method name of the parent method to call
+     * @param paramCount The number of parameters to collect, or
+     *  zero for a single argument from the body of ths element
+     * @param paramTypes The Java class names of the arguments
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
+     *  for a <code>boolean</code> parameter)
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #CallMethodRule(String methodName,int paramCount, String [] paramTypes)} instead.
+     */
+    public CallMethodRule(Digester digester, String methodName,
+                          int paramCount, String paramTypes[]) {
+
+        this(methodName, paramCount, paramTypes);
+
+    }
+
+
+    /**
+     * Construct a "call method" rule with the specified method name.
+     *
+     * @param digester The associated Digester
+     * @param methodName Method name of the parent method to call
+     * @param paramCount The number of parameters to collect, or
+     *  zero for a single argument from the body of ths element
+     * @param paramTypes The Java classes that represent the
+     *  parameter types of the method arguments
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean.TYPE</code>
+     *  for a <code>boolean</code> parameter)
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #CallMethodRule(String methodName,int paramCount, Class [] paramTypes)} instead.
+     */
+    public CallMethodRule(Digester digester, String methodName,
+                          int paramCount, Class paramTypes[]) {
+
+        this(methodName, paramCount, paramTypes);
+    }
+
+
+    /**
+     * Construct a "call method" rule with the specified method name.  The
+     * parameter types (if any) default to java.lang.String.
+     *
+     * @param methodName Method name of the parent method to call
+     * @param paramCount The number of parameters to collect, or
+     *  zero for a single argument from the body of this element.
+     */
+    public CallMethodRule(String methodName,
+                          int paramCount) {
+        this(0, methodName, paramCount);
+    }
+
+    /**
+     * Construct a "call method" rule with the specified method name.  The
+     * parameter types (if any) default to java.lang.String.
+     *
+     * @param targetOffset location of the target object. Positive numbers are
+     * relative to the top of the digester object stack. Negative numbers 
+     * are relative to the bottom of the stack. Zero implies the top
+     * object on the stack.
+     * @param methodName Method name of the parent method to call
+     * @param paramCount The number of parameters to collect, or
+     *  zero for a single argument from the body of this element.
+     */
+    public CallMethodRule(int targetOffset,
+                          String methodName,
+                          int paramCount) {
+
+        this.targetOffset = targetOffset;
+        this.methodName = methodName;
+        this.paramCount = paramCount;        
+        if (paramCount == 0) {
+            this.paramTypes = new Class[] { String.class };
+        } else {
+            this.paramTypes = new Class[paramCount];
+            for (int i = 0; i < this.paramTypes.length; i++) {
+                this.paramTypes[i] = String.class;
+            }
+        }
+
+    }
+
+    /**
+     * Construct a "call method" rule with the specified method name.  
+     * The method should accept no parameters.
+     *
+     * @param methodName Method name of the parent method to call
+     */
+    public CallMethodRule(String methodName) {
+    
+        this(0, methodName, 0, (Class[]) null);
+    
+    }
+    
+
+    /**
+     * Construct a "call method" rule with the specified method name.  
+     * The method should accept no parameters.
+     *
+     * @param targetOffset location of the target object. Positive numbers are
+     * relative to the top of the digester object stack. Negative numbers 
+     * are relative to the bottom of the stack. Zero implies the top
+     * object on the stack.
+     * @param methodName Method name of the parent method to call
+     */
+    public CallMethodRule(int targetOffset, String methodName) {
+    
+        this(targetOffset, methodName, 0, (Class[]) null);
+    
+    }
+    
+
+    /**
+     * Construct a "call method" rule with the specified method name and
+     * parameter types. If <code>paramCount</code> is set to zero the rule
+     * will use the body of this element as the single argument of the
+     * method, unless <code>paramTypes</code> is null or empty, in this
+     * case the rule will call the specified method with no arguments.
+     *
+     * @param methodName Method name of the parent method to call
+     * @param paramCount The number of parameters to collect, or
+     *  zero for a single argument from the body of ths element
+     * @param paramTypes The Java class names of the arguments
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
+     *  for a <code>boolean</code> parameter)
+     */
+    public CallMethodRule(
+                            String methodName,
+                            int paramCount, 
+                            String paramTypes[]) {
+        this(0, methodName, paramCount, paramTypes);
+    }
+
+    /**
+     * Construct a "call method" rule with the specified method name and
+     * parameter types. If <code>paramCount</code> is set to zero the rule
+     * will use the body of this element as the single argument of the
+     * method, unless <code>paramTypes</code> is null or empty, in this
+     * case the rule will call the specified method with no arguments.
+     *
+     * @param targetOffset location of the target object. Positive numbers are
+     * relative to the top of the digester object stack. Negative numbers 
+     * are relative to the bottom of the stack. Zero implies the top
+     * object on the stack.
+     * @param methodName Method name of the parent method to call
+     * @param paramCount The number of parameters to collect, or
+     *  zero for a single argument from the body of ths element
+     * @param paramTypes The Java class names of the arguments
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
+     *  for a <code>boolean</code> parameter)
+     */
+    public CallMethodRule(  int targetOffset,
+                            String methodName,
+                            int paramCount, 
+                            String paramTypes[]) {
+
+        this.targetOffset = targetOffset;
+        this.methodName = methodName;
+        this.paramCount = paramCount;
+        if (paramTypes == null) {
+            this.paramTypes = new Class[paramCount];
+            for (int i = 0; i < this.paramTypes.length; i++) {
+                this.paramTypes[i] = "abc".getClass();
+            }
+        } else {
+            // copy the parameter class names into an array
+            // the classes will be loaded when the digester is set 
+            this.paramClassNames = new String[paramTypes.length];
+            for (int i = 0; i < this.paramClassNames.length; i++) {
+                this.paramClassNames[i] = paramTypes[i];
+            }
+        }
+
+    }
+
+
+    /**
+     * Construct a "call method" rule with the specified method name and
+     * parameter types. If <code>paramCount</code> is set to zero the rule
+     * will use the body of this element as the single argument of the
+     * method, unless <code>paramTypes</code> is null or empty, in this
+     * case the rule will call the specified method with no arguments.
+     *
+     * @param methodName Method name of the parent method to call
+     * @param paramCount The number of parameters to collect, or
+     *  zero for a single argument from the body of ths element
+     * @param paramTypes The Java classes that represent the
+     *  parameter types of the method arguments
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean.TYPE</code>
+     *  for a <code>boolean</code> parameter)
+     */
+    public CallMethodRule(
+                            String methodName,
+                            int paramCount, 
+                            Class paramTypes[]) {
+        this(0, methodName, paramCount, paramTypes);
+    }
+
+    /**
+     * Construct a "call method" rule with the specified method name and
+     * parameter types. If <code>paramCount</code> is set to zero the rule
+     * will use the body of this element as the single argument of the
+     * method, unless <code>paramTypes</code> is null or empty, in this
+     * case the rule will call the specified method with no arguments.
+     *
+     * @param targetOffset location of the target object. Positive numbers are
+     * relative to the top of the digester object stack. Negative numbers 
+     * are relative to the bottom of the stack. Zero implies the top
+     * object on the stack.
+     * @param methodName Method name of the parent method to call
+     * @param paramCount The number of parameters to collect, or
+     *  zero for a single argument from the body of ths element
+     * @param paramTypes The Java classes that represent the
+     *  parameter types of the method arguments
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean.TYPE</code>
+     *  for a <code>boolean</code> parameter)
+     */
+    public CallMethodRule(  int targetOffset,
+                            String methodName,
+                            int paramCount, 
+                            Class paramTypes[]) {
+
+        this.targetOffset = targetOffset;
+        this.methodName = methodName;
+        this.paramCount = paramCount;
+        if (paramTypes == null) {
+            this.paramTypes = new Class[paramCount];
+            for (int i = 0; i < this.paramTypes.length; i++) {
+                this.paramTypes[i] = "abc".getClass();
+            }
+        } else {
+            this.paramTypes = new Class[paramTypes.length];
+            for (int i = 0; i < this.paramTypes.length; i++) {
+                this.paramTypes[i] = paramTypes[i];
+            }
+        }
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The body text collected from this element.
+     */
+    protected String bodyText = null;
+
+
+    /** 
+     * location of the target object for the call, relative to the
+     * top of the digester object stack. The default value of zero
+     * means the target object is the one on top of the stack.
+     */
+    private int targetOffset = 0;
+
+    /**
+     * The method name to call on the parent object.
+     */
+    protected String methodName = null;
+
+
+    /**
+     * The number of parameters to collect from <code>MethodParam</code> rules.
+     * If this value is zero, a single parameter will be collected from the
+     * body of this element.
+     */
+    protected int paramCount = 0;
+
+
+    /**
+     * The parameter types of the parameters to be collected.
+     */
+    protected Class paramTypes[] = null;
+
+    /**
+     * The names of the classes of the parameters to be collected.
+     * This attribute allows creation of the classes to be postponed until the digester is set.
+     */
+    private String paramClassNames[] = null;
+    
+    /**
+     * Should <code>MethodUtils.invokeExactMethod</code> be used for reflection.
+     */
+    protected boolean useExactMatch = false;
+    
+    // --------------------------------------------------------- Public Methods
+    
+    /**
+     * Should <code>MethodUtils.invokeExactMethod</code>
+     * be used for the reflection.
+     */
+    public boolean getUseExactMatch() {
+        return useExactMatch;
+    }
+    
+    /**
+     * Set whether <code>MethodUtils.invokeExactMethod</code>
+     * should be used for the reflection.
+     */    
+    public void setUseExactMatch(boolean useExactMatch)
+    { 
+        this.useExactMatch = useExactMatch;
+    }
+
+    /**
+     * Set the associated digester.
+     * If needed, this class loads the parameter classes from their names.
+     */
+    public void setDigester(Digester digester)
+    {
+        // call superclass
+        super.setDigester(digester);
+        // if necessary, load parameter classes
+        if (this.paramClassNames != null) {
+            this.paramTypes = new Class[paramClassNames.length];
+            for (int i = 0; i < this.paramClassNames.length; i++) {
+                try {
+                    this.paramTypes[i] =
+                            digester.getClassLoader().loadClass(this.paramClassNames[i]);
+                } catch (ClassNotFoundException e) {
+                    // use the digester log
+                    digester.getLogger().error("(CallMethodRule) Cannot load class " + this.paramClassNames[i], e);
+                    this.paramTypes[i] = null; // Will cause NPE later
+                }
+            }
+        }
+    }
+
+    /**
+     * Process the start of this element.
+     *
+     * @param attributes The attribute list for this element
+     */
+    public void begin(Attributes attributes) throws Exception {
+
+        // Push an array to capture the parameter values if necessary
+        if (paramCount > 0) {
+            Object parameters[] = new Object[paramCount];
+            for (int i = 0; i < parameters.length; i++) {
+                parameters[i] = null;
+            }
+            digester.pushParams(parameters);
+        }
+
+    }
+
+
+    /**
+     * Process the body text of this element.
+     *
+     * @param bodyText The body text of this element
+     */
+    public void body(String bodyText) throws Exception {
+
+        if (paramCount == 0) {
+            this.bodyText = bodyText.trim();
+        }
+
+    }
+
+
+    /**
+     * Process the end of this element.
+     */
+    public void end() throws Exception {
+
+        // Retrieve or construct the parameter values array
+        Object parameters[] = null;
+        if (paramCount > 0) {
+
+            parameters = (Object[]) digester.popParams();
+            
+            if (digester.log.isTraceEnabled()) {
+                for (int i=0,size=parameters.length;i<size;i++) {
+                    digester.log.trace("[CallMethodRule](" + i + ")" + parameters[i]) ;
+                }
+            }
+            
+            // In the case where the parameter for the method
+            // is taken from an attribute, and that attribute
+            // isn't actually defined in the source XML file,
+            // skip the method call
+            if (paramCount == 1 && parameters[0] == null) {
+                return;
+            }
+
+        } else if (paramTypes != null && paramTypes.length != 0) {
+
+            // In the case where the parameter for the method
+            // is taken from the body text, but there is no
+            // body text included in the source XML file,
+            // skip the method call
+            if (bodyText == null) {
+                return;
+            }
+
+            parameters = new Object[1];
+            parameters[0] = bodyText;
+            if (paramTypes.length == 0) {
+                paramTypes = new Class[1];
+                paramTypes[0] = "abc".getClass();
+            }
+
+        }
+
+        // Construct the parameter values array we will need
+        // We only do the conversion if the param value is a String and
+        // the specified paramType is not String. 
+        Object paramValues[] = new Object[paramTypes.length];
+        for (int i = 0; i < paramTypes.length; i++) {
+            // convert nulls and convert stringy parameters 
+            // for non-stringy param types
+            if(
+                parameters[i] == null ||
+                 (parameters[i] instanceof String && 
+                   !String.class.isAssignableFrom(paramTypes[i]))) {
+                
+                paramValues[i] =
+                        IntrospectionUtils.convert((String) parameters[i], paramTypes[i]);
+            } else {
+                paramValues[i] = parameters[i];
+            }
+        }
+
+        // Determine the target object for the method call
+        Object target;
+        if (targetOffset >= 0) {
+            target = digester.peek(targetOffset);
+        } else {
+            target = digester.peek( digester.getCount() + targetOffset );
+        }
+        
+        if (target == null) {
+            StringBuffer sb = new StringBuffer();
+            sb.append("[CallMethodRule]{");
+            sb.append(digester.match);
+            sb.append("} Call target is null (");
+            sb.append("targetOffset=");
+            sb.append(targetOffset);
+            sb.append(",stackdepth=");
+            sb.append(digester.getCount());
+            sb.append(")");
+            throw new org.xml.sax.SAXException(sb.toString());
+        }
+        
+        // Invoke the required method on the top object
+        if (digester.log.isDebugEnabled()) {
+            StringBuffer sb = new StringBuffer("[CallMethodRule]{");
+            sb.append(digester.match);
+            sb.append("} Call ");
+            sb.append(target.getClass().getName());
+            sb.append(".");
+            sb.append(methodName);
+            sb.append("(");
+            for (int i = 0; i < paramValues.length; i++) {
+                if (i > 0) {
+                    sb.append(",");
+                }
+                if (paramValues[i] == null) {
+                    sb.append("null");
+                } else {
+                    sb.append(paramValues[i].toString());
+                }
+                sb.append("/");
+                if (paramTypes[i] == null) {
+                    sb.append("null");
+                } else {
+                    sb.append(paramTypes[i].getName());
+                }
+            }
+            sb.append(")");
+            digester.log.debug(sb.toString());
+        }
+        Object result = IntrospectionUtils.callMethodN(target, methodName,
+                paramValues, paramTypes);   
+        processMethodCallResult(result);
+    }
+
+
+    /**
+     * Clean up after parsing is complete.
+     */
+    public void finish() throws Exception {
+
+        bodyText = null;
+
+    }
+
+    /**
+     * Subclasses may override this method to perform additional processing of the 
+     * invoked method's result.
+     *
+     * @param result the Object returned by the method invoked, possibly null
+     */
+    protected void processMethodCallResult(Object result) {
+        // do nothing
+    }
+
+    /**
+     * Render a printable version of this Rule.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("CallMethodRule[");
+        sb.append("methodName=");
+        sb.append(methodName);
+        sb.append(", paramCount=");
+        sb.append(paramCount);
+        sb.append(", paramTypes={");
+        if (paramTypes != null) {
+            for (int i = 0; i < paramTypes.length; i++) {
+                if (i > 0) {
+                    sb.append(", ");
+                }
+                sb.append(paramTypes[i].getName());
+            }
+        }
+        sb.append("}");
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/CallParamRule.java b/connectors/util/java/org/apache/tomcat/util/digester/CallParamRule.java
new file mode 100644
index 0000000..f374656
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/CallParamRule.java
@@ -0,0 +1,262 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+
+import org.xml.sax.Attributes;
+
+
+/**
+ * <p>Rule implementation that saves a parameter for use by a surrounding 
+ * <code>CallMethodRule<code>.</p>
+ *
+ * <p>This parameter may be:
+ * <ul>
+ * <li>from an attribute of the current element
+ * See {@link #CallParamRule(int paramIndex, String attributeName)}
+ * <li>from current the element body
+ * See {@link #CallParamRule(int paramIndex)}
+ * <li>from the top object on the stack. 
+ * See {@link #CallParamRule(int paramIndex, boolean fromStack)}
+ * <li>the current path being processed (separate <code>Rule</code>). 
+ * See {@link PathCallParamRule}
+ * </ul>
+ * </p>
+ */
+
+public class CallParamRule extends Rule {
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a "call parameter" rule that will save the body text of this
+     * element as the parameter value.
+     *
+     * @param digester The associated Digester
+     * @param paramIndex The zero-relative parameter number
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #CallParamRule(int paramIndex)} instead.
+     */
+    public CallParamRule(Digester digester, int paramIndex) {
+
+        this(paramIndex);
+
+    }
+
+
+    /**
+     * Construct a "call parameter" rule that will save the value of the
+     * specified attribute as the parameter value.
+     *
+     * @param digester The associated Digester
+     * @param paramIndex The zero-relative parameter number
+     * @param attributeName The name of the attribute to save
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #CallParamRule(int paramIndex, String attributeName)} instead.
+     */
+    public CallParamRule(Digester digester, int paramIndex,
+                         String attributeName) {
+
+        this(paramIndex, attributeName);
+
+    }
+
+    /**
+     * Construct a "call parameter" rule that will save the body text of this
+     * element as the parameter value.
+     *
+     * @param paramIndex The zero-relative parameter number
+     */
+    public CallParamRule(int paramIndex) {
+
+        this(paramIndex, null);
+
+    }
+
+
+    /**
+     * Construct a "call parameter" rule that will save the value of the
+     * specified attribute as the parameter value.
+     *
+     * @param paramIndex The zero-relative parameter number
+     * @param attributeName The name of the attribute to save
+     */
+    public CallParamRule(int paramIndex,
+                         String attributeName) {
+
+        this.paramIndex = paramIndex;
+        this.attributeName = attributeName;
+
+    }
+
+
+    /**
+     * Construct a "call parameter" rule.
+     *
+     * @param paramIndex The zero-relative parameter number
+     * @param fromStack should this parameter be taken from the top of the stack?
+     */    
+    public CallParamRule(int paramIndex, boolean fromStack) {
+    
+        this.paramIndex = paramIndex;  
+        this.fromStack = fromStack;
+
+    }
+    
+    /**
+     * Constructs a "call parameter" rule which sets a parameter from the stack.
+     * If the stack contains too few objects, then the parameter will be set to null.
+     *
+     * @param paramIndex The zero-relative parameter number
+     * @param stackIndex the index of the object which will be passed as a parameter. 
+     * The zeroth object is the top of the stack, 1 is the next object down and so on.
+     */    
+    public CallParamRule(int paramIndex, int stackIndex) {
+    
+        this.paramIndex = paramIndex;  
+        this.fromStack = true;
+        this.stackIndex = stackIndex;
+    }
+ 
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The attribute from which to save the parameter value
+     */
+    protected String attributeName = null;
+
+
+    /**
+     * The zero-relative index of the parameter we are saving.
+     */
+    protected int paramIndex = 0;
+
+
+    /**
+     * Is the parameter to be set from the stack?
+     */
+    protected boolean fromStack = false;
+    
+    /**
+     * The position of the object from the top of the stack
+     */
+    protected int stackIndex = 0;
+
+    /** 
+     * Stack is used to allow nested body text to be processed.
+     * Lazy creation.
+     */
+    protected ArrayStack bodyTextStack;
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the start of this element.
+     *
+     * @param attributes The attribute list for this element
+     */
+    public void begin(Attributes attributes) throws Exception {
+
+        Object param = null;
+        
+        if (attributeName != null) {
+        
+            param = attributes.getValue(attributeName);
+            
+        } else if(fromStack) {
+        
+            param = digester.peek(stackIndex);
+            
+            if (digester.log.isDebugEnabled()) {
+            
+                StringBuffer sb = new StringBuffer("[CallParamRule]{");
+                sb.append(digester.match);
+                sb.append("} Save from stack; from stack?").append(fromStack);
+                sb.append("; object=").append(param);
+                digester.log.debug(sb.toString());
+            }   
+        }
+        
+        // Have to save the param object to the param stack frame here.
+        // Can't wait until end(). Otherwise, the object will be lost.
+        // We can't save the object as instance variables, as 
+        // the instance variables will be overwritten
+        // if this CallParamRule is reused in subsequent nesting.
+        
+        if(param != null) {
+            Object parameters[] = (Object[]) digester.peekParams();
+            parameters[paramIndex] = param;
+        }
+    }
+
+
+    /**
+     * Process the body text of this element.
+     *
+     * @param bodyText The body text of this element
+     */
+    public void body(String bodyText) throws Exception {
+
+        if (attributeName == null && !fromStack) {
+            // We must wait to set the parameter until end
+            // so that we can make sure that the right set of parameters
+            // is at the top of the stack
+            if (bodyTextStack == null) {
+                bodyTextStack = new ArrayStack();
+            }
+            bodyTextStack.push(bodyText.trim());
+        }
+
+    }
+    
+    /**
+     * Process any body texts now.
+     */
+    public void end(String namespace, String name) {
+        if (bodyTextStack != null && !bodyTextStack.empty()) {
+            // what we do now is push one parameter onto the top set of parameters
+            Object parameters[] = (Object[]) digester.peekParams();
+            parameters[paramIndex] = bodyTextStack.pop();
+        }
+    }
+
+    /**
+     * Render a printable version of this Rule.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("CallParamRule[");
+        sb.append("paramIndex=");
+        sb.append(paramIndex);
+        sb.append(", attributeName=");
+        sb.append(attributeName);
+        sb.append(", from stack=");
+        sb.append(fromStack);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/Digester.java b/connectors/util/java/org/apache/tomcat/util/digester/Digester.java
new file mode 100644
index 0000000..1014ab4
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/Digester.java
@@ -0,0 +1,2845 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.tomcat.util.digester;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.lang.reflect.InvocationTargetException;
+import java.util.EmptyStackException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.xml.sax.Attributes;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.DefaultHandler;
+
+
+
+
+/**
+ * <p>A <strong>Digester</strong> processes an XML input stream by matching a
+ * series of element nesting patterns to execute Rules that have been added
+ * prior to the start of parsing.  This package was inspired by the
+ * <code>XmlMapper</code> class that was part of Tomcat 3.0 and 3.1,
+ * but is organized somewhat differently.</p>
+ *
+ * <p>See the <a href="package-summary.html#package_description">Digester
+ * Developer Guide</a> for more information.</p>
+ *
+ * <p><strong>IMPLEMENTATION NOTE</strong> - A single Digester instance may
+ * only be used within the context of a single thread at a time, and a call
+ * to <code>parse()</code> must be completed before another can be initiated
+ * even from the same thread.</p>
+ *
+ * <p><strong>IMPLEMENTATION NOTE</strong> - A bug in Xerces 2.0.2 prevents
+ * the support of XML schema. You need Xerces 2.1/2.3 and up to make
+ * this class working with XML schema</p>
+ */
+
+public class Digester extends DefaultHandler {
+
+
+    // ---------------------------------------------------------- Static Fields
+
+
+    private static class SystemPropertySource 
+        implements IntrospectionUtils.PropertySource {
+        public String getProperty( String key ) {
+            return System.getProperty(key);
+        }
+    }
+
+    protected static IntrospectionUtils.PropertySource source[] = 
+        new IntrospectionUtils.PropertySource[] { new SystemPropertySource() };
+
+
+    // --------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a new Digester with default properties.
+     */
+    public Digester() {
+
+        super();
+
+    }
+
+
+    /**
+     * Construct a new Digester, allowing a SAXParser to be passed in.  This
+     * allows Digester to be used in environments which are unfriendly to
+     * JAXP1.1 (such as WebLogic 6.0).  Thanks for the request to change go to
+     * James House (james@interobjective.com).  This may help in places where
+     * you are able to load JAXP 1.1 classes yourself.
+     */
+    public Digester(SAXParser parser) {
+
+        super();
+
+        this.parser = parser;
+
+    }
+
+
+    /**
+     * Construct a new Digester, allowing an XMLReader to be passed in.  This
+     * allows Digester to be used in environments which are unfriendly to
+     * JAXP1.1 (such as WebLogic 6.0).  Note that if you use this option you
+     * have to configure namespace and validation support yourself, as these
+     * properties only affect the SAXParser and emtpy constructor.
+     */
+    public Digester(XMLReader reader) {
+
+        super();
+
+        this.reader = reader;
+
+    }
+
+
+    // --------------------------------------------------- Instance Variables
+
+
+    /**
+     * The body text of the current element.
+     */
+    protected StringBuffer bodyText = new StringBuffer();
+
+
+    /**
+     * The stack of body text string buffers for surrounding elements.
+     */
+    protected ArrayStack bodyTexts = new ArrayStack();
+
+
+    /**
+     * Stack whose elements are List objects, each containing a list of
+     * Rule objects as returned from Rules.getMatch(). As each xml element
+     * in the input is entered, the matching rules are pushed onto this
+     * stack. After the end tag is reached, the matches are popped again.
+     * The depth of is stack is therefore exactly the same as the current
+     * "nesting" level of the input xml. 
+     *
+     * @since 1.6
+     */
+    protected ArrayStack matches = new ArrayStack(10);
+    
+    /**
+     * The class loader to use for instantiating application objects.
+     * If not specified, the context class loader, or the class loader
+     * used to load Digester itself, is used, based on the value of the
+     * <code>useContextClassLoader</code> variable.
+     */
+    protected ClassLoader classLoader = null;
+
+
+    /**
+     * Has this Digester been configured yet.
+     */
+    protected boolean configured = false;
+
+
+    /**
+     * The EntityResolver used by the SAX parser. By default it use this class
+     */
+    protected EntityResolver entityResolver;
+    
+    /**
+     * The URLs of entityValidator that have been registered, keyed by the public
+     * identifier that corresponds.
+     */
+    protected HashMap entityValidator = new HashMap();
+
+
+    /**
+     * The application-supplied error handler that is notified when parsing
+     * warnings, errors, or fatal errors occur.
+     */
+    protected ErrorHandler errorHandler = null;
+
+
+    /**
+     * The SAXParserFactory that is created the first time we need it.
+     */
+    protected SAXParserFactory factory = null;
+
+    /**
+     * @deprecated This is now managed by {@link ParserFeatureSetterFactory}
+     */
+    protected String JAXP_SCHEMA_LANGUAGE =
+        "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
+    
+    
+    /**
+     * The Locator associated with our parser.
+     */
+    protected Locator locator = null;
+
+
+    /**
+     * The current match pattern for nested element processing.
+     */
+    protected String match = "";
+
+
+    /**
+     * Do we want a "namespace aware" parser.
+     */
+    protected boolean namespaceAware = false;
+
+
+    /**
+     * Registered namespaces we are currently processing.  The key is the
+     * namespace prefix that was declared in the document.  The value is an
+     * ArrayStack of the namespace URIs this prefix has been mapped to --
+     * the top Stack element is the most current one.  (This architecture
+     * is required because documents can declare nested uses of the same
+     * prefix for different Namespace URIs).
+     */
+    protected HashMap namespaces = new HashMap();
+
+
+    /**
+     * The parameters stack being utilized by CallMethodRule and
+     * CallParamRule rules.
+     */
+    protected ArrayStack params = new ArrayStack();
+
+
+    /**
+     * The SAXParser we will use to parse the input stream.
+     */
+    protected SAXParser parser = null;
+
+
+    /**
+     * The public identifier of the DTD we are currently parsing under
+     * (if any).
+     */
+    protected String publicId = null;
+
+
+    /**
+     * The XMLReader used to parse digester rules.
+     */
+    protected XMLReader reader = null;
+
+
+    /**
+     * The "root" element of the stack (in other words, the last object
+     * that was popped.
+     */
+    protected Object root = null;
+
+
+    /**
+     * The <code>Rules</code> implementation containing our collection of
+     * <code>Rule</code> instances and associated matching policy.  If not
+     * established before the first rule is added, a default implementation
+     * will be provided.
+     */
+    protected Rules rules = null;
+
+   /**
+     * The XML schema language to use for validating an XML instance. By
+     * default this value is set to <code>W3C_XML_SCHEMA</code>
+     */
+    protected String schemaLanguage = W3C_XML_SCHEMA;
+    
+        
+    /**
+     * The XML schema to use for validating an XML instance.
+     */
+    protected String schemaLocation = null;
+    
+    
+    /**
+     * The object stack being constructed.
+     */
+    protected ArrayStack stack = new ArrayStack();
+
+
+    /**
+     * Do we want to use the Context ClassLoader when loading classes
+     * for instantiating new objects.  Default is <code>false</code>.
+     */
+    protected boolean useContextClassLoader = false;
+
+
+    /**
+     * Do we want to use a validating parser.
+     */
+    protected boolean validating = false;
+
+
+    /**
+     * The Log to which most logging calls will be made.
+     */
+    protected Log log =
+        LogFactory.getLog("org.apache.commons.digester.Digester");
+
+
+    /**
+     * The Log to which all SAX event related logging calls will be made.
+     */
+    protected Log saxLog =
+        LogFactory.getLog("org.apache.commons.digester.Digester.sax");
+    
+        
+    /**
+     * The schema language supported. By default, we use this one.
+     */
+    protected static final String W3C_XML_SCHEMA =
+        "http://www.w3.org/2001/XMLSchema";
+    
+    /** Stacks used for interrule communication, indexed by name String */
+    private HashMap stacksByName = new HashMap();
+    
+    // ------------------------------------------------------------- Properties
+
+    /**
+     * Return the currently mapped namespace URI for the specified prefix,
+     * if any; otherwise return <code>null</code>.  These mappings come and
+     * go dynamically as the document is parsed.
+     *
+     * @param prefix Prefix to look up
+     */
+    public String findNamespaceURI(String prefix) {
+        
+        ArrayStack stack = (ArrayStack) namespaces.get(prefix);
+        if (stack == null) {
+            return (null);
+        }
+        try {
+            return ((String) stack.peek());
+        } catch (EmptyStackException e) {
+            return (null);
+        }
+
+    }
+
+
+    /**
+     * Return the class loader to be used for instantiating application objects
+     * when required.  This is determined based upon the following rules:
+     * <ul>
+     * <li>The class loader set by <code>setClassLoader()</code>, if any</li>
+     * <li>The thread context class loader, if it exists and the
+     *     <code>useContextClassLoader</code> property is set to true</li>
+     * <li>The class loader used to load the Digester class itself.
+     * </ul>
+     */
+    public ClassLoader getClassLoader() {
+
+        if (this.classLoader != null) {
+            return (this.classLoader);
+        }
+        if (this.useContextClassLoader) {
+            ClassLoader classLoader =
+                    Thread.currentThread().getContextClassLoader();
+            if (classLoader != null) {
+                return (classLoader);
+            }
+        }
+        return (this.getClass().getClassLoader());
+
+    }
+
+
+    /**
+     * Set the class loader to be used for instantiating application objects
+     * when required.
+     *
+     * @param classLoader The new class loader to use, or <code>null</code>
+     *  to revert to the standard rules
+     */
+    public void setClassLoader(ClassLoader classLoader) {
+
+        this.classLoader = classLoader;
+
+    }
+
+
+    /**
+     * Return the current depth of the element stack.
+     */
+    public int getCount() {
+
+        return (stack.size());
+
+    }
+
+
+    /**
+     * Return the name of the XML element that is currently being processed.
+     */
+    public String getCurrentElementName() {
+
+        String elementName = match;
+        int lastSlash = elementName.lastIndexOf('/');
+        if (lastSlash >= 0) {
+            elementName = elementName.substring(lastSlash + 1);
+        }
+        return (elementName);
+
+    }
+
+
+    /**
+     * Return the debugging detail level of our currently enabled logger.
+     *
+     * @deprecated This method now always returns 0. Digester uses the apache
+     * jakarta commons-logging library; see the documentation for that library
+     * for more information.
+     */
+    public int getDebug() {
+
+        return (0);
+
+    }
+
+
+    /**
+     * Set the debugging detail level of our currently enabled logger.
+     *
+     * @param debug New debugging detail level (0=off, increasing integers
+     *  for more detail)
+     *
+     * @deprecated This method now has no effect at all. Digester uses
+     * the apache jakarta comons-logging library; see the documentation
+     * for that library for more information.
+     */
+    public void setDebug(int debug) {
+
+        ; // No action is taken
+
+    }
+
+
+    /**
+     * Return the error handler for this Digester.
+     */
+    public ErrorHandler getErrorHandler() {
+
+        return (this.errorHandler);
+
+    }
+
+
+    /**
+     * Set the error handler for this Digester.
+     *
+     * @param errorHandler The new error handler
+     */
+    public void setErrorHandler(ErrorHandler errorHandler) {
+
+        this.errorHandler = errorHandler;
+
+    }
+
+
+    /**
+     * Return the SAXParserFactory we will use, creating one if necessary.
+     */
+    public SAXParserFactory getFactory() {
+
+        if (factory == null) {
+            factory = SAXParserFactory.newInstance();
+            factory.setNamespaceAware(namespaceAware);
+            factory.setValidating(validating);
+        }
+        return (factory);
+
+    }
+
+
+    /**
+     * Returns a flag indicating whether the requested feature is supported
+     * by the underlying implementation of <code>org.xml.sax.XMLReader</code>.
+     * See <a href="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description"
+     * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a>
+     * for information about the standard SAX2 feature flags.
+     *
+     * @param feature Name of the feature to inquire about
+     *
+     * @exception ParserConfigurationException if a parser configuration error
+     *  occurs
+     * @exception SAXNotRecognizedException if the property name is
+     *  not recognized
+     * @exception SAXNotSupportedException if the property name is
+     *  recognized but not supported
+     */
+    public boolean getFeature(String feature)
+        throws ParserConfigurationException, SAXNotRecognizedException,
+        SAXNotSupportedException {
+
+        return (getFactory().getFeature(feature));
+
+    }
+
+
+    /**
+     * Sets a flag indicating whether the requested feature is supported
+     * by the underlying implementation of <code>org.xml.sax.XMLReader</code>.
+     * See <a href="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description"
+     * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a>
+     * for information about the standard SAX2 feature flags.  In order to be
+     * effective, this method must be called <strong>before</strong> the
+     * <code>getParser()</code> method is called for the first time, either
+     * directly or indirectly.
+     *
+     * @param feature Name of the feature to set the status for
+     * @param value The new value for this feature
+     *
+     * @exception ParserConfigurationException if a parser configuration error
+     *  occurs
+     * @exception SAXNotRecognizedException if the property name is
+     *  not recognized
+     * @exception SAXNotSupportedException if the property name is
+     *  recognized but not supported
+     */
+    public void setFeature(String feature, boolean value)
+        throws ParserConfigurationException, SAXNotRecognizedException,
+        SAXNotSupportedException {
+
+        getFactory().setFeature(feature, value);
+
+    }
+
+
+    /**
+     * Return the current Logger associated with this instance of the Digester
+     */
+    public Log getLogger() {
+
+        return log;
+
+    }
+
+
+    /**
+     * Set the current logger for this Digester.
+     */
+    public void setLogger(Log log) {
+
+        this.log = log;
+
+    }
+
+    /**
+     * Gets the logger used for logging SAX-related information.
+     * <strong>Note</strong> the output is finely grained.
+     *
+     * @since 1.6
+     */
+    public Log getSAXLogger() {
+        
+        return saxLog;
+    }
+    
+
+    /**
+     * Sets the logger used for logging SAX-related information.
+     * <strong>Note</strong> the output is finely grained.
+     * @param saxLog Log, not null
+     *
+     * @since 1.6
+     */    
+    public void setSAXLogger(Log saxLog) {
+    
+        this.saxLog = saxLog;
+    }
+
+    /**
+     * Return the current rule match path
+     */
+    public String getMatch() {
+
+        return match;
+
+    }
+
+
+    /**
+     * Return the "namespace aware" flag for parsers we create.
+     */
+    public boolean getNamespaceAware() {
+
+        return (this.namespaceAware);
+
+    }
+
+
+    /**
+     * Set the "namespace aware" flag for parsers we create.
+     *
+     * @param namespaceAware The new "namespace aware" flag
+     */
+    public void setNamespaceAware(boolean namespaceAware) {
+
+        this.namespaceAware = namespaceAware;
+
+    }
+
+    
+    /**
+     * Set the publid id of the current file being parse.
+     * @param publicId the DTD/Schema public's id.
+     */
+    public void setPublicId(String publicId){
+        this.publicId = publicId;
+    }
+    
+    
+    /**
+     * Return the public identifier of the DTD we are currently
+     * parsing under, if any.
+     */
+    public String getPublicId() {
+
+        return (this.publicId);
+
+    }
+
+
+    /**
+     * Return the namespace URI that will be applied to all subsequently
+     * added <code>Rule</code> objects.
+     */
+    public String getRuleNamespaceURI() {
+
+        return (getRules().getNamespaceURI());
+
+    }
+
+
+    /**
+     * Set the namespace URI that will be applied to all subsequently
+     * added <code>Rule</code> objects.
+     *
+     * @param ruleNamespaceURI Namespace URI that must match on all
+     *  subsequently added rules, or <code>null</code> for matching
+     *  regardless of the current namespace URI
+     */
+    public void setRuleNamespaceURI(String ruleNamespaceURI) {
+
+        getRules().setNamespaceURI(ruleNamespaceURI);
+
+    }
+
+
+    /**
+     * Return the SAXParser we will use to parse the input stream.  If there
+     * is a problem creating the parser, return <code>null</code>.
+     */
+    public SAXParser getParser() {
+
+        // Return the parser we already created (if any)
+        if (parser != null) {
+            return (parser);
+        }
+
+        // Create a new parser
+        try {
+            if (validating) {
+                Properties properties = new Properties();
+                properties.put("SAXParserFactory", getFactory());
+                if (schemaLocation != null) {
+                    properties.put("schemaLocation", schemaLocation);
+                    properties.put("schemaLanguage", schemaLanguage);
+                }
+                parser = ParserFeatureSetterFactory.newSAXParser(properties);               } else {
+                parser = getFactory().newSAXParser();
+            }
+        } catch (Exception e) {
+            log.error("Digester.getParser: ", e);
+            return (null);
+        }
+
+        return (parser);
+
+    }
+
+
+    /**
+     * Return the current value of the specified property for the underlying
+     * <code>XMLReader</code> implementation.
+     * See <a href="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description"
+     * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a>
+     * for information about the standard SAX2 properties.
+     *
+     * @param property Property name to be retrieved
+     *
+     * @exception SAXNotRecognizedException if the property name is
+     *  not recognized
+     * @exception SAXNotSupportedException if the property name is
+     *  recognized but not supported
+     */
+    public Object getProperty(String property)
+        throws SAXNotRecognizedException, SAXNotSupportedException {
+
+        return (getParser().getProperty(property));
+
+    }
+
+
+    /**
+     * Set the current value of the specified property for the underlying
+     * <code>XMLReader</code> implementation.
+     * See <a href="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description"
+     * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a>
+     * for information about the standard SAX2 properties.
+     *
+     * @param property Property name to be set
+     * @param value Property value to be set
+     *
+     * @exception SAXNotRecognizedException if the property name is
+     *  not recognized
+     * @exception SAXNotSupportedException if the property name is
+     *  recognized but not supported
+     */
+    public void setProperty(String property, Object value)
+        throws SAXNotRecognizedException, SAXNotSupportedException {
+
+        getParser().setProperty(property, value);
+
+    }
+
+
+    /**
+     * By setting the reader in the constructor, you can bypass JAXP and
+     * be able to use digester in Weblogic 6.0.  
+     *
+     * @deprecated Use getXMLReader() instead, which can throw a
+     *  SAXException if the reader cannot be instantiated
+     */
+    public XMLReader getReader() {
+
+        try {
+            return (getXMLReader());
+        } catch (SAXException e) {
+            log.error("Cannot get XMLReader", e);
+            return (null);
+        }
+
+    }
+
+
+    /**
+     * Return the <code>Rules</code> implementation object containing our
+     * rules collection and associated matching policy.  If none has been
+     * established, a default implementation will be created and returned.
+     */
+    public Rules getRules() {
+
+        if (this.rules == null) {
+            this.rules = new RulesBase();
+            this.rules.setDigester(this);
+        }
+        return (this.rules);
+
+    }
+
+    
+    /**
+     * Set the <code>Rules</code> implementation object containing our
+     * rules collection and associated matching policy.
+     *
+     * @param rules New Rules implementation
+     */
+    public void setRules(Rules rules) {
+
+        this.rules = rules;
+        this.rules.setDigester(this);
+
+    }
+
+
+    /**
+     * Return the XML Schema URI used for validating an XML instance.
+     */
+    public String getSchema() {
+
+        return (this.schemaLocation);
+
+    }
+
+
+    /**
+     * Set the XML Schema URI used for validating a XML Instance.
+     *
+     * @param schemaLocation a URI to the schema.
+     */
+    public void setSchema(String schemaLocation){
+
+        this.schemaLocation = schemaLocation;
+
+    }   
+    
+
+    /**
+     * Return the XML Schema language used when parsing.
+     */
+    public String getSchemaLanguage() {
+
+        return (this.schemaLanguage);
+
+    }
+
+
+    /**
+     * Set the XML Schema language used when parsing. By default, we use W3C.
+     *
+     * @param schemaLanguage a URI to the schema language.
+     */
+    public void setSchemaLanguage(String schemaLanguage){
+
+        this.schemaLanguage = schemaLanguage;
+
+    }   
+
+
+    /**
+     * Return the boolean as to whether the context classloader should be used.
+     */
+    public boolean getUseContextClassLoader() {
+
+        return useContextClassLoader;
+
+    }
+
+
+    /**
+     * Determine whether to use the Context ClassLoader (the one found by
+     * calling <code>Thread.currentThread().getContextClassLoader()</code>)
+     * to resolve/load classes that are defined in various rules.  If not
+     * using Context ClassLoader, then the class-loading defaults to
+     * using the calling-class' ClassLoader.
+     *
+     * @param use determines whether to use Context ClassLoader.
+     */
+    public void setUseContextClassLoader(boolean use) {
+
+        useContextClassLoader = use;
+
+    }
+
+
+    /**
+     * Return the validating parser flag.
+     */
+    public boolean getValidating() {
+
+        return (this.validating);
+
+    }
+
+
+    /**
+     * Set the validating parser flag.  This must be called before
+     * <code>parse()</code> is called the first time.
+     *
+     * @param validating The new validating parser flag.
+     */
+    public void setValidating(boolean validating) {
+
+        this.validating = validating;
+
+    }
+
+
+    /**
+     * Return the XMLReader to be used for parsing the input document.
+     *
+     * FIX ME: there is a bug in JAXP/XERCES that prevent the use of a 
+     * parser that contains a schema with a DTD.
+     * @exception SAXException if no XMLReader can be instantiated
+     */
+    public XMLReader getXMLReader() throws SAXException {
+        if (reader == null){
+            reader = getParser().getXMLReader();
+        }        
+                               
+        reader.setDTDHandler(this);           
+        reader.setContentHandler(this);        
+        
+        if (entityResolver == null){
+            reader.setEntityResolver(this);
+        } else {
+            reader.setEntityResolver(entityResolver);           
+        }
+        
+        reader.setErrorHandler(this);
+        return reader;
+    }
+
+    // ------------------------------------------------- ContentHandler Methods
+
+
+    /**
+     * Process notification of character data received from the body of
+     * an XML element.
+     *
+     * @param buffer The characters from the XML document
+     * @param start Starting offset into the buffer
+     * @param length Number of characters from the buffer
+     *
+     * @exception SAXException if a parsing error is to be reported
+     */
+    public void characters(char buffer[], int start, int length)
+            throws SAXException {
+
+        if (saxLog.isDebugEnabled()) {
+            saxLog.debug("characters(" + new String(buffer, start, length) + ")");
+        }
+
+        bodyText.append(buffer, start, length);
+
+    }
+
+
+    /**
+     * Process notification of the end of the document being reached.
+     *
+     * @exception SAXException if a parsing error is to be reported
+     */
+    public void endDocument() throws SAXException {
+
+        if (saxLog.isDebugEnabled()) {
+            if (getCount() > 1) {
+                saxLog.debug("endDocument():  " + getCount() +
+                             " elements left");
+            } else {
+                saxLog.debug("endDocument()");
+            }
+        }
+
+        while (getCount() > 1) {
+            pop();
+        }
+
+        // Fire "finish" events for all defined rules
+        Iterator rules = getRules().rules().iterator();
+        while (rules.hasNext()) {
+            Rule rule = (Rule) rules.next();
+            try {
+                rule.finish();
+            } catch (Exception e) {
+                log.error("Finish event threw exception", e);
+                throw createSAXException(e);
+            } catch (Error e) {
+                log.error("Finish event threw error", e);
+                throw e;
+            }
+        }
+
+        // Perform final cleanup
+        clear();
+
+    }
+
+
+    /**
+     * Process notification of the end of an XML element being reached.
+     *
+     * @param namespaceURI - The Namespace URI, or the empty string if the
+     *   element has no Namespace URI or if Namespace processing is not
+     *   being performed.
+     * @param localName - The local name (without prefix), or the empty
+     *   string if Namespace processing is not being performed.
+     * @param qName - The qualified XML 1.0 name (with prefix), or the
+     *   empty string if qualified names are not available.
+     * @exception SAXException if a parsing error is to be reported
+     */
+    public void endElement(String namespaceURI, String localName,
+                           String qName) throws SAXException {
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            if (saxLog.isDebugEnabled()) {
+                saxLog.debug("endElement(" + namespaceURI + "," + localName +
+                        "," + qName + ")");
+            }
+            log.debug("  match='" + match + "'");
+            log.debug("  bodyText='" + bodyText + "'");
+        }
+
+        // Parse system properties
+        bodyText = updateBodyText(bodyText);
+
+        // the actual element name is either in localName or qName, depending 
+        // on whether the parser is namespace aware
+        String name = localName;
+        if ((name == null) || (name.length() < 1)) {
+            name = qName;
+        }
+
+        // Fire "body" events for all relevant rules
+        List rules = (List) matches.pop();
+        if ((rules != null) && (rules.size() > 0)) {
+            String bodyText = this.bodyText.toString();
+            for (int i = 0; i < rules.size(); i++) {
+                try {
+                    Rule rule = (Rule) rules.get(i);
+                    if (debug) {
+                        log.debug("  Fire body() for " + rule);
+                    }
+                    rule.body(namespaceURI, name, bodyText);
+                } catch (Exception e) {
+                    log.error("Body event threw exception", e);
+                    throw createSAXException(e);
+                } catch (Error e) {
+                    log.error("Body event threw error", e);
+                    throw e;
+                }
+            }
+        } else {
+            if (debug) {
+                log.debug("  No rules found matching '" + match + "'.");
+            }
+        }
+
+        // Recover the body text from the surrounding element
+        bodyText = (StringBuffer) bodyTexts.pop();
+        if (debug) {
+            log.debug("  Popping body text '" + bodyText.toString() + "'");
+        }
+
+        // Fire "end" events for all relevant rules in reverse order
+        if (rules != null) {
+            for (int i = 0; i < rules.size(); i++) {
+                int j = (rules.size() - i) - 1;
+                try {
+                    Rule rule = (Rule) rules.get(j);
+                    if (debug) {
+                        log.debug("  Fire end() for " + rule);
+                    }
+                    rule.end(namespaceURI, name);
+                } catch (Exception e) {
+                    log.error("End event threw exception", e);
+                    throw createSAXException(e);
+                } catch (Error e) {
+                    log.error("End event threw error", e);
+                    throw e;
+                }
+            }
+        }
+
+        // Recover the previous match expression
+        int slash = match.lastIndexOf('/');
+        if (slash >= 0) {
+            match = match.substring(0, slash);
+        } else {
+            match = "";
+        }
+
+    }
+
+
+    /**
+     * Process notification that a namespace prefix is going out of scope.
+     *
+     * @param prefix Prefix that is going out of scope
+     *
+     * @exception SAXException if a parsing error is to be reported
+     */
+    public void endPrefixMapping(String prefix) throws SAXException {
+
+        if (saxLog.isDebugEnabled()) {
+            saxLog.debug("endPrefixMapping(" + prefix + ")");
+        }
+
+        // Deregister this prefix mapping
+        ArrayStack stack = (ArrayStack) namespaces.get(prefix);
+        if (stack == null) {
+            return;
+        }
+        try {
+            stack.pop();
+            if (stack.empty())
+                namespaces.remove(prefix);
+        } catch (EmptyStackException e) {
+            throw createSAXException("endPrefixMapping popped too many times");
+        }
+
+    }
+
+
+    /**
+     * Process notification of ignorable whitespace received from the body of
+     * an XML element.
+     *
+     * @param buffer The characters from the XML document
+     * @param start Starting offset into the buffer
+     * @param len Number of characters from the buffer
+     *
+     * @exception SAXException if a parsing error is to be reported
+     */
+    public void ignorableWhitespace(char buffer[], int start, int len)
+            throws SAXException {
+
+        if (saxLog.isDebugEnabled()) {
+            saxLog.debug("ignorableWhitespace(" +
+                    new String(buffer, start, len) + ")");
+        }
+
+        ;   // No processing required
+
+    }
+
+
+    /**
+     * Process notification of a processing instruction that was encountered.
+     *
+     * @param target The processing instruction target
+     * @param data The processing instruction data (if any)
+     *
+     * @exception SAXException if a parsing error is to be reported
+     */
+    public void processingInstruction(String target, String data)
+            throws SAXException {
+
+        if (saxLog.isDebugEnabled()) {
+            saxLog.debug("processingInstruction('" + target + "','" + data + "')");
+        }
+
+        ;   // No processing is required
+
+    }
+
+
+    /**
+     * Gets the document locator associated with our parser.
+     *
+     * @return the Locator supplied by the document parser
+     */
+    public Locator getDocumentLocator() {
+
+        return locator;
+
+    }
+
+    /**
+     * Sets the document locator associated with our parser.
+     *
+     * @param locator The new locator
+     */
+    public void setDocumentLocator(Locator locator) {
+
+        if (saxLog.isDebugEnabled()) {
+            saxLog.debug("setDocumentLocator(" + locator + ")");
+        }
+
+        this.locator = locator;
+
+    }
+
+
+    /**
+     * Process notification of a skipped entity.
+     *
+     * @param name Name of the skipped entity
+     *
+     * @exception SAXException if a parsing error is to be reported
+     */
+    public void skippedEntity(String name) throws SAXException {
+
+        if (saxLog.isDebugEnabled()) {
+            saxLog.debug("skippedEntity(" + name + ")");
+        }
+
+        ; // No processing required
+
+    }
+
+
+    /**
+     * Process notification of the beginning of the document being reached.
+     *
+     * @exception SAXException if a parsing error is to be reported
+     */
+    public void startDocument() throws SAXException {
+
+        if (saxLog.isDebugEnabled()) {
+            saxLog.debug("startDocument()");
+        }
+
+        // ensure that the digester is properly configured, as 
+        // the digester could be used as a SAX ContentHandler
+        // rather than via the parse() methods.
+        configure();
+    }
+
+
+    /**
+     * Process notification of the start of an XML element being reached.
+     *
+     * @param namespaceURI The Namespace URI, or the empty string if the element
+     *   has no Namespace URI or if Namespace processing is not being performed.
+     * @param localName The local name (without prefix), or the empty
+     *   string if Namespace processing is not being performed.
+     * @param qName The qualified name (with prefix), or the empty
+     *   string if qualified names are not available.\
+     * @param list The attributes attached to the element. If there are
+     *   no attributes, it shall be an empty Attributes object. 
+     * @exception SAXException if a parsing error is to be reported
+     */
+    public void startElement(String namespaceURI, String localName,
+                             String qName, Attributes list)
+            throws SAXException {
+        boolean debug = log.isDebugEnabled();
+        
+        if (saxLog.isDebugEnabled()) {
+            saxLog.debug("startElement(" + namespaceURI + "," + localName + "," +
+                    qName + ")");
+        }
+        
+        // Parse system properties
+        list = updateAttributes(list);
+        
+        // Save the body text accumulated for our surrounding element
+        bodyTexts.push(bodyText);
+        if (debug) {
+            log.debug("  Pushing body text '" + bodyText.toString() + "'");
+        }
+        bodyText = new StringBuffer();
+
+        // the actual element name is either in localName or qName, depending 
+        // on whether the parser is namespace aware
+        String name = localName;
+        if ((name == null) || (name.length() < 1)) {
+            name = qName;
+        }
+
+        // Compute the current matching rule
+        StringBuffer sb = new StringBuffer(match);
+        if (match.length() > 0) {
+            sb.append('/');
+        }
+        sb.append(name);
+        match = sb.toString();
+        if (debug) {
+            log.debug("  New match='" + match + "'");
+        }
+
+        // Fire "begin" events for all relevant rules
+        List rules = getRules().match(namespaceURI, match);
+        matches.push(rules);
+        if ((rules != null) && (rules.size() > 0)) {
+            for (int i = 0; i < rules.size(); i++) {
+                try {
+                    Rule rule = (Rule) rules.get(i);
+                    if (debug) {
+                        log.debug("  Fire begin() for " + rule);
+                    }
+                    rule.begin(namespaceURI, name, list);
+                } catch (Exception e) {
+                    log.error("Begin event threw exception", e);
+                    throw createSAXException(e);
+                } catch (Error e) {
+                    log.error("Begin event threw error", e);
+                    throw e;
+                }
+            }
+        } else {
+            if (debug) {
+                log.debug("  No rules found matching '" + match + "'.");
+            }
+        }
+
+    }
+
+
+    /**
+     * Process notification that a namespace prefix is coming in to scope.
+     *
+     * @param prefix Prefix that is being declared
+     * @param namespaceURI Corresponding namespace URI being mapped to
+     *
+     * @exception SAXException if a parsing error is to be reported
+     */
+    public void startPrefixMapping(String prefix, String namespaceURI)
+            throws SAXException {
+
+        if (saxLog.isDebugEnabled()) {
+            saxLog.debug("startPrefixMapping(" + prefix + "," + namespaceURI + ")");
+        }
+
+        // Register this prefix mapping
+        ArrayStack stack = (ArrayStack) namespaces.get(prefix);
+        if (stack == null) {
+            stack = new ArrayStack();
+            namespaces.put(prefix, stack);
+        }
+        stack.push(namespaceURI);
+
+    }
+
+
+    // ----------------------------------------------------- DTDHandler Methods
+
+
+    /**
+     * Receive notification of a notation declaration event.
+     *
+     * @param name The notation name
+     * @param publicId The public identifier (if any)
+     * @param systemId The system identifier (if any)
+     */
+    public void notationDecl(String name, String publicId, String systemId) {
+
+        if (saxLog.isDebugEnabled()) {
+            saxLog.debug("notationDecl(" + name + "," + publicId + "," +
+                    systemId + ")");
+        }
+
+    }
+
+
+    /**
+     * Receive notification of an unparsed entity declaration event.
+     *
+     * @param name The unparsed entity name
+     * @param publicId The public identifier (if any)
+     * @param systemId The system identifier (if any)
+     * @param notation The name of the associated notation
+     */
+    public void unparsedEntityDecl(String name, String publicId,
+                                   String systemId, String notation) {
+
+        if (saxLog.isDebugEnabled()) {
+            saxLog.debug("unparsedEntityDecl(" + name + "," + publicId + "," +
+                    systemId + "," + notation + ")");
+        }
+
+    }
+
+
+    // ----------------------------------------------- EntityResolver Methods
+
+    /**
+     * Set the <code>EntityResolver</code> used by SAX when resolving
+     * public id and system id.
+     * This must be called before the first call to <code>parse()</code>.
+     * @param entityResolver a class that implement the <code>EntityResolver</code> interface.
+     */
+    public void setEntityResolver(EntityResolver entityResolver){
+        this.entityResolver = entityResolver;
+    }
+    
+    
+    /**
+     * Return the Entity Resolver used by the SAX parser.
+     * @return Return the Entity Resolver used by the SAX parser.
+     */
+    public EntityResolver getEntityResolver(){
+        return entityResolver;
+    }
+
+    /**
+     * Resolve the requested external entity.
+     *
+     * @param publicId The public identifier of the entity being referenced
+     * @param systemId The system identifier of the entity being referenced
+     *
+     * @exception SAXException if a parsing exception occurs
+     * 
+     */
+    public InputSource resolveEntity(String publicId, String systemId)
+            throws SAXException {     
+                
+        if (saxLog.isDebugEnabled()) {
+            saxLog.debug("resolveEntity('" + publicId + "', '" + systemId + "')");
+        }
+        
+        if (publicId != null)
+            this.publicId = publicId;
+                                       
+        // Has this system identifier been registered?
+        String entityURL = null;
+        if (publicId != null) {
+            entityURL = (String) entityValidator.get(publicId);
+        }
+         
+        // Redirect the schema location to a local destination
+        if (schemaLocation != null && entityURL == null && systemId != null){
+            entityURL = (String)entityValidator.get(systemId);
+        } 
+
+        if (entityURL == null) { 
+            if (systemId == null) {
+                // cannot resolve
+                if (log.isDebugEnabled()) {
+                    log.debug(" Cannot resolve entity: '" + entityURL + "'");
+                }
+                return (null);
+                
+            } else {
+                // try to resolve using system ID
+                if (log.isDebugEnabled()) {
+                    log.debug(" Trying to resolve using system ID '" + systemId + "'");
+                } 
+                entityURL = systemId;
+            }
+        }
+        
+        // Return an input source to our alternative URL
+        if (log.isDebugEnabled()) {
+            log.debug(" Resolving to alternate DTD '" + entityURL + "'");
+        }  
+        
+        try {
+            return (new InputSource(entityURL));
+        } catch (Exception e) {
+            throw createSAXException(e);
+        }
+    }
+
+
+    // ------------------------------------------------- ErrorHandler Methods
+
+
+    /**
+     * Forward notification of a parsing error to the application supplied
+     * error handler (if any).
+     *
+     * @param exception The error information
+     *
+     * @exception SAXException if a parsing exception occurs
+     */
+    public void error(SAXParseException exception) throws SAXException {
+
+        log.error("Parse Error at line " + exception.getLineNumber() +
+                " column " + exception.getColumnNumber() + ": " +
+                exception.getMessage(), exception);
+        if (errorHandler != null) {
+            errorHandler.error(exception);
+        }
+
+    }
+
+
+    /**
+     * Forward notification of a fatal parsing error to the application
+     * supplied error handler (if any).
+     *
+     * @param exception The fatal error information
+     *
+     * @exception SAXException if a parsing exception occurs
+     */
+    public void fatalError(SAXParseException exception) throws SAXException {
+
+        log.error("Parse Fatal Error at line " + exception.getLineNumber() +
+                " column " + exception.getColumnNumber() + ": " +
+                exception.getMessage(), exception);
+        if (errorHandler != null) {
+            errorHandler.fatalError(exception);
+        }
+
+    }
+
+
+    /**
+     * Forward notification of a parse warning to the application supplied
+     * error handler (if any).
+     *
+     * @param exception The warning information
+     *
+     * @exception SAXException if a parsing exception occurs
+     */
+    public void warning(SAXParseException exception) throws SAXException {
+         if (errorHandler != null) {
+            log.warn("Parse Warning Error at line " + exception.getLineNumber() +
+                " column " + exception.getColumnNumber() + ": " +
+                exception.getMessage(), exception);
+            
+            errorHandler.warning(exception);
+        }
+
+    }
+
+
+    // ------------------------------------------------------- Public Methods
+
+
+    /**
+     * Log a message to our associated logger.
+     *
+     * @param message The message to be logged
+     * @deprecated Call getLogger() and use it's logging methods
+     */
+    public void log(String message) {
+
+        log.info(message);
+
+    }
+
+
+    /**
+     * Log a message and exception to our associated logger.
+     *
+     * @param message The message to be logged
+     * @deprecated Call getLogger() and use it's logging methods
+     */
+    public void log(String message, Throwable exception) {
+
+        log.error(message, exception);
+
+    }
+
+
+    /**
+     * Parse the content of the specified file using this Digester.  Returns
+     * the root element from the object stack (if any).
+     *
+     * @param file File containing the XML data to be parsed
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception SAXException if a parsing exception occurs
+     */
+    public Object parse(File file) throws IOException, SAXException {
+
+        configure();
+        InputSource input = new InputSource(new FileInputStream(file));
+        input.setSystemId("file://" + file.getAbsolutePath());
+        getXMLReader().parse(input);
+        return (root);
+
+    }   
+    /**
+     * Parse the content of the specified input source using this Digester.
+     * Returns the root element from the object stack (if any).
+     *
+     * @param input Input source containing the XML data to be parsed
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception SAXException if a parsing exception occurs
+     */
+    public Object parse(InputSource input) throws IOException, SAXException {
+ 
+        configure();
+        getXMLReader().parse(input);
+        return (root);
+
+    }
+
+
+    /**
+     * Parse the content of the specified input stream using this Digester.
+     * Returns the root element from the object stack (if any).
+     *
+     * @param input Input stream containing the XML data to be parsed
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception SAXException if a parsing exception occurs
+     */
+    public Object parse(InputStream input) throws IOException, SAXException {
+
+        configure();
+        InputSource is = new InputSource(input);
+        getXMLReader().parse(is);
+        return (root);
+
+    }
+
+
+    /**
+     * Parse the content of the specified reader using this Digester.
+     * Returns the root element from the object stack (if any).
+     *
+     * @param reader Reader containing the XML data to be parsed
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception SAXException if a parsing exception occurs
+     */
+    public Object parse(Reader reader) throws IOException, SAXException {
+
+        configure();
+        InputSource is = new InputSource(reader);
+        getXMLReader().parse(is);
+        return (root);
+
+    }
+
+
+    /**
+     * Parse the content of the specified URI using this Digester.
+     * Returns the root element from the object stack (if any).
+     *
+     * @param uri URI containing the XML data to be parsed
+     *
+     * @exception IOException if an input/output error occurs
+     * @exception SAXException if a parsing exception occurs
+     */
+    public Object parse(String uri) throws IOException, SAXException {
+
+        configure();
+        InputSource is = new InputSource(uri);
+        getXMLReader().parse(is);
+        return (root);
+
+    }
+
+
+    /**
+     * <p>Register the specified DTD URL for the specified public identifier.
+     * This must be called before the first call to <code>parse()</code>.
+     * </p><p>
+     * <code>Digester</code> contains an internal <code>EntityResolver</code>
+     * implementation. This maps <code>PUBLICID</code>'s to URLs 
+     * (from which the resource will be loaded). A common use case for this
+     * method is to register local URLs (possibly computed at runtime by a 
+     * classloader) for DTDs. This allows the performance advantage of using
+     * a local version without having to ensure every <code>SYSTEM</code>
+     * URI on every processed xml document is local. This implementation provides
+     * only basic functionality. If more sophisticated features are required,
+     * using {@link #setEntityResolver} to set a custom resolver is recommended.
+     * </p><p>
+     * <strong>Note:</strong> This method will have no effect when a custom 
+     * <code>EntityResolver</code> has been set. (Setting a custom 
+     * <code>EntityResolver</code> overrides the internal implementation.) 
+     * </p>
+     * @param publicId Public identifier of the DTD to be resolved
+     * @param entityURL The URL to use for reading this DTD
+     */
+    public void register(String publicId, String entityURL) {
+
+        if (log.isDebugEnabled()) {
+            log.debug("register('" + publicId + "', '" + entityURL + "'");
+        }
+        entityValidator.put(publicId, entityURL);
+
+    }
+
+
+    // --------------------------------------------------------- Rule Methods
+
+
+    /**
+     * <p>Register a new Rule matching the specified pattern.
+     * This method sets the <code>Digester</code> property on the rule.</p>
+     *
+     * @param pattern Element matching pattern
+     * @param rule Rule to be registered
+     */
+    public void addRule(String pattern, Rule rule) {
+
+        rule.setDigester(this);
+        getRules().add(pattern, rule);
+
+    }
+
+
+    /**
+     * Register a set of Rule instances defined in a RuleSet.
+     *
+     * @param ruleSet The RuleSet instance to configure from
+     */
+    public void addRuleSet(RuleSet ruleSet) {
+
+        String oldNamespaceURI = getRuleNamespaceURI();
+        String newNamespaceURI = ruleSet.getNamespaceURI();
+        if (log.isDebugEnabled()) {
+            if (newNamespaceURI == null) {
+                log.debug("addRuleSet() with no namespace URI");
+            } else {
+                log.debug("addRuleSet() with namespace URI " + newNamespaceURI);
+            }
+        }
+        setRuleNamespaceURI(newNamespaceURI);
+        ruleSet.addRuleInstances(this);
+        setRuleNamespaceURI(oldNamespaceURI);
+
+    }
+
+
+    /**
+     * Add a "bean property setter" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @see BeanPropertySetterRule
+     */
+    public void addBeanPropertySetter(String pattern) {
+
+        addRule(pattern,
+                new BeanPropertySetterRule());
+
+    }
+
+
+    /**
+     * Add a "bean property setter" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param propertyName Name of property to set
+     * @see BeanPropertySetterRule
+     */
+    public void addBeanPropertySetter(String pattern,
+                                      String propertyName) {
+
+        addRule(pattern,
+                new BeanPropertySetterRule(propertyName));
+
+    }
+
+    /**
+     * Add an "call method" rule for a method which accepts no arguments.
+     *
+     * @param pattern Element matching pattern
+     * @param methodName Method name to be called
+     * @see CallMethodRule
+     */
+    public void addCallMethod(String pattern, String methodName) {
+
+        addRule(
+                pattern,
+                new CallMethodRule(methodName));
+
+    }
+
+    /**
+     * Add an "call method" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param methodName Method name to be called
+     * @param paramCount Number of expected parameters (or zero
+     *  for a single parameter from the body of this element)
+     * @see CallMethodRule
+     */
+    public void addCallMethod(String pattern, String methodName,
+                              int paramCount) {
+
+        addRule(pattern,
+                new CallMethodRule(methodName, paramCount));
+
+    }
+
+
+    /**
+     * Add an "call method" rule for the specified parameters.
+     * If <code>paramCount</code> is set to zero the rule will use
+     * the body of the matched element as the single argument of the
+     * method, unless <code>paramTypes</code> is null or empty, in this
+     * case the rule will call the specified method with no arguments.
+     *
+     * @param pattern Element matching pattern
+     * @param methodName Method name to be called
+     * @param paramCount Number of expected parameters (or zero
+     *  for a single parameter from the body of this element)
+     * @param paramTypes Set of Java class names for the types
+     *  of the expected parameters
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
+     *  for a <code>boolean</code> parameter)
+     * @see CallMethodRule
+     */
+    public void addCallMethod(String pattern, String methodName,
+                              int paramCount, String paramTypes[]) {
+
+        addRule(pattern,
+                new CallMethodRule(
+                                    methodName,
+                                    paramCount, 
+                                    paramTypes));
+
+    }
+
+
+    /**
+     * Add an "call method" rule for the specified parameters.
+     * If <code>paramCount</code> is set to zero the rule will use
+     * the body of the matched element as the single argument of the
+     * method, unless <code>paramTypes</code> is null or empty, in this
+     * case the rule will call the specified method with no arguments.
+     *
+     * @param pattern Element matching pattern
+     * @param methodName Method name to be called
+     * @param paramCount Number of expected parameters (or zero
+     *  for a single parameter from the body of this element)
+     * @param paramTypes The Java class names of the arguments
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
+     *  for a <code>boolean</code> parameter)
+     * @see CallMethodRule
+     */
+    public void addCallMethod(String pattern, String methodName,
+                              int paramCount, Class paramTypes[]) {
+
+        addRule(pattern,
+                new CallMethodRule(
+                                    methodName,
+                                    paramCount, 
+                                    paramTypes));
+
+    }
+
+
+    /**
+     * Add a "call parameter" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param paramIndex Zero-relative parameter index to set
+     *  (from the body of this element)
+     * @see CallParamRule
+     */
+    public void addCallParam(String pattern, int paramIndex) {
+
+        addRule(pattern,
+                new CallParamRule(paramIndex));
+
+    }
+
+
+    /**
+     * Add a "call parameter" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param paramIndex Zero-relative parameter index to set
+     *  (from the specified attribute)
+     * @param attributeName Attribute whose value is used as the
+     *  parameter value
+     * @see CallParamRule
+     */
+    public void addCallParam(String pattern, int paramIndex,
+                             String attributeName) {
+
+        addRule(pattern,
+                new CallParamRule(paramIndex, attributeName));
+
+    }
+
+
+    /**
+     * Add a "call parameter" rule.
+     * This will either take a parameter from the stack 
+     * or from the current element body text. 
+     *
+     * @param paramIndex The zero-relative parameter number
+     * @param fromStack Should the call parameter be taken from the top of the stack?
+     * @see CallParamRule
+     */    
+    public void addCallParam(String pattern, int paramIndex, boolean fromStack) {
+    
+        addRule(pattern,
+                new CallParamRule(paramIndex, fromStack));
+      
+    }
+
+    /**
+     * Add a "call parameter" rule that sets a parameter from the stack.
+     * This takes a parameter from the given position on the stack.
+     *
+     * @param paramIndex The zero-relative parameter number
+     * @param stackIndex set the call parameter to the stackIndex'th object down the stack,
+     * where 0 is the top of the stack, 1 the next element down and so on
+     * @see CallMethodRule
+     */    
+    public void addCallParam(String pattern, int paramIndex, int stackIndex) {
+    
+        addRule(pattern,
+                new CallParamRule(paramIndex, stackIndex));
+      
+    }
+    
+    /**
+     * Add a "call parameter" rule that sets a parameter from the current 
+     * <code>Digester</code> matching path.
+     * This is sometimes useful when using rules that support wildcards.
+     *
+     * @param pattern the pattern that this rule should match
+     * @param paramIndex The zero-relative parameter number
+     * @see CallMethodRule
+     */
+    public void addCallParamPath(String pattern,int paramIndex) {
+        addRule(pattern, new PathCallParamRule(paramIndex));
+    }
+    
+    /**
+     * Add a "call parameter" rule that sets a parameter from a 
+     * caller-provided object. This can be used to pass constants such as
+     * strings to methods; it can also be used to pass mutable objects,
+     * providing ways for objects to do things like "register" themselves
+     * with some shared object.
+     * <p>
+     * Note that when attempting to locate a matching method to invoke,
+     * the true type of the paramObj is used, so that despite the paramObj
+     * being passed in here as type Object, the target method can declare
+     * its parameters as being the true type of the object (or some ancestor
+     * type, according to the usual type-conversion rules).
+     *
+     * @param paramIndex The zero-relative parameter number
+     * @param paramObj Any arbitrary object to be passed to the target
+     * method.
+     * @see CallMethodRule
+     *
+     * @since 1.6
+     */    
+    public void addObjectParam(String pattern, int paramIndex, 
+                               Object paramObj) {
+    
+        addRule(pattern,
+                new ObjectParamRule(paramIndex, paramObj));
+      
+    }
+    
+    /**
+     * Add a "factory create" rule for the specified parameters.
+     * Exceptions thrown during the object creation process will be propagated.
+     *
+     * @param pattern Element matching pattern
+     * @param className Java class name of the object creation factory class
+     * @see FactoryCreateRule
+     */
+    public void addFactoryCreate(String pattern, String className) {
+
+        addFactoryCreate(pattern, className, false);
+
+    }
+
+
+    /**
+     * Add a "factory create" rule for the specified parameters.
+     * Exceptions thrown during the object creation process will be propagated.
+     *
+     * @param pattern Element matching pattern
+     * @param clazz Java class of the object creation factory class
+     * @see FactoryCreateRule
+     */
+    public void addFactoryCreate(String pattern, Class clazz) {
+
+        addFactoryCreate(pattern, clazz, false);
+
+    }
+
+
+    /**
+     * Add a "factory create" rule for the specified parameters.
+     * Exceptions thrown during the object creation process will be propagated.
+     *
+     * @param pattern Element matching pattern
+     * @param className Java class name of the object creation factory class
+     * @param attributeName Attribute name which, if present, overrides the
+     *  value specified by <code>className</code>
+     * @see FactoryCreateRule
+     */
+    public void addFactoryCreate(String pattern, String className,
+                                 String attributeName) {
+
+        addFactoryCreate(pattern, className, attributeName, false);
+
+    }
+
+
+    /**
+     * Add a "factory create" rule for the specified parameters.
+     * Exceptions thrown during the object creation process will be propagated.
+     *
+     * @param pattern Element matching pattern
+     * @param clazz Java class of the object creation factory class
+     * @param attributeName Attribute name which, if present, overrides the
+     *  value specified by <code>className</code>
+     * @see FactoryCreateRule
+     */
+    public void addFactoryCreate(String pattern, Class clazz,
+                                 String attributeName) {
+
+        addFactoryCreate(pattern, clazz, attributeName, false);
+
+    }
+
+
+    /**
+     * Add a "factory create" rule for the specified parameters.
+     * Exceptions thrown during the object creation process will be propagated.
+     *
+     * @param pattern Element matching pattern
+     * @param creationFactory Previously instantiated ObjectCreationFactory
+     *  to be utilized
+     * @see FactoryCreateRule
+     */
+    public void addFactoryCreate(String pattern,
+                                 ObjectCreationFactory creationFactory) {
+
+        addFactoryCreate(pattern, creationFactory, false);
+
+    }
+
+    /**
+     * Add a "factory create" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param className Java class name of the object creation factory class
+     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
+     * object creation will be ignored.
+     * @see FactoryCreateRule
+     */
+    public void addFactoryCreate(
+                                    String pattern, 
+                                    String className,
+                                    boolean ignoreCreateExceptions) {
+
+        addRule(
+                pattern,
+                new FactoryCreateRule(className, ignoreCreateExceptions));
+
+    }
+
+
+    /**
+     * Add a "factory create" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param clazz Java class of the object creation factory class
+     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
+     * object creation will be ignored.
+     * @see FactoryCreateRule
+     */
+    public void addFactoryCreate(
+                                    String pattern, 
+                                    Class clazz,
+                                    boolean ignoreCreateExceptions) {
+
+        addRule(
+                pattern,
+                new FactoryCreateRule(clazz, ignoreCreateExceptions));
+
+    }
+
+
+    /**
+     * Add a "factory create" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param className Java class name of the object creation factory class
+     * @param attributeName Attribute name which, if present, overrides the
+     *  value specified by <code>className</code>
+     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
+     * object creation will be ignored.
+     * @see FactoryCreateRule
+     */
+    public void addFactoryCreate(
+                                String pattern, 
+                                String className,
+                                String attributeName,
+                                boolean ignoreCreateExceptions) {
+
+        addRule(
+                pattern,
+                new FactoryCreateRule(className, attributeName, ignoreCreateExceptions));
+
+    }
+
+
+    /**
+     * Add a "factory create" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param clazz Java class of the object creation factory class
+     * @param attributeName Attribute name which, if present, overrides the
+     *  value specified by <code>className</code>
+     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
+     * object creation will be ignored.
+     * @see FactoryCreateRule
+     */
+    public void addFactoryCreate(
+                                    String pattern, 
+                                    Class clazz,
+                                    String attributeName,
+                                    boolean ignoreCreateExceptions) {
+
+        addRule(
+                pattern,
+                new FactoryCreateRule(clazz, attributeName, ignoreCreateExceptions));
+
+    }
+
+
+    /**
+     * Add a "factory create" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param creationFactory Previously instantiated ObjectCreationFactory
+     *  to be utilized
+     * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during
+     * object creation will be ignored.
+     * @see FactoryCreateRule
+     */
+    public void addFactoryCreate(String pattern,
+                                 ObjectCreationFactory creationFactory,
+                                 boolean ignoreCreateExceptions) {
+
+        creationFactory.setDigester(this);
+        addRule(pattern,
+                new FactoryCreateRule(creationFactory, ignoreCreateExceptions));
+
+    }
+
+    /**
+     * Add an "object create" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param className Java class name to be created
+     * @see ObjectCreateRule
+     */
+    public void addObjectCreate(String pattern, String className) {
+
+        addRule(pattern,
+                new ObjectCreateRule(className));
+
+    }
+
+
+    /**
+     * Add an "object create" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param clazz Java class to be created
+     * @see ObjectCreateRule
+     */
+    public void addObjectCreate(String pattern, Class clazz) {
+
+        addRule(pattern,
+                new ObjectCreateRule(clazz));
+
+    }
+
+
+    /**
+     * Add an "object create" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param className Default Java class name to be created
+     * @param attributeName Attribute name that optionally overrides
+     *  the default Java class name to be created
+     * @see ObjectCreateRule
+     */
+    public void addObjectCreate(String pattern, String className,
+                                String attributeName) {
+
+        addRule(pattern,
+                new ObjectCreateRule(className, attributeName));
+
+    }
+
+
+    /**
+     * Add an "object create" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param attributeName Attribute name that optionally overrides
+     * @param clazz Default Java class to be created
+     *  the default Java class name to be created
+     * @see ObjectCreateRule
+     */
+    public void addObjectCreate(String pattern,
+                                String attributeName,
+                                Class clazz) {
+
+        addRule(pattern,
+                new ObjectCreateRule(attributeName, clazz));
+
+    }
+
+    /**
+     * Add a "set next" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param methodName Method name to call on the parent element
+     * @see SetNextRule
+     */
+    public void addSetNext(String pattern, String methodName) {
+
+        addRule(pattern,
+                new SetNextRule(methodName));
+
+    }
+
+
+    /**
+     * Add a "set next" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param methodName Method name to call on the parent element
+     * @param paramType Java class name of the expected parameter type
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
+     *  for a <code>boolean</code> parameter)
+     * @see SetNextRule
+     */
+    public void addSetNext(String pattern, String methodName,
+                           String paramType) {
+
+        addRule(pattern,
+                new SetNextRule(methodName, paramType));
+
+    }
+
+
+    /**
+     * Add {@link SetRootRule} with the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param methodName Method name to call on the root object
+     * @see SetRootRule
+     */
+    public void addSetRoot(String pattern, String methodName) {
+
+        addRule(pattern,
+                new SetRootRule(methodName));
+
+    }
+
+
+    /**
+     * Add {@link SetRootRule} with the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param methodName Method name to call on the root object
+     * @param paramType Java class name of the expected parameter type
+     * @see SetRootRule
+     */
+    public void addSetRoot(String pattern, String methodName,
+                           String paramType) {
+
+        addRule(pattern,
+                new SetRootRule(methodName, paramType));
+
+    }
+
+    /**
+     * Add a "set properties" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @see SetPropertiesRule
+     */
+    public void addSetProperties(String pattern) {
+
+        addRule(pattern,
+                new SetPropertiesRule());
+
+    }
+
+    /**
+     * Add a "set properties" rule with a single overridden parameter.
+     * See {@link SetPropertiesRule#SetPropertiesRule(String attributeName, String propertyName)}
+     *
+     * @param pattern Element matching pattern
+     * @param attributeName map this attribute
+     * @param propertyName to this property
+     * @see SetPropertiesRule
+     */
+    public void addSetProperties(
+                                String pattern, 
+                                String attributeName,
+                                String propertyName) {
+
+        addRule(pattern,
+                new SetPropertiesRule(attributeName, propertyName));
+
+    }
+
+    /**
+     * Add a "set properties" rule with overridden parameters.
+     * See {@link SetPropertiesRule#SetPropertiesRule(String [] attributeNames, String [] propertyNames)}
+     *
+     * @param pattern Element matching pattern
+     * @param attributeNames names of attributes with custom mappings
+     * @param propertyNames property names these attributes map to
+     * @see SetPropertiesRule
+     */
+    public void addSetProperties(
+                                String pattern, 
+                                String [] attributeNames,
+                                String [] propertyNames) {
+
+        addRule(pattern,
+                new SetPropertiesRule(attributeNames, propertyNames));
+
+    }
+
+
+    /**
+     * Add a "set property" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param name Attribute name containing the property name to be set
+     * @param value Attribute name containing the property value to set
+     * @see SetPropertyRule
+     */
+    public void addSetProperty(String pattern, String name, String value) {
+
+        addRule(pattern,
+                new SetPropertyRule(name, value));
+
+    }
+
+
+    /**
+     * Add a "set top" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param methodName Method name to call on the parent element
+     * @see SetTopRule
+     */
+    public void addSetTop(String pattern, String methodName) {
+
+        addRule(pattern,
+                new SetTopRule(methodName));
+
+    }
+
+
+    /**
+     * Add a "set top" rule for the specified parameters.
+     *
+     * @param pattern Element matching pattern
+     * @param methodName Method name to call on the parent element
+     * @param paramType Java class name of the expected parameter type
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
+     *  for a <code>boolean</code> parameter)
+     * @see SetTopRule
+     */
+    public void addSetTop(String pattern, String methodName,
+                          String paramType) {
+
+        addRule(pattern,
+                new SetTopRule(methodName, paramType));
+
+    }
+
+
+    // --------------------------------------------------- Object Stack Methods
+
+
+    /**
+     * Clear the current contents of the object stack.
+     * <p>
+     * Calling this method <i>might</i> allow another document of the same type
+     * to be correctly parsed. However this method was not intended for this 
+     * purpose. In general, a separate Digester object should be created for
+     * each document to be parsed.
+     */
+    public void clear() {
+
+        match = "";
+        bodyTexts.clear();
+        params.clear();
+        publicId = null;
+        stack.clear();
+
+    }
+
+    
+    public void reset() {
+        root = null;
+    }
+
+
+    /**
+     * Return the top object on the stack without removing it.  If there are
+     * no objects on the stack, return <code>null</code>.
+     */
+    public Object peek() {
+
+        try {
+            return (stack.peek());
+        } catch (EmptyStackException e) {
+            log.warn("Empty stack (returning null)");
+            return (null);
+        }
+
+    }
+
+
+    /**
+     * Return the n'th object down the stack, where 0 is the top element
+     * and [getCount()-1] is the bottom element.  If the specified index
+     * is out of range, return <code>null</code>.
+     *
+     * @param n Index of the desired element, where 0 is the top of the stack,
+     *  1 is the next element down, and so on.
+     */
+    public Object peek(int n) {
+
+        try {
+            return (stack.peek(n));
+        } catch (EmptyStackException e) {
+            log.warn("Empty stack (returning null)");
+            return (null);
+        }
+
+    }
+
+
+    /**
+     * Pop the top object off of the stack, and return it.  If there are
+     * no objects on the stack, return <code>null</code>.
+     */
+    public Object pop() {
+
+        try {
+            return (stack.pop());
+        } catch (EmptyStackException e) {
+            log.warn("Empty stack (returning null)");
+            return (null);
+        }
+
+    }
+
+
+    /**
+     * Push a new object onto the top of the object stack.
+     *
+     * @param object The new object
+     */
+    public void push(Object object) {
+
+        if (stack.size() == 0) {
+            root = object;
+        }
+        stack.push(object);
+
+    }
+
+    /**
+     * Pushes the given object onto the stack with the given name.
+     * If no stack already exists with the given name then one will be created.
+     * 
+     * @param stackName the name of the stack onto which the object should be pushed
+     * @param value the Object to be pushed onto the named stack.
+     *
+     * @since 1.6
+     */
+    public void push(String stackName, Object value) {
+        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
+        if (namedStack == null) {
+            namedStack = new ArrayStack();
+            stacksByName.put(stackName, namedStack);
+        }
+        namedStack.push(value);
+    }
+
+    /**
+     * <p>Pops (gets and removes) the top object from the stack with the given name.</p>
+     *
+     * <p><strong>Note:</strong> a stack is considered empty
+     * if no objects have been pushed onto it yet.</p>
+     * 
+     * @param stackName the name of the stack from which the top value is to be popped
+     * @return the top <code>Object</code> on the stack or or null if the stack is either 
+     * empty or has not been created yet
+     * @throws EmptyStackException if the named stack is empty
+     *
+     * @since 1.6
+     */
+    public Object pop(String stackName) {
+        Object result = null;
+        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
+        if (namedStack == null) {
+            if (log.isDebugEnabled()) {
+                log.debug("Stack '" + stackName + "' is empty");
+            }
+            throw new EmptyStackException();
+            
+        } else {
+        
+            result = namedStack.pop();
+        }
+        return result;
+    }
+    
+    /**
+     * <p>Gets the top object from the stack with the given name.
+     * This method does not remove the object from the stack.
+     * </p>
+     * <p><strong>Note:</strong> a stack is considered empty
+     * if no objects have been pushed onto it yet.</p>
+     *
+     * @param stackName the name of the stack to be peeked
+     * @return the top <code>Object</code> on the stack or null if the stack is either 
+     * empty or has not been created yet
+     * @throws EmptyStackException if the named stack is empty 
+     *
+     * @since 1.6
+     */
+    public Object peek(String stackName) {
+        Object result = null;
+        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
+        if (namedStack == null ) {
+            if (log.isDebugEnabled()) {
+                log.debug("Stack '" + stackName + "' is empty");
+            }        
+            throw new EmptyStackException();
+        
+        } else {
+        
+            result = namedStack.peek();
+        }
+        return result;
+    }
+
+    /**
+     * <p>Is the stack with the given name empty?</p>
+     * <p><strong>Note:</strong> a stack is considered empty
+     * if no objects have been pushed onto it yet.</p>
+     * @param stackName the name of the stack whose emptiness 
+     * should be evaluated
+     * @return true if the given stack if empty 
+     *
+     * @since 1.6
+     */
+    public boolean isEmpty(String stackName) {
+        boolean result = true;
+        ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName);
+        if (namedStack != null ) {
+            result = namedStack.isEmpty();
+        }
+        return result;
+    }
+    
+    /**
+     * When the Digester is being used as a SAXContentHandler, 
+     * this method allows you to access the root object that has been
+     * created after parsing.
+     * 
+     * @return the root object that has been created after parsing
+     *  or null if the digester has not parsed any XML yet.
+     */
+    public Object getRoot() {
+        return root;
+    }
+    
+
+    // ------------------------------------------------ Parameter Stack Methods
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * <p>
+     * Provide a hook for lazy configuration of this <code>Digester</code>
+     * instance.  The default implementation does nothing, but subclasses
+     * can override as needed.
+     * </p>
+     *
+     * <p>
+     * <strong>Note</strong> This method may be called more than once.
+     * Once only initialization code should be placed in {@link #initialize}
+     * or the code should take responsibility by checking and setting the 
+     * {@link #configured} flag.
+     * </p>
+     */
+    protected void configure() {
+
+        // Do not configure more than once
+        if (configured) {
+            return;
+        }
+
+        // Perform lazy configuration as needed
+        initialize(); // call hook method for subclasses that want to be initialized once only
+        // Nothing else required by default
+
+        // Set the configuration flag to avoid repeating
+        configured = true;
+
+    }
+    
+    /**
+     * <p>
+     * Provides a hook for lazy initialization of this <code>Digester</code>
+     * instance.  
+     * The default implementation does nothing, but subclasses
+     * can override as needed.
+     * Digester (by default) only calls this method once.
+     * </p>
+     *
+     * <p>
+     * <strong>Note</strong> This method will be called by {@link #configure} 
+     * only when the {@link #configured} flag is false. 
+     * Subclasses that override <code>configure</code> or who set <code>configured</code>
+     * may find that this method may be called more than once.
+     * </p>
+     *
+     * @since 1.6
+     */
+    protected void initialize() {
+
+        // Perform lazy initialization as needed
+        ; // Nothing required by default
+
+    }    
+
+    // -------------------------------------------------------- Package Methods
+
+
+    /**
+     * Return the set of DTD URL registrations, keyed by public identifier.
+     */
+    Map getRegistrations() {
+
+        return (entityValidator);
+
+    }
+
+
+    /**
+     * Return the set of rules that apply to the specified match position.
+     * The selected rules are those that match exactly, or those rules
+     * that specify a suffix match and the tail of the rule matches the
+     * current match position.  Exact matches have precedence over
+     * suffix matches, then (among suffix matches) the longest match
+     * is preferred.
+     *
+     * @param match The current match position
+     *
+     * @deprecated Call <code>match()</code> on the <code>Rules</code>
+     *  implementation returned by <code>getRules()</code>
+     */
+    List getRules(String match) {
+
+        return (getRules().match(match));
+
+    }
+
+
+    /**
+     * <p>Return the top object on the parameters stack without removing it.  If there are
+     * no objects on the stack, return <code>null</code>.</p>
+     *
+     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters. 
+     * See {@link #params}.</p>
+     */
+    public Object peekParams() {
+
+        try {
+            return (params.peek());
+        } catch (EmptyStackException e) {
+            log.warn("Empty stack (returning null)");
+            return (null);
+        }
+
+    }
+
+
+    /**
+     * <p>Return the n'th object down the parameters stack, where 0 is the top element
+     * and [getCount()-1] is the bottom element.  If the specified index
+     * is out of range, return <code>null</code>.</p>
+     *
+     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters. 
+     * See {@link #params}.</p>
+     *
+     * @param n Index of the desired element, where 0 is the top of the stack,
+     *  1 is the next element down, and so on.
+     */
+    public Object peekParams(int n) {
+
+        try {
+            return (params.peek(n));
+        } catch (EmptyStackException e) {
+            log.warn("Empty stack (returning null)");
+            return (null);
+        }
+
+    }
+
+
+    /**
+     * <p>Pop the top object off of the parameters stack, and return it.  If there are
+     * no objects on the stack, return <code>null</code>.</p>
+     *
+     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters. 
+     * See {@link #params}.</p>
+     */
+    public Object popParams() {
+
+        try {
+            if (log.isTraceEnabled()) {
+                log.trace("Popping params");
+            }
+            return (params.pop());
+        } catch (EmptyStackException e) {
+            log.warn("Empty stack (returning null)");
+            return (null);
+        }
+
+    }
+
+
+    /**
+     * <p>Push a new object onto the top of the parameters stack.</p>
+     *
+     * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters. 
+     * See {@link #params}.</p>
+     *
+     * @param object The new object
+     */
+    public void pushParams(Object object) {
+        if (log.isTraceEnabled()) {
+            log.trace("Pushing params");
+        }
+        params.push(object);
+
+    }
+
+    /**
+     * Create a SAX exception which also understands about the location in
+     * the digester file where the exception occurs
+     *
+     * @return the new exception
+     */
+    public SAXException createSAXException(String message, Exception e) {
+        if ((e != null) &&
+            (e instanceof InvocationTargetException)) {
+            Throwable t = ((InvocationTargetException) e).getTargetException();
+            if ((t != null) && (t instanceof Exception)) {
+                e = (Exception) t;
+            }
+        }
+        if (locator != null) {
+            String error = "Error at (" + locator.getLineNumber() + ", " +
+                    locator.getColumnNumber() + ": " + message;
+            if (e != null) {
+                return new SAXParseException(error, locator, e);
+            } else {
+                return new SAXParseException(error, locator);
+            }
+        }
+        log.error("No Locator!");
+        if (e != null) {
+            return new SAXException(message, e);
+        } else {
+            return new SAXException(message);
+        }
+    }
+
+    /**
+     * Create a SAX exception which also understands about the location in
+     * the digester file where the exception occurs
+     *
+     * @return the new exception
+     */
+    public SAXException createSAXException(Exception e) {
+        if (e instanceof InvocationTargetException) {
+            Throwable t = ((InvocationTargetException) e).getTargetException();
+            if ((t != null) && (t instanceof Exception)) {
+                e = (Exception) t;
+            }
+        }
+        return createSAXException(e.getMessage(), e);
+    }
+
+    /**
+     * Create a SAX exception which also understands about the location in
+     * the digester file where the exception occurs
+     *
+     * @return the new exception
+     */
+    public SAXException createSAXException(String message) {
+        return createSAXException(message, null);
+    }
+    
+
+    // ------------------------------------------------------- Private Methods
+
+
+   /**
+     * Returns an attributes list which contains all the attributes
+     * passed in, with any text of form "${xxx}" in an attribute value
+     * replaced by the appropriate value from the system property.
+     */
+    private Attributes updateAttributes(Attributes list) {
+
+        if (list.getLength() == 0) {
+            return list;
+        }
+        
+        AttributesImpl newAttrs = new AttributesImpl(list);
+        int nAttributes = newAttrs.getLength();
+        for (int i = 0; i < nAttributes; ++i) {
+            String value = newAttrs.getValue(i);
+            try {
+                String newValue = 
+                    IntrospectionUtils.replaceProperties(value, null, source);
+                if (value != newValue) {
+                    newAttrs.setValue(i, newValue);
+                }
+            }
+            catch (Exception e) {
+                // ignore - let the attribute have its original value
+            }
+        }
+
+        return newAttrs;
+
+    }
+
+
+    /**
+     * Return a new StringBuffer containing the same contents as the
+     * input buffer, except that data of form ${varname} have been
+     * replaced by the value of that var as defined in the system property.
+     */
+    private StringBuffer updateBodyText(StringBuffer bodyText) {
+        String in = bodyText.toString();
+        String out;
+        try {
+            out = IntrospectionUtils.replaceProperties(in, null, source);
+        } catch(Exception e) {
+            return bodyText; // return unchanged data
+        }
+
+        if (out == in)  {
+            // No substitutions required. Don't waste memory creating
+            // a new buffer
+            return bodyText;
+        } else {
+            return new StringBuffer(out);
+        }
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/ExtendedBaseRules.java b/connectors/util/java/org/apache/tomcat/util/digester/ExtendedBaseRules.java
new file mode 100644
index 0000000..50199b5
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/ExtendedBaseRules.java
@@ -0,0 +1,446 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * <p>Extension of {@link RulesBase} for complex schema.</p>
+ *
+ * <p>This is an extension of the basic pattern matching scheme
+ * intended to improve support for mapping complex xml-schema.
+ * It is intended to be a minimal extension of the standard rules
+ * big enough to support complex schema but without the full generality
+ * offered by more exotic matching pattern rules.</p>
+ *
+ * <h4>When should you use this rather than the original?</h4>
+ *
+ * <p>These rules are complex and slower but offer more functionality.
+ * The <code>RulesBase</code> matching set allows interaction between patterns.
+ * This allows sophisticated matching schema to be created
+ * but it also means that it can be hard to create and debug mappings
+ * for complex schema.
+ * This extension introduces <em>universal</em> versions of these patterns
+ * that always act independently.</p>
+ *
+ * <p>Another three kinds of matching pattern are also introduced.
+ * The parent matchs allow common method to be easily called for children.
+ * The wildcard match allows rules to be specified for all elements.</p>
+ *
+ * <h4>The additional matching patterns:</h4>
+ *
+ * <ul>
+ * <li><em>Parent Match </em> - Will match child elements of a particular
+ *     kind of parent.  This is useful if a parent has a particular method
+ *     to call.
+ *     <ul>
+ *     <li><code>"a/b/c/?"</code> matches any child whose parent matches
+ *         <code>"a/b/c"</code>.  Exact parent rules take precedence over
+ *         standard wildcard tail endings.</li>
+ *     <li><code>"*&#47;a/b/c/?"</code> matches any child whose parent matches
+ *         "*&#47;a/b/c"</code>.  The longest matching still applies to parent
+ *         matches but the length excludes the '?', which effectively means
+ *         that standard wildcard matches with the same level of depth are
+ *         chosen in preference.</li>
+ *     </ul></li>
+ * <li><em>Ancester Match</em> - Will match elements who parentage includes 
+ * 	a particular sequence of elements.
+ * 	<ul>
+ *      <li><code>"a/b/*"</code> matches any element whose parentage path starts with
+ *	    'a' then 'b'. Exact parent and parent match rules take precedence. The longest
+ *           ancester match will take precedence.</li>
+ *	<li><code>"*&#47;a/b/*"</code> matches any elements whose parentage path contains
+ *           an element 'a' followed by an element 'b'. The longest matching still applies
+ *           but the length excludes the '*' at the end.</li>
+ *      </ul>
+ * </li>
+ * <li><em>Universal Wildcard Match</em> -  Any pattern prefixed with '!'
+ *     bypasses the longest matching rule.  Even if there is an exact match
+ *     or a longer wildcard match,  patterns prefixed by '!' will still be
+ *     tested to see if they match.  This can be used for example to specify
+ *     universal construction rules.
+ *     <ul>
+ *     <li>Pattern <code>"!*&#47;a/b"</code> matches whenever an 'b' element
+ *         is inside an 'a'.</li>
+ *     <li>Pattern <code>"!a/b/?"</code> matches any child of a parent
+ *         matching <code>"a/b"</code>.</li>
+ *     <li>Pattern <code>"!*&#47;a/b/?"</code> matches any child of a parent
+ *         matching <code>"!*&#47;a/b"</code></li>
+ *     <li>Pattern <code>"!a/b/*"</code> matches any element whose parentage path starts with
+ *	    "a" then "b".</li>
+ *     <li>Pattern <code>"!*&#47;a/b/*"</code> matches any elements whose parentage path contains
+ *          'a/b'</li>
+ *    </ul></li>
+ * <li><em>Wild Match</em>
+ *     <ul>
+ *     <li>Pattern <code>"*"</code> matches every pattern that isn't matched
+ *         by any other basic rule.</li>
+ *     <li>Pattern <code>"!*"</code> matches every pattern.</li>
+ *     </ul></li>
+ * </ul>
+ *
+ * <h4>Using The Extended Rules</h4>
+ *
+ * <p>The most important thing to remember
+ * when using the extended rules is that universal
+ * and non-universal patterns are completely independent.
+ * Universal patterns are never effected by the addition of new patterns
+ * or the removal of existing ones.
+ * Non-universal patterns are never effected
+ * by the addition of new <em>universal</em> patterns
+ * or the removal of existing <em>universal</em> patterns.
+ * As in the basic matching rules, non-universal (basic) patterns
+ * <strong>can</strong> be effected
+ * by the addition of new <em>non-universal</em> patterns
+ * or the removal of existing <em>non-universal</em> patterns.
+ * <p> This means that you can use universal patterns
+ * to build up the simple parts of your structure
+ * - for example defining universal creation and property setting rules.
+ * More sophisticated and complex mapping will require non-universal patterns
+ * and this might mean that some of the universal rules will need to be
+ * replaced by a series of
+ * special cases using non-universal rules.
+ * But by using universal rules as your backbone,
+ * these additions should not break your existing rules.</p>
+ */
+
+
+public class ExtendedBaseRules extends RulesBase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Counts the entry number for the rules.
+     */
+    private int counter = 0;
+
+
+    /**
+     * The decision algorithm used (unfortunately) doesn't preserve the entry
+     * order.
+     * This map is used by a comparator which orders the list of matches
+     * before it's returned.
+     * This map stores the entry number keyed by the rule.
+     */
+    private Map order = new HashMap();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Register a new Rule instance matching the specified pattern.
+     *
+     * @param pattern Nesting pattern to be matched for this Rule
+     * @param rule Rule instance to be registered
+     */
+    public void add(String pattern, Rule rule) {
+        super.add(pattern, rule);
+        counter++;
+        order.put(rule, new Integer(counter));
+    }
+
+
+    /**
+     * Return a List of all registered Rule instances that match the specified
+     * nesting pattern, or a zero-length List if there are no matches.  If more
+     * than one Rule instance matches, they <strong>must</strong> be returned
+     * in the order originally registered through the <code>add()</code>
+     * method.
+     *
+     * @param pattern Nesting pattern to be matched
+     */
+    public List match(String namespace, String pattern) {
+        // calculate the pattern of the parent
+        // (if the element has one)
+        String parentPattern = "";
+        int lastIndex = pattern.lastIndexOf('/');
+
+        boolean hasParent = true;
+        if (lastIndex == -1) {
+            // element has no parent
+            hasParent = false;
+
+        } else {
+            // calculate the pattern of the parent
+            parentPattern = pattern.substring(0, lastIndex);
+
+        }
+
+
+        // we keep the list of universal matches separate
+        List universalList = new ArrayList(counter);
+
+        // Universal all wildards ('!*')
+        // These are always matched so always add them
+        List tempList = (List) this.cache.get("!*");
+        if (tempList != null) {
+            universalList.addAll(tempList);
+        }
+
+        // Universal exact parent match
+        // need to get this now since only wildcards are considered later
+        tempList = (List) this.cache.get("!" + parentPattern + "/?");
+        if (tempList != null) {
+            universalList.addAll(tempList);
+        }
+
+
+        // base behaviour means that if we certain matches, we don't continue
+        // but we just have a single combined loop and so we have to set
+        // a variable
+        boolean ignoreBasicMatches = false;
+
+
+        // see if we have an exact basic pattern match
+        List rulesList = (List) this.cache.get(pattern);
+        if (rulesList != null) {
+            // we have a match!
+            // so ignore all basic matches from now on
+            ignoreBasicMatches = true;
+
+        } else {
+
+            // see if we have an exact child match
+            if (hasParent) {
+                // matching children takes preference
+                rulesList = (List) this.cache.get(parentPattern + "/?");
+                if (rulesList != null) {
+                    // we have a match!
+                    // so ignore all basic matches from now on
+                    ignoreBasicMatches = true;
+                    
+                } else {
+                    // we don't have a match yet - so try exact ancester
+                    //
+                    rulesList = findExactAncesterMatch(pattern);
+                    if (rulesList != null) {
+                        // we have a match!
+                        // so ignore all basic matches from now on
+                        ignoreBasicMatches = true;
+                    }
+                }
+            }
+        }
+
+
+        // OK - we're ready for the big loop!
+        // Unlike the basic rules case,
+        // we have to go through for all those universal rules in all cases.
+
+        // Find the longest key, ie more discriminant
+        String longKey = "";
+        int longKeyLength = 0;
+        
+        Iterator keys = this.cache.keySet().iterator();
+        while (keys.hasNext()) {
+            String key = (String) keys.next();
+
+            // find out if it's a univeral pattern
+            // set a flag
+            boolean isUniversal = key.startsWith("!");
+            if (isUniversal) {
+                // and find the underlying key
+                key = key.substring(1, key.length());
+            }
+
+                    
+            // don't need to check exact matches
+            boolean wildcardMatchStart = key.startsWith("*/");
+            boolean wildcardMatchEnd = key.endsWith("/*");
+            if (wildcardMatchStart || (isUniversal && wildcardMatchEnd)) {
+
+                boolean parentMatched = false;
+                boolean basicMatched = false;
+                boolean ancesterMatched = false;
+                
+                boolean parentMatchEnd = key.endsWith("/?");
+                if (parentMatchEnd) {
+                    // try for a parent match
+                    parentMatched = parentMatch(key, pattern, parentPattern);
+
+                } else if (wildcardMatchEnd) {
+                    // check for ancester match
+                    if (wildcardMatchStart) {
+                        String patternBody = key.substring(2, key.length() - 2);
+                        if (pattern.endsWith(patternBody)) {
+                            ancesterMatched = true;
+                        } else {
+                            ancesterMatched = (pattern.indexOf(patternBody + "/") > -1);
+                        }
+                    } else {
+                        String bodyPattern = key.substring(0, key.length() - 2);
+                        if (pattern.startsWith(bodyPattern))
+                        {
+                            if (pattern.length() == bodyPattern.length()) {
+                                // exact match
+                                ancesterMatched = true;
+                            } else {
+                                ancesterMatched = ( pattern.charAt(bodyPattern.length()) == '/' );
+                            }
+                        } else {
+                            ancesterMatched = false;  
+                        } 
+                    }               
+                } else {
+                    // try for a base match
+                    basicMatched = basicMatch(key, pattern);
+                }
+
+                if (parentMatched || basicMatched || ancesterMatched) {
+                    if (isUniversal) {
+                        // universal rules go straight in
+                        // (no longest matching rule)
+                        tempList = (List) this.cache.get("!" + key);
+                        if (tempList != null) {
+                            universalList.addAll(tempList);
+                        }
+
+                    } else {
+                        if (!ignoreBasicMatches) {
+                            // ensure that all parent matches are SHORTER
+                            // than rules with same level of matching.
+                            //
+                            // the calculations below don't work for universal
+                            // matching, but we don't care because in that case
+                            // this if-stmt is not entered.
+                            int keyLength = key.length();
+                            if (wildcardMatchStart) {
+                                --keyLength;
+                            }
+                            if (wildcardMatchEnd) {
+                                --keyLength;
+                            } else if (parentMatchEnd) {
+                                --keyLength;
+                            }
+
+                            if (keyLength > longKeyLength) {
+                                rulesList = (List) this.cache.get(key);
+                                longKey = key;
+                                longKeyLength = keyLength;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+
+        // '*' works in practice as a default matching
+        // (this is because anything is a deeper match!)
+        if (rulesList == null) {
+            rulesList = (List) this.cache.get("*");
+        }
+
+        // if we've matched a basic pattern, then add to the universal list
+        if (rulesList != null) {
+            universalList.addAll(rulesList);
+        }
+
+
+        // don't filter if namespace is null
+        if (namespace != null) {
+            // remove invalid namespaces
+            Iterator it = universalList.iterator();
+            while (it.hasNext()) {
+                Rule rule = (Rule) it.next();
+                String ns_uri = rule.getNamespaceURI();
+                if (ns_uri != null && !ns_uri.equals(namespace)) {
+                    it.remove();
+                }
+            }
+        }
+
+
+        // need to make sure that the collection is sort in the order
+        // of addition.  We use a custom comparator for this
+        Collections.sort(
+                universalList,
+                new Comparator() {
+
+                    public int compare(Object o1, Object o2) throws ClassCastException {
+                        // Get the entry order from the map
+                        Integer i1 = (Integer) order.get(o1);
+                        Integer i2 = (Integer) order.get(o2);
+
+                        // and use that to perform the comparison
+                        if (i1 == null) {
+                            if (i2 == null) {
+
+                                return 0;
+
+                            } else {
+
+                                return -1;
+
+                            }
+                        } else if (i2 == null) {
+                            return 1;
+                        }
+
+                        return (i1.intValue() - i2.intValue());
+                    }
+                });
+
+        return universalList;
+    }
+
+    /**
+     * Matching parent.
+     */
+    private boolean parentMatch(String key, String pattern, String parentPattern) {
+        return parentPattern.endsWith(key.substring(1, key.length() - 2));
+    }
+
+    /**
+     * Standard match.
+     * Matches the end of the pattern to the key.
+     */
+    private boolean basicMatch(String key, String pattern) {
+        return (pattern.equals(key.substring(2)) ||
+                pattern.endsWith(key.substring(1)));
+    }
+    
+    /**
+     * Finds an exact ancester match for given pattern
+     */
+    private List findExactAncesterMatch(String parentPattern) {
+        List matchingRules = null;
+        int lastIndex = parentPattern.length();
+        while (lastIndex-- > 0) {
+            lastIndex = parentPattern.lastIndexOf('/', lastIndex);
+            if (lastIndex > 0) {
+                matchingRules = (List) this.cache.get(parentPattern.substring(0, lastIndex) + "/*");
+                if (matchingRules != null) {
+                    return matchingRules;
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/FactoryCreateRule.java b/connectors/util/java/org/apache/tomcat/util/digester/FactoryCreateRule.java
new file mode 100644
index 0000000..9763068
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/FactoryCreateRule.java
@@ -0,0 +1,495 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+import org.xml.sax.Attributes;
+
+
+/**
+ * <p>Rule implementation that uses an {@link ObjectCreationFactory} to create
+ * a new object which it pushes onto the object stack.  When the element is
+ * complete, the object will be popped.</p>
+ *
+ * <p>This rule is intended in situations where the element's attributes are
+ * needed before the object can be created.  A common senario is for the
+ * ObjectCreationFactory implementation to use the attributes  as parameters
+ * in a call to either a factory method or to a non-empty constructor.
+ */
+
+public class FactoryCreateRule extends Rule {
+
+    // ----------------------------------------------------------- Fields
+    
+    /** Should exceptions thrown by the factory be ignored? */
+    private boolean ignoreCreateExceptions;
+    /** Stock to manage */
+    private ArrayStack exceptionIgnoredStack;
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a factory create rule that will use the specified
+     * class name to create an {@link ObjectCreationFactory} which will
+     * then be used to create an object and push it on the stack.
+     *
+     * @param digester The associated Digester
+     * @param className Java class name of the object creation factory class
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #FactoryCreateRule(String className)} instead.
+     */
+    public FactoryCreateRule(Digester digester, String className) {
+
+        this(className);
+
+    }
+
+
+    /**
+     * Construct a factory create rule that will use the specified
+     * class to create an {@link ObjectCreationFactory} which will
+     * then be used to create an object and push it on the stack.
+     *
+     * @param digester The associated Digester
+     * @param clazz Java class name of the object creation factory class
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #FactoryCreateRule(Class clazz)} instead.
+     */
+    public FactoryCreateRule(Digester digester, Class clazz) {
+
+        this(clazz);
+
+    }
+
+
+    /**
+     * Construct a factory create rule that will use the specified
+     * class name (possibly overridden by the specified attribute if present)
+     * to create an {@link ObjectCreationFactory}, which will then be used
+     * to instantiate an object instance and push it onto the stack.
+     *
+     * @param digester The associated Digester
+     * @param className Default Java class name of the factory class
+     * @param attributeName Attribute name which, if present, contains an
+     *  override of the class name of the object creation factory to create.
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #FactoryCreateRule(String className, String attributeName)} instead.
+     */
+    public FactoryCreateRule(Digester digester,
+                             String className, String attributeName) {
+
+        this(className, attributeName);
+
+    }
+
+
+    /**
+     * Construct a factory create rule that will use the specified
+     * class (possibly overridden by the specified attribute if present)
+     * to create an {@link ObjectCreationFactory}, which will then be used
+     * to instantiate an object instance and push it onto the stack.
+     *
+     * @param digester The associated Digester
+     * @param clazz Default Java class name of the factory class
+     * @param attributeName Attribute name which, if present, contains an
+     *  override of the class name of the object creation factory to create.
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #FactoryCreateRule(Class clazz, String attributeName)} instead.
+     */
+    public FactoryCreateRule(Digester digester,
+                             Class clazz, String attributeName) {
+
+        this(clazz, attributeName);
+
+    }
+
+
+    /**
+     * Construct a factory create rule using the given, already instantiated,
+     * {@link ObjectCreationFactory}.
+     *
+     * @param digester The associated Digester
+     * @param creationFactory called on to create the object.
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #FactoryCreateRule(ObjectCreationFactory creationFactory)} instead.
+     */
+    public FactoryCreateRule(Digester digester,
+                             ObjectCreationFactory creationFactory) {
+
+        this(creationFactory);
+
+    }    
+
+    /**
+     * <p>Construct a factory create rule that will use the specified
+     * class name to create an {@link ObjectCreationFactory} which will
+     * then be used to create an object and push it on the stack.</p>
+     *
+     * <p>Exceptions thrown during the object creation process will be propagated.</p>
+     *
+     * @param className Java class name of the object creation factory class
+     */
+    public FactoryCreateRule(String className) {
+
+        this(className, false);
+
+    }
+
+
+    /**
+     * <p>Construct a factory create rule that will use the specified
+     * class to create an {@link ObjectCreationFactory} which will
+     * then be used to create an object and push it on the stack.</p>
+     *
+     * <p>Exceptions thrown during the object creation process will be propagated.</p>
+     *
+     * @param clazz Java class name of the object creation factory class
+     */
+    public FactoryCreateRule(Class clazz) {
+
+        this(clazz, false);
+
+    }
+
+
+    /**
+     * <p>Construct a factory create rule that will use the specified
+     * class name (possibly overridden by the specified attribute if present)
+     * to create an {@link ObjectCreationFactory}, which will then be used
+     * to instantiate an object instance and push it onto the stack.</p>
+     *
+     * <p>Exceptions thrown during the object creation process will be propagated.</p>
+     *
+     * @param className Default Java class name of the factory class
+     * @param attributeName Attribute name which, if present, contains an
+     *  override of the class name of the object creation factory to create.
+     */
+    public FactoryCreateRule(String className, String attributeName) {
+
+        this(className, attributeName, false);
+
+    }
+
+
+    /**
+     * <p>Construct a factory create rule that will use the specified
+     * class (possibly overridden by the specified attribute if present)
+     * to create an {@link ObjectCreationFactory}, which will then be used
+     * to instantiate an object instance and push it onto the stack.</p>
+     *
+     * <p>Exceptions thrown during the object creation process will be propagated.</p>
+     *
+     * @param clazz Default Java class name of the factory class
+     * @param attributeName Attribute name which, if present, contains an
+     *  override of the class name of the object creation factory to create.
+     */
+    public FactoryCreateRule(Class clazz, String attributeName) {
+
+        this(clazz, attributeName, false);
+
+    }
+
+
+    /**
+     * <p>Construct a factory create rule using the given, already instantiated,
+     * {@link ObjectCreationFactory}.</p>
+     *
+     * <p>Exceptions thrown during the object creation process will be propagated.</p>
+     *
+     * @param creationFactory called on to create the object.
+     */
+    public FactoryCreateRule(ObjectCreationFactory creationFactory) {
+
+        this(creationFactory, false);
+
+    }
+    
+    /**
+     * Construct a factory create rule that will use the specified
+     * class name to create an {@link ObjectCreationFactory} which will
+     * then be used to create an object and push it on the stack.
+     *
+     * @param className Java class name of the object creation factory class
+     * @param ignoreCreateExceptions if true, exceptions thrown by the object
+     *  creation factory
+     * will be ignored.
+     */
+    public FactoryCreateRule(String className, boolean ignoreCreateExceptions) {
+
+        this(className, null, ignoreCreateExceptions);
+
+    }
+
+
+    /**
+     * Construct a factory create rule that will use the specified
+     * class to create an {@link ObjectCreationFactory} which will
+     * then be used to create an object and push it on the stack.
+     *
+     * @param clazz Java class name of the object creation factory class
+     * @param ignoreCreateExceptions if true, exceptions thrown by the
+     *  object creation factory
+     * will be ignored.
+     */
+    public FactoryCreateRule(Class clazz, boolean ignoreCreateExceptions) {
+
+        this(clazz, null, ignoreCreateExceptions);
+
+    }
+
+
+    /**
+     * Construct a factory create rule that will use the specified
+     * class name (possibly overridden by the specified attribute if present)
+     * to create an {@link ObjectCreationFactory}, which will then be used
+     * to instantiate an object instance and push it onto the stack.
+     *
+     * @param className Default Java class name of the factory class
+     * @param attributeName Attribute name which, if present, contains an
+     *  override of the class name of the object creation factory to create.
+     * @param ignoreCreateExceptions if true, exceptions thrown by the object
+     *  creation factory will be ignored.
+     */
+    public FactoryCreateRule(
+                                String className, 
+                                String attributeName,
+                                boolean ignoreCreateExceptions) {
+
+        this.className = className;
+        this.attributeName = attributeName;
+        this.ignoreCreateExceptions = ignoreCreateExceptions;
+
+    }
+
+
+    /**
+     * Construct a factory create rule that will use the specified
+     * class (possibly overridden by the specified attribute if present)
+     * to create an {@link ObjectCreationFactory}, which will then be used
+     * to instantiate an object instance and push it onto the stack.
+     *
+     * @param clazz Default Java class name of the factory class
+     * @param attributeName Attribute name which, if present, contains an
+     *  override of the class name of the object creation factory to create.
+     * @param ignoreCreateExceptions if true, exceptions thrown by the object
+     *  creation factory will be ignored.
+     */
+    public FactoryCreateRule(
+                                Class clazz, 
+                                String attributeName,
+                                boolean ignoreCreateExceptions) {
+
+        this(clazz.getName(), attributeName, ignoreCreateExceptions);
+
+    }
+
+
+    /**
+     * Construct a factory create rule using the given, already instantiated,
+     * {@link ObjectCreationFactory}.
+     *
+     * @param creationFactory called on to create the object.
+     * @param ignoreCreateExceptions if true, exceptions thrown by the object
+     *  creation factory will be ignored.
+     */
+    public FactoryCreateRule(
+                            ObjectCreationFactory creationFactory, 
+                            boolean ignoreCreateExceptions) {
+
+        this.creationFactory = creationFactory;
+        this.ignoreCreateExceptions = ignoreCreateExceptions;
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The attribute containing an override class name if it is present.
+     */
+    protected String attributeName = null;
+
+
+    /**
+     * The Java class name of the ObjectCreationFactory to be created.
+     * This class must have a no-arguments constructor.
+     */
+    protected String className = null;
+
+
+    /**
+     * The object creation factory we will use to instantiate objects
+     * as required based on the attributes specified in the matched XML
+     * element.
+     */
+    protected ObjectCreationFactory creationFactory = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the beginning of this element.
+     *
+     * @param attributes The attribute list of this element
+     */
+    public void begin(String namespace, String name, Attributes attributes) throws Exception {
+        
+        if (ignoreCreateExceptions) {
+        
+            if (exceptionIgnoredStack == null) {
+                exceptionIgnoredStack = new ArrayStack();
+            }
+            
+            try {
+                Object instance = getFactory(attributes).createObject(attributes);
+                
+                if (digester.log.isDebugEnabled()) {
+                    digester.log.debug("[FactoryCreateRule]{" + digester.match +
+                            "} New " + instance.getClass().getName());
+                }
+                digester.push(instance);
+                exceptionIgnoredStack.push(Boolean.FALSE);
+                
+            } catch (Exception e) {
+                // log message and error
+                if (digester.log.isInfoEnabled()) {
+                    digester.log.info("[FactoryCreateRule] Create exception ignored: " +
+                        ((e.getMessage() == null) ? e.getClass().getName() : e.getMessage()));
+                    if (digester.log.isDebugEnabled()) {
+                        digester.log.debug("[FactoryCreateRule] Ignored exception:", e);
+                    }
+                }
+                exceptionIgnoredStack.push(Boolean.TRUE);
+            }
+            
+        } else {
+            Object instance = getFactory(attributes).createObject(attributes);
+            
+            if (digester.log.isDebugEnabled()) {
+                digester.log.debug("[FactoryCreateRule]{" + digester.match +
+                        "} New " + instance.getClass().getName());
+            }
+            digester.push(instance);
+        }
+    }
+
+
+    /**
+     * Process the end of this element.
+     */
+    public void end(String namespace, String name) throws Exception {
+        
+        // check if object was created 
+        // this only happens if an exception was thrown and we're ignoring them
+        if (	
+                ignoreCreateExceptions &&
+                exceptionIgnoredStack != null &&
+                !(exceptionIgnoredStack.empty())) {
+                
+            if (((Boolean) exceptionIgnoredStack.pop()).booleanValue()) {
+                // creation exception was ignored
+                // nothing was put onto the stack
+                if (digester.log.isTraceEnabled()) {
+                    digester.log.trace("[FactoryCreateRule] No creation so no push so no pop");
+                }
+                return;
+            }
+        } 
+
+        Object top = digester.pop();
+        if (digester.log.isDebugEnabled()) {
+            digester.log.debug("[FactoryCreateRule]{" + digester.match +
+                    "} Pop " + top.getClass().getName());
+        }
+
+    }
+
+
+    /**
+     * Clean up after parsing is complete.
+     */
+    public void finish() throws Exception {
+
+        if (attributeName != null) {
+            creationFactory = null;
+        }
+
+    }
+
+
+    /**
+     * Render a printable version of this Rule.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("FactoryCreateRule[");
+        sb.append("className=");
+        sb.append(className);
+        sb.append(", attributeName=");
+        sb.append(attributeName);
+        if (creationFactory != null) {
+            sb.append(", creationFactory=");
+            sb.append(creationFactory);
+        }
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return an instance of our associated object creation factory,
+     * creating one if necessary.
+     *
+     * @param attributes Attributes passed to our factory creation element
+     *
+     * @exception Exception if any error occurs
+     */
+    protected ObjectCreationFactory getFactory(Attributes attributes)
+            throws Exception {
+
+        if (creationFactory == null) {
+            String realClassName = className;
+            if (attributeName != null) {
+                String value = attributes.getValue(attributeName);
+                if (value != null) {
+                    realClassName = value;
+                }
+            }
+            if (digester.log.isDebugEnabled()) {
+                digester.log.debug("[FactoryCreateRule]{" + digester.match +
+                        "} New factory " + realClassName);
+            }
+            Class clazz = digester.getClassLoader().loadClass(realClassName);
+            creationFactory = (ObjectCreationFactory)
+                    clazz.newInstance();
+            creationFactory.setDigester(digester);
+        }
+        return (creationFactory);
+
+    }    
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/GenericParser.java b/connectors/util/java/org/apache/tomcat/util/digester/GenericParser.java
new file mode 100644
index 0000000..78659b7
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/GenericParser.java
@@ -0,0 +1,86 @@
+/* $Id$
+ *
+ * Copyright 2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+import java.util.Properties;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+
+/**
+ * Create a <code>SAXParser</code> configured to support XML Schema and DTD.
+ *
+ * @since 1.6
+ */
+
+public class GenericParser{
+
+    /**
+     * The Log to which all SAX event related logging calls will be made.
+     */
+    protected static Log log =
+        LogFactory.getLog("org.apache.commons.digester.Digester.sax");
+
+    /**
+     * The JAXP 1.2 property required to set up the schema location.
+     */
+    private static final String JAXP_SCHEMA_SOURCE =
+        "http://java.sun.com/xml/jaxp/properties/schemaSource";
+
+    /**
+     * The JAXP 1.2 property to set up the schemaLanguage used.
+     */
+    protected static String JAXP_SCHEMA_LANGUAGE =
+        "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
+
+    /**
+     * Create a <code>SAXParser</code> configured to support XML Scheman and DTD
+     * @param properties parser specific properties/features
+     * @return an XML Schema/DTD enabled <code>SAXParser</code>
+     */
+    public static SAXParser newSAXParser(Properties properties)
+            throws ParserConfigurationException, 
+                   SAXException,
+                   SAXNotRecognizedException{ 
+
+        SAXParserFactory factory = 
+                        (SAXParserFactory)properties.get("SAXParserFactory");
+        SAXParser parser = factory.newSAXParser();
+        String schemaLocation = (String)properties.get("schemaLocation");
+        String schemaLanguage = (String)properties.get("schemaLanguage");
+
+        try{
+            if (schemaLocation != null) {
+                parser.setProperty(JAXP_SCHEMA_LANGUAGE, schemaLanguage);
+                parser.setProperty(JAXP_SCHEMA_SOURCE, schemaLocation);
+            }
+        } catch (SAXNotRecognizedException e){
+            log.info(parser.getClass().getName() + ": "  
+                                        + e.getMessage() + " not supported."); 
+        }
+        return parser;
+    }
+
+}
\ No newline at end of file
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/NodeCreateRule.java b/connectors/util/java/org/apache/tomcat/util/digester/NodeCreateRule.java
new file mode 100644
index 0000000..fbc7520
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/NodeCreateRule.java
@@ -0,0 +1,428 @@
+/* $Id$
+ *
+ * Copyright 2002-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+
+/**
+ * A rule implementation that creates a DOM
+ * {@link org.w3c.dom.Node Node} containing the XML at the element that matched
+ * the rule. Two concrete types of nodes can be created by this rule:
+ * <ul>
+ *   <li>the default is to create an {@link org.w3c.dom.Element Element} node.
+ *   The created element will correspond to the element that matched the rule,
+ *   containing all XML content underneath that element.</li>
+ *   <li>alternatively, this rule can create nodes of type
+ *   {@link org.w3c.dom.DocumentFragment DocumentFragment}, which will contain
+ *   only the XML content under the element the rule was trigged on.</li>
+ * </ul>
+ * The created node will be normalized, meaning it will not contain text nodes 
+ * that only contain white space characters.
+ * 
+
+ * 
+ * <p>The created <code>Node</code> will be pushed on Digester's object stack
+ * when done. To use it in the context of another DOM
+ * {@link org.w3c.dom.Document Document}, it must be imported first, using the
+ * Document method
+ * {@link org.w3c.dom.Document#importNode(org.w3c.dom.Node, boolean) importNode()}.
+ * </p>
+ *
+ * <p><strong>Important Note:</strong> This is implemented by replacing the SAX
+ * {@link org.xml.sax.ContentHandler ContentHandler} in the parser used by 
+ * Digester, and resetting it when the matched element is closed. As a side 
+ * effect, rules that would match XML nodes under the element that matches 
+ * a <code>NodeCreateRule</code> will never be triggered by Digester, which 
+ * usually is the behavior one would expect.</p>
+ * 
+ * <p><strong>Note</strong> that the current implementation does not set the namespace prefixes
+ * in the exported nodes. The (usually more important) namespace URIs are set,
+ * of course.</p>
+ *
+ * @since Digester 1.4
+ */
+
+public class NodeCreateRule extends Rule {
+
+
+    // ---------------------------------------------------------- Inner Classes
+
+
+    /**
+     * The SAX content handler that does all the actual work of assembling the 
+     * DOM node tree from the SAX events.
+     */
+    private class NodeBuilder
+        extends DefaultHandler {
+
+
+        // ------------------------------------------------------- Constructors
+
+
+        /**
+         * Constructor.
+         * 
+         * <p>Stores the content handler currently used by Digester so it can 
+         * be reset when done, and initializes the DOM objects needed to 
+         * build the node.</p>
+         * 
+         * @param doc the document to use to create nodes
+         * @param root the root node
+         * @throws ParserConfigurationException if the DocumentBuilderFactory 
+         *   could not be instantiated
+         * @throws SAXException if the XMLReader could not be instantiated by 
+         *   Digester (should not happen)
+         */
+        public NodeBuilder(Document doc, Node root)
+            throws ParserConfigurationException, SAXException {
+
+            this.doc = doc;
+            this.root = root;
+            this.top = root;
+            
+            oldContentHandler = digester.getXMLReader().getContentHandler();
+
+        }
+
+
+        // ------------------------------------------------- Instance Variables
+
+
+        /**
+         * The content handler used by Digester before it was set to this 
+         * content handler.
+         */
+        protected ContentHandler oldContentHandler = null;
+
+
+        /**
+         * Depth of the current node, relative to the element where the content
+         * handler was put into action.
+         */
+        protected int depth = 0;
+
+
+        /**
+         * A DOM Document used to create the various Node instances.
+         */
+        protected Document doc = null;
+
+
+        /**
+         * The DOM node that will be pushed on Digester's stack.
+         */
+        protected Node root = null;
+
+
+        /**
+         * The current top DOM mode.
+         */
+        protected Node top = null;
+
+
+        // --------------------------------------------- ContentHandler Methods
+
+
+        /**
+         * Appends a {@link org.w3c.dom.Text Text} node to the current node.
+         * 
+         * @param ch the characters from the XML document
+         * @param start the start position in the array
+         * @param length the number of characters to read from the array
+         * @throws SAXException if the DOM implementation throws an exception
+         */
+        public void characters(char[] ch, int start, int length)
+            throws SAXException {
+
+            try {
+                String str = new String(ch, start, length);
+                if (str.trim().length() > 0) { 
+                    top.appendChild(doc.createTextNode(str));
+                }
+            } catch (DOMException e) {
+                throw new SAXException(e.getMessage());
+            }
+
+        }
+
+
+        /**
+         * Checks whether control needs to be returned to Digester.
+         * 
+         * @param namespaceURI the namespace URI
+         * @param localName the local name
+         * @param qName the qualified (prefixed) name
+         * @throws SAXException if the DOM implementation throws an exception
+         */
+        public void endElement(String namespaceURI, String localName,
+                               String qName)
+            throws SAXException {
+            
+            try {
+                if (depth == 0) {
+                    getDigester().getXMLReader().setContentHandler(
+                        oldContentHandler);
+                    getDigester().push(root);
+                    getDigester().endElement(namespaceURI, localName, qName);
+                }
+    
+                top = top.getParentNode();
+                depth--;
+            } catch (DOMException e) {
+                throw new SAXException(e.getMessage());
+            }
+
+        }
+
+
+        /**
+         * Adds a new
+         * {@link org.w3c.dom.ProcessingInstruction ProcessingInstruction} to 
+         * the current node.
+         * 
+         * @param target the processing instruction target
+         * @param data the processing instruction data, or null if none was 
+         *   supplied
+         * @throws SAXException if the DOM implementation throws an exception
+         */
+        public void processingInstruction(String target, String data)
+            throws SAXException {
+            
+            try {
+                top.appendChild(doc.createProcessingInstruction(target, data));
+            } catch (DOMException e) {
+                throw new SAXException(e.getMessage());
+            }
+
+        }
+
+
+        /**
+         * Adds a new child {@link org.w3c.dom.Element Element} to the current
+         * node.
+         * 
+         * @param namespaceURI the namespace URI
+         * @param localName the local name
+         * @param qName the qualified (prefixed) name
+         * @param atts the list of attributes
+         * @throws SAXException if the DOM implementation throws an exception
+         */
+        public void startElement(String namespaceURI, String localName,
+                                 String qName, Attributes atts)
+            throws SAXException {
+
+            try {
+                Node previousTop = top;
+                if ((localName == null) || (localName.length() == 0)) { 
+                    top = doc.createElement(qName);
+                } else {
+                    top = doc.createElementNS(namespaceURI, localName);
+                }
+                for (int i = 0; i < atts.getLength(); i++) {
+                    Attr attr = null;
+                    if ((atts.getLocalName(i) == null) ||
+                        (atts.getLocalName(i).length() == 0)) {
+                        attr = doc.createAttribute(atts.getQName(i));
+                        attr.setNodeValue(atts.getValue(i));
+                        ((Element)top).setAttributeNode(attr);
+                    } else {
+                        attr = doc.createAttributeNS(atts.getURI(i),
+                                                     atts.getLocalName(i));
+                        attr.setNodeValue(atts.getValue(i));
+                        ((Element)top).setAttributeNodeNS(attr);
+                    }
+                }
+                previousTop.appendChild(top);
+                depth++;
+            } catch (DOMException e) {
+                throw new SAXException(e.getMessage());
+            }
+
+        }
+
+    }
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor. Creates an instance of this rule that will create a
+     * DOM {@link org.w3c.dom.Element Element}.
+     */
+    public NodeCreateRule() throws ParserConfigurationException {
+
+        this(Node.ELEMENT_NODE);
+
+    }
+
+
+    /**
+     * Constructor. Creates an instance of this rule that will create a DOM
+     * {@link org.w3c.dom.Element Element}, but lets you specify the JAXP 
+     * <code>DocumentBuilder</code> that should be used when constructing the
+     * node tree.
+     * 
+     * @param documentBuilder the JAXP <code>DocumentBuilder</code> to use
+     */
+    public NodeCreateRule(DocumentBuilder documentBuilder) {
+
+        this(Node.ELEMENT_NODE, documentBuilder);
+
+    }
+
+
+    /**
+     * Constructor. Creates an instance of this rule that will create either a 
+     * DOM {@link org.w3c.dom.Element Element} or a DOM 
+     * {@link org.w3c.dom.DocumentFragment DocumentFragment}, depending on the
+     * value of the <code>nodeType</code> parameter.
+     * 
+     * @param nodeType the type of node to create, which can be either
+     *   {@link org.w3c.dom.Node#ELEMENT_NODE Node.ELEMENT_NODE} or 
+     *   {@link org.w3c.dom.Node#DOCUMENT_FRAGMENT_NODE Node.DOCUMENT_FRAGMENT_NODE}
+     * @throws IllegalArgumentException if the node type is not supported
+     */
+    public NodeCreateRule(int nodeType) throws ParserConfigurationException {
+
+        this(nodeType,
+             DocumentBuilderFactory.newInstance().newDocumentBuilder());
+
+    }
+
+
+    /**
+     * Constructor. Creates an instance of this rule that will create either a 
+     * DOM {@link org.w3c.dom.Element Element} or a DOM 
+     * {@link org.w3c.dom.DocumentFragment DocumentFragment}, depending on the
+     * value of the <code>nodeType</code> parameter. This constructor lets you
+     * specify the JAXP <code>DocumentBuilder</code> that should be used when
+     * constructing the node tree.
+     * 
+     * @param nodeType the type of node to create, which can be either
+     *   {@link org.w3c.dom.Node#ELEMENT_NODE Node.ELEMENT_NODE} or 
+     *   {@link org.w3c.dom.Node#DOCUMENT_FRAGMENT_NODE Node.DOCUMENT_FRAGMENT_NODE}
+     * @param documentBuilder the JAXP <code>DocumentBuilder</code> to use
+     * @throws IllegalArgumentException if the node type is not supported
+     */
+    public NodeCreateRule(int nodeType, DocumentBuilder documentBuilder) {
+
+        if (!((nodeType == Node.DOCUMENT_FRAGMENT_NODE) ||
+              (nodeType == Node.ELEMENT_NODE))) {
+            throw new IllegalArgumentException(
+                "Can only create nodes of type DocumentFragment and Element");
+        }
+        this.nodeType = nodeType;
+        this.documentBuilder = documentBuilder;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The JAXP <code>DocumentBuilder</code> to use.
+     */
+    private DocumentBuilder documentBuilder = null;
+
+
+    /**
+     * The type of the node that should be created. Must be one of the
+     * constants defined in {@link org.w3c.dom.Node Node}, but currently only
+     * {@link org.w3c.dom.Node#ELEMENT_NODE Node.ELEMENT_NODE} and 
+     * {@link org.w3c.dom.Node#DOCUMENT_FRAGMENT_NODE Node.DOCUMENT_FRAGMENT_NODE}
+     * are allowed values.
+     */
+    private int nodeType = Node.ELEMENT_NODE;
+
+
+    // ----------------------------------------------------------- Rule Methods
+
+
+    /**
+     * Implemented to replace the content handler currently in use by a 
+     * NodeBuilder.
+     * 
+     * @param namespaceURI the namespace URI of the matching element, or an 
+     *   empty string if the parser is not namespace aware or the element has
+     *   no namespace
+     * @param name the local name if the parser is namespace aware, or just 
+     *   the element name otherwise
+     * @param attributes The attribute list of this element
+     * @throws Exception indicates a JAXP configuration problem
+     */
+    public void begin(String namespaceURI, String name, Attributes attributes)
+        throws Exception {
+
+        XMLReader xmlReader = getDigester().getXMLReader();
+        Document doc = documentBuilder.newDocument();
+        NodeBuilder builder = null;
+        if (nodeType == Node.ELEMENT_NODE) {
+            Element element = null;
+            if (getDigester().getNamespaceAware()) {
+                element =
+                    doc.createElementNS(namespaceURI, name);
+                for (int i = 0; i < attributes.getLength(); i++) {
+                    element.setAttributeNS(attributes.getURI(i),
+                                           attributes.getLocalName(i),
+                                           attributes.getValue(i));
+                }
+            } else {
+                element = doc.createElement(name);
+                for (int i = 0; i < attributes.getLength(); i++) {
+                    element.setAttribute(attributes.getQName(i),
+                                         attributes.getValue(i));
+                }
+            }
+            builder = new NodeBuilder(doc, element);
+        } else {
+            builder = new NodeBuilder(doc, doc.createDocumentFragment());
+        }
+        xmlReader.setContentHandler(builder);
+
+    }
+
+
+    /**
+     * Pop the Node off the top of the stack.
+     */
+    public void end() throws Exception {
+
+        Object top = digester.pop();
+
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/ObjectCreateRule.java b/connectors/util/java/org/apache/tomcat/util/digester/ObjectCreateRule.java
new file mode 100644
index 0000000..c08bdd3
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/ObjectCreateRule.java
@@ -0,0 +1,241 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+
+import org.xml.sax.Attributes;
+
+
+/**
+ * Rule implementation that creates a new object and pushes it
+ * onto the object stack.  When the element is complete, the
+ * object will be popped
+ */
+
+public class ObjectCreateRule extends Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct an object create rule with the specified class name.
+     *
+     * @param digester The associated Digester
+     * @param className Java class name of the object to be created
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #ObjectCreateRule(String className)} instead.
+     */
+    public ObjectCreateRule(Digester digester, String className) {
+
+        this(className);
+
+    }
+
+
+    /**
+     * Construct an object create rule with the specified class.
+     *
+     * @param digester The associated Digester
+     * @param clazz Java class name of the object to be created
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #ObjectCreateRule(Class clazz)} instead.
+     */
+    public ObjectCreateRule(Digester digester, Class clazz) {
+
+        this(clazz);
+
+    }
+
+
+    /**
+     * Construct an object create rule with the specified class name and an
+     * optional attribute name containing an override.
+     *
+     * @param digester The associated Digester
+     * @param className Java class name of the object to be created
+     * @param attributeName Attribute name which, if present, contains an
+     *  override of the class name to create
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #ObjectCreateRule(String className, String attributeName)} instead.
+     */
+    public ObjectCreateRule(Digester digester, String className,
+                            String attributeName) {
+
+        this (className, attributeName);
+
+    }
+
+
+    /**
+     * Construct an object create rule with the specified class and an
+     * optional attribute name containing an override.
+     *
+     * @param digester The associated Digester
+     * @param attributeName Attribute name which, if present, contains an
+     * @param clazz Java class name of the object to be created
+     *  override of the class name to create
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #ObjectCreateRule(String attributeName, Class clazz)} instead.
+     */
+    public ObjectCreateRule(Digester digester,
+                            String attributeName,
+                            Class clazz) {
+
+        this(attributeName, clazz);
+
+    }
+
+    /**
+     * Construct an object create rule with the specified class name.
+     *
+     * @param className Java class name of the object to be created
+     */
+    public ObjectCreateRule(String className) {
+
+        this(className, (String) null);
+
+    }
+
+
+    /**
+     * Construct an object create rule with the specified class.
+     *
+     * @param clazz Java class name of the object to be created
+     */
+    public ObjectCreateRule(Class clazz) {
+
+        this(clazz.getName(), (String) null);
+
+    }
+
+
+    /**
+     * Construct an object create rule with the specified class name and an
+     * optional attribute name containing an override.
+     *
+     * @param className Java class name of the object to be created
+     * @param attributeName Attribute name which, if present, contains an
+     *  override of the class name to create
+     */
+    public ObjectCreateRule(String className,
+                            String attributeName) {
+
+        this.className = className;
+        this.attributeName = attributeName;
+
+    }
+
+
+    /**
+     * Construct an object create rule with the specified class and an
+     * optional attribute name containing an override.
+     *
+     * @param attributeName Attribute name which, if present, contains an
+     * @param clazz Java class name of the object to be created
+     *  override of the class name to create
+     */
+    public ObjectCreateRule(String attributeName,
+                            Class clazz) {
+
+        this(clazz.getName(), attributeName);
+
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The attribute containing an override class name if it is present.
+     */
+    protected String attributeName = null;
+
+
+    /**
+     * The Java class name of the object to be created.
+     */
+    protected String className = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the beginning of this element.
+     *
+     * @param attributes The attribute list of this element
+     */
+    public void begin(Attributes attributes) throws Exception {
+
+        // Identify the name of the class to instantiate
+        String realClassName = className;
+        if (attributeName != null) {
+            String value = attributes.getValue(attributeName);
+            if (value != null) {
+                realClassName = value;
+            }
+        }
+        if (digester.log.isDebugEnabled()) {
+            digester.log.debug("[ObjectCreateRule]{" + digester.match +
+                    "}New " + realClassName);
+        }
+
+        // Instantiate the new object and push it on the context stack
+        Class clazz = digester.getClassLoader().loadClass(realClassName);
+        Object instance = clazz.newInstance();
+        digester.push(instance);
+
+    }
+
+
+    /**
+     * Process the end of this element.
+     */
+    public void end() throws Exception {
+
+        Object top = digester.pop();
+        if (digester.log.isDebugEnabled()) {
+            digester.log.debug("[ObjectCreateRule]{" + digester.match +
+                    "} Pop " + top.getClass().getName());
+        }
+
+    }
+
+
+    /**
+     * Render a printable version of this Rule.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("ObjectCreateRule[");
+        sb.append("className=");
+        sb.append(className);
+        sb.append(", attributeName=");
+        sb.append(attributeName);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/ObjectCreationFactory.java b/connectors/util/java/org/apache/tomcat/util/digester/ObjectCreationFactory.java
new file mode 100644
index 0000000..621db34
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/ObjectCreationFactory.java
@@ -0,0 +1,59 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.tomcat.util.digester;
+
+
+import org.xml.sax.Attributes;
+
+/**
+ * <p> Interface for use with {@link FactoryCreateRule}.
+ * The rule calls {@link #createObject} to create an object
+ * to be pushed onto the <code>Digester</code> stack
+ * whenever it is matched.</p>
+ *
+ * <p> {@link AbstractObjectCreationFactory} is an abstract
+ * implementation suitable for creating anonymous
+ * <code>ObjectCreationFactory</code> implementations.
+ */
+public interface ObjectCreationFactory {
+
+    /**
+     * <p>Factory method called by {@link FactoryCreateRule} to supply an
+     * object based on the element's attributes.
+     *
+     * @param attributes the element's attributes
+     *
+     * @throws Exception any exception thrown will be propagated upwards
+     */
+    public Object createObject(Attributes attributes) throws Exception;
+
+    /**
+     * <p>Returns the {@link Digester} that was set by the
+     * {@link FactoryCreateRule} upon initialization.
+     */
+    public Digester getDigester();
+
+    /**
+     * <p>Set the {@link Digester} to allow the implementation to do logging,
+     * classloading based on the digester's classloader, etc.
+     *
+     * @param digester parent Digester object
+     */
+    public void setDigester(Digester digester);
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/ObjectParamRule.java b/connectors/util/java/org/apache/tomcat/util/digester/ObjectParamRule.java
new file mode 100644
index 0000000..4ebe142
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/ObjectParamRule.java
@@ -0,0 +1,124 @@
+/* $Id$
+ *
+ * Copyright 2002-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+import org.xml.sax.Attributes;
+
+/**
+ * <p>Rule implementation that saves a parameter for use by a surrounding
+ * <code>CallMethodRule<code>.</p>
+ *
+ * <p>This parameter may be:
+ * <ul>
+ * <li>an arbitrary Object defined programatically, assigned when the element pattern associated with the Rule is matched
+ * See {@link #ObjectParamRule(int paramIndex, Object param)}
+ * <li>an arbitrary Object defined programatically, assigned if the element pattern AND specified attribute name are matched
+ * See {@link #ObjectParamRule(int paramIndex, String attributeName, Object param)}
+ * </ul>
+ * </p>
+ *
+ * @since 1.4
+ */
+
+public class ObjectParamRule extends Rule {
+    // ----------------------------------------------------------- Constructors
+    /**
+     * Construct a "call parameter" rule that will save the given Object as
+     * the parameter value.
+     *
+     * @param paramIndex The zero-relative parameter number
+     * @param param the parameter to pass along
+     */
+    public ObjectParamRule(int paramIndex, Object param) {
+        this(paramIndex, null, param);
+    }
+
+
+    /**
+     * Construct a "call parameter" rule that will save the given Object as
+     * the parameter value, provided that the specified attribute exists.
+     *
+     * @param paramIndex The zero-relative parameter number
+     * @param attributeName The name of the attribute to match
+     * @param param the parameter to pass along
+     */
+    public ObjectParamRule(int paramIndex, String attributeName, Object param) {
+        this.paramIndex = paramIndex;
+        this.attributeName = attributeName;
+        this.param = param;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The attribute which we are attempting to match
+     */
+    protected String attributeName = null;
+
+    /**
+     * The zero-relative index of the parameter we are saving.
+     */
+    protected int paramIndex = 0;
+
+    /**
+     * The parameter we wish to pass to the method call
+     */
+    protected Object param = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Process the start of this element.
+     *
+     * @param attributes The attribute list for this element
+     */
+    public void begin(String namespace, String name,
+                      Attributes attributes) throws Exception {
+        Object anAttribute = null;
+        Object parameters[] = (Object[]) digester.peekParams();
+
+        if (attributeName != null) {
+            anAttribute = attributes.getValue(attributeName);
+            if(anAttribute != null) {
+                parameters[paramIndex] = param;
+            }
+            // note -- if attributeName != null and anAttribute == null, this rule
+            // will pass null as its parameter!
+        }else{
+            parameters[paramIndex] = param;
+        }
+    }
+
+    /**
+     * Render a printable version of this Rule.
+     */
+    public String toString() {
+        StringBuffer sb = new StringBuffer("ObjectParamRule[");
+        sb.append("paramIndex=");
+        sb.append(paramIndex);
+        sb.append(", attributeName=");
+        sb.append(attributeName);
+        sb.append(", param=");
+        sb.append(param);
+        sb.append("]");
+        return (sb.toString());
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/ParserFeatureSetterFactory.java b/connectors/util/java/org/apache/tomcat/util/digester/ParserFeatureSetterFactory.java
new file mode 100644
index 0000000..9f6ae1e
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/ParserFeatureSetterFactory.java
@@ -0,0 +1,74 @@
+/* $Id$
+ *
+ * Copyright 2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+import java.util.Properties;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+
+/**
+ * Creates a <code>SAXParser</code> based on the underlying parser.
+ * Allows logical properties depending on logical parser versions
+ * to be set.
+ *
+ * @since 1.6
+ */
+public class ParserFeatureSetterFactory{
+
+    /**
+     * <code>true</code> is Xerces is used.
+     */
+    private static boolean isXercesUsed; 
+
+    static {
+        try{
+            // Use reflection to avoid a build dependency with Xerces.
+            Class versionClass = 
+                            Class.forName("org.apache.xerces.impl.Version");
+            isXercesUsed = true;
+        } catch (Exception ex){
+            isXercesUsed = false;
+        }
+    }
+
+    /**
+     * Create a new <code>SAXParser</code>
+     * @param properties (logical) properties to be set on parser
+     * @return a <code>SAXParser</code> configured based on the underlying
+     * parser implementation.
+     */
+    public static SAXParser newSAXParser(Properties properties)
+            throws ParserConfigurationException, 
+                   SAXException,
+                   SAXNotRecognizedException, 
+                   SAXNotSupportedException {
+
+        if (isXercesUsed){
+            return XercesParser.newSAXParser(properties);
+        } else {
+            return GenericParser.newSAXParser(properties);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/PathCallParamRule.java b/connectors/util/java/org/apache/tomcat/util/digester/PathCallParamRule.java
new file mode 100644
index 0000000..58603ab
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/PathCallParamRule.java
@@ -0,0 +1,93 @@
+/* $Id$
+ *
+ * Copyright 2003-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+
+import org.xml.sax.Attributes;
+
+/**
+ * <p>Rule implementation that saves a parameter containing the 
+ * <code>Digester</code> matching path for use by a surrounding 
+ * <code>CallMethodRule</code>. This Rule is most useful when making 
+ * extensive use of wildcards in rule patterns.</p>
+ *
+ * @since 1.6
+ */
+
+public class PathCallParamRule extends Rule {
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Construct a "call parameter" rule that will save the body text of this
+     * element as the parameter value.
+     *
+     * @param paramIndex The zero-relative parameter number
+     */
+    public PathCallParamRule(int paramIndex) {
+
+        this.paramIndex = paramIndex;
+
+    }
+ 
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The zero-relative index of the parameter we are saving.
+     */
+    protected int paramIndex = 0;
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the start of this element.
+     *
+     * @param namespace the namespace URI of the matching element, or an 
+     *   empty string if the parser is not namespace aware or the element has
+     *   no namespace
+     * @param name the local name if the parser is namespace aware, or just 
+     *   the element name otherwise
+     * @param attributes The attribute list for this element
+
+     */
+    public void begin(String namespace, String name, Attributes attributes) throws Exception {
+
+        String param = getDigester().getMatch();
+        
+        if(param != null) {
+            Object parameters[] = (Object[]) digester.peekParams();
+            parameters[paramIndex] = param;
+        }
+        
+    }
+
+    /**
+     * Render a printable version of this Rule.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("PathCallParamRule[");
+        sb.append("paramIndex=");
+        sb.append(paramIndex);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/Rule.java b/connectors/util/java/org/apache/tomcat/util/digester/Rule.java
new file mode 100644
index 0000000..eb0b7e3
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/Rule.java
@@ -0,0 +1,244 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+
+import org.xml.sax.Attributes;
+
+
+/**
+ * Concrete implementations of this class implement actions to be taken when
+ * a corresponding nested pattern of XML elements has been matched.
+ */
+
+public abstract class Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Constructor sets the associated Digester.
+     *
+     * @param digester The digester with which this rule is associated
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. Use {@link #Rule()} instead.
+     */
+    public Rule(Digester digester) {
+
+        super();
+        setDigester(digester);
+
+    }
+    
+    /**
+     * <p>Base constructor.
+     * Now the digester will be set when the rule is added.</p>
+     */
+    public Rule() {}
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The Digester with which this Rule is associated.
+     */
+    protected Digester digester = null;
+
+
+    /**
+     * The namespace URI for which this Rule is relevant, if any.
+     */
+    protected String namespaceURI = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Digester with which this Rule is associated.
+     */
+    public Digester getDigester() {
+
+        return (this.digester);
+
+    }
+    
+    /**
+     * Set the <code>Digester</code> with which this <code>Rule</code> is associated.
+     */
+    public void setDigester(Digester digester) {
+        
+        this.digester = digester;
+        
+    }
+
+    /**
+     * Return the namespace URI for which this Rule is relevant, if any.
+     */
+    public String getNamespaceURI() {
+
+        return (this.namespaceURI);
+
+    }
+
+
+    /**
+     * Set the namespace URI for which this Rule is relevant, if any.
+     *
+     * @param namespaceURI Namespace URI for which this Rule is relevant,
+     *  or <code>null</code> to match independent of namespace.
+     */
+    public void setNamespaceURI(String namespaceURI) {
+
+        this.namespaceURI = namespaceURI;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * This method is called when the beginning of a matching XML element
+     * is encountered.
+     *
+     * @param attributes The attribute list of this element
+     * @deprecated Use the {@link #begin(String,String,Attributes) begin}
+     *   method with <code>namespace</code> and <code>name</code>
+     *   parameters instead.
+     */
+    public void begin(Attributes attributes) throws Exception {
+
+        ;	// The default implementation does nothing
+
+    }
+
+
+    /**
+     * This method is called when the beginning of a matching XML element
+     * is encountered. The default implementation delegates to the deprecated
+     * method {@link #begin(Attributes) begin} without the 
+     * <code>namespace</code> and <code>name</code> parameters, to retain 
+     * backwards compatibility.
+     *
+     * @param namespace the namespace URI of the matching element, or an 
+     *   empty string if the parser is not namespace aware or the element has
+     *   no namespace
+     * @param name the local name if the parser is namespace aware, or just 
+     *   the element name otherwise
+     * @param attributes The attribute list of this element
+     * @since Digester 1.4
+     */
+    public void begin(String namespace, String name, Attributes attributes)
+        throws Exception {
+
+        begin(attributes);
+
+    }
+
+
+    /**
+     * This method is called when the body of a matching XML element
+     * is encountered.  If the element has no body, this method is
+     * not called at all.
+     *
+     * @param text The text of the body of this element
+     * @deprecated Use the {@link #body(String,String,String) body} method
+     *   with <code>namespace</code> and <code>name</code> parameters
+     *   instead.
+     */
+    public void body(String text) throws Exception {
+
+        ;	// The default implementation does nothing
+
+    }
+
+
+    /**
+     * This method is called when the body of a matching XML element is 
+     * encountered.  If the element has no body, this method is not called at 
+     * all. The default implementation delegates to the deprecated method 
+     * {@link #body(String) body} without the <code>namespace</code> and
+     * <code>name</code> parameters, to retain backwards compatibility.
+     *
+     * @param namespace the namespace URI of the matching element, or an 
+     *   empty string if the parser is not namespace aware or the element has
+     *   no namespace
+     * @param name the local name if the parser is namespace aware, or just 
+     *   the element name otherwise
+     * @param text The text of the body of this element
+     * @since Digester 1.4
+     */
+    public void body(String namespace, String name, String text)
+        throws Exception {
+
+        body(text);
+
+    }
+
+
+    /**
+     * This method is called when the end of a matching XML element
+     * is encountered.
+     * 
+     * @deprecated Use the {@link #end(String,String) end} method with 
+     *   <code>namespace</code> and <code>name</code> parameters instead.
+     */
+    public void end() throws Exception {
+
+        ;	// The default implementation does nothing
+
+    }
+
+
+    /**
+     * This method is called when the end of a matching XML element
+     * is encountered. The default implementation delegates to the deprecated
+     * method {@link #end end} without the 
+     * <code>namespace</code> and <code>name</code> parameters, to retain 
+     * backwards compatibility.
+     *
+     * @param namespace the namespace URI of the matching element, or an 
+     *   empty string if the parser is not namespace aware or the element has
+     *   no namespace
+     * @param name the local name if the parser is namespace aware, or just 
+     *   the element name otherwise
+     * @since Digester 1.4
+     */
+    public void end(String namespace, String name)
+        throws Exception {
+
+        end();
+
+    }
+
+
+    /**
+     * This method is called after all parsing methods have been
+     * called, to allow Rules to remove temporary data.
+     */
+    public void finish() throws Exception {
+
+        ;	// The default implementation does nothing
+
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/RuleSet.java b/connectors/util/java/org/apache/tomcat/util/digester/RuleSet.java
new file mode 100644
index 0000000..777a8d2
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/RuleSet.java
@@ -0,0 +1,66 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.tomcat.util.digester;
+
+
+/**
+ * <p>Public interface defining a shorthand means of configuring a complete
+ * set of related <code>Rule</code> definitions, possibly associated with
+ * a particular namespace URI, in one operation.  To use an instance of a
+ * class that imlements this interface:</p>
+ * <ul>
+ * <li>Create a concrete implementation of this interface.</li>
+ * <li>Optionally, you can configure a <code>RuleSet</code> to be relevant
+ *     only for a particular namespace URI by configuring the value to be
+ *     returned by <code>getNamespaceURI()</code>.</li>
+ * <li>As you are configuring your Digester instance, call
+ *     <code>digester.addRuleSet()</code> and pass the RuleSet instance.</li>
+ * <li>Digester will call the <code>addRuleInstances()</code> method of
+ *     your RuleSet to configure the necessary rules.</li>
+ * </ul>
+ */
+
+public interface RuleSet {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the namespace URI that will be applied to all Rule instances
+     * created from this RuleSet.
+     */
+    public String getNamespaceURI();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add the set of Rule instances defined in this RuleSet to the
+     * specified <code>Digester</code> instance, associating them with
+     * our namespace URI (if any).  This method should only be called
+     * by a Digester instance.
+     *
+     * @param digester Digester instance to which the new Rule instances
+     *  should be added.
+     */
+    public void addRuleInstances(Digester digester);
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/RuleSetBase.java b/connectors/util/java/org/apache/tomcat/util/digester/RuleSetBase.java
new file mode 100644
index 0000000..817f761
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/RuleSetBase.java
@@ -0,0 +1,70 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+
+/**
+ * <p>Convenience base class that implements the {@link RuleSet} interface.
+ * Concrete implementations should list all of their actual rule creation
+ * logic in the <code>addRuleSet()</code> implementation.</p>
+ */
+
+public abstract class RuleSetBase implements RuleSet {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The namespace URI that all Rule instances created by this RuleSet
+     * will be associated with.
+     */
+    protected String namespaceURI = null;
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the namespace URI that will be applied to all Rule instances
+     * created from this RuleSet.
+     */
+    public String getNamespaceURI() {
+
+        return (this.namespaceURI);
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Add the set of Rule instances defined in this RuleSet to the
+     * specified <code>Digester</code> instance, associating them with
+     * our namespace URI (if any).  This method should only be called
+     * by a Digester instance.
+     *
+     * @param digester Digester instance to which the new Rule instances
+     *  should be added.
+     */
+    public abstract void addRuleInstances(Digester digester);
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/Rules.java b/connectors/util/java/org/apache/tomcat/util/digester/Rules.java
new file mode 100644
index 0000000..73ff032
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/Rules.java
@@ -0,0 +1,127 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+
+import java.util.List;
+
+
+/**
+ * Public interface defining a collection of Rule instances (and corresponding
+ * matching patterns) plus an implementation of a matching policy that selects
+ * the rules that match a particular pattern of nested elements discovered
+ * during parsing.
+ */
+
+public interface Rules {
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Digester instance with which this Rules instance is
+     * associated.
+     */
+    public Digester getDigester();
+
+
+    /**
+     * Set the Digester instance with which this Rules instance is associated.
+     *
+     * @param digester The newly associated Digester instance
+     */
+    public void setDigester(Digester digester);
+
+
+    /**
+     * Return the namespace URI that will be applied to all subsequently
+     * added <code>Rule</code> objects.
+     */
+    public String getNamespaceURI();
+
+
+    /**
+     * Set the namespace URI that will be applied to all subsequently
+     * added <code>Rule</code> objects.
+     *
+     * @param namespaceURI Namespace URI that must match on all
+     *  subsequently added rules, or <code>null</code> for matching
+     *  regardless of the current namespace URI
+     */
+    public void setNamespaceURI(String namespaceURI);
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Register a new Rule instance matching the specified pattern.
+     *
+     * @param pattern Nesting pattern to be matched for this Rule
+     * @param rule Rule instance to be registered
+     */
+    public void add(String pattern, Rule rule);
+
+
+    /**
+     * Clear all existing Rule instance registrations.
+     */
+    public void clear();
+
+
+    /**
+     * Return a List of all registered Rule instances that match the specified
+     * nesting pattern, or a zero-length List if there are no matches.  If more
+     * than one Rule instance matches, they <strong>must</strong> be returned
+     * in the order originally registered through the <code>add()</code>
+     * method.
+     *
+     * @param pattern Nesting pattern to be matched
+     *
+     * @deprecated Call match(namespaceURI,pattern) instead.
+     */
+    public List match(String pattern);
+
+
+    /**
+     * Return a List of all registered Rule instances that match the specified
+     * nesting pattern, or a zero-length List if there are no matches.  If more
+     * than one Rule instance matches, they <strong>must</strong> be returned
+     * in the order originally registered through the <code>add()</code>
+     * method.
+     *
+     * @param namespaceURI Namespace URI for which to select matching rules,
+     *  or <code>null</code> to match regardless of namespace URI
+     * @param pattern Nesting pattern to be matched
+     */
+    public List match(String namespaceURI, String pattern);
+
+
+    /**
+     * Return a List of all registered Rule instances, or a zero-length List
+     * if there are no registered Rule instances.  If more than one Rule
+     * instance has been registered, they <strong>must</strong> be returned
+     * in the order originally registered through the <code>add()</code>
+     * method.
+     */
+    public List rules();
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/RulesBase.java b/connectors/util/java/org/apache/tomcat/util/digester/RulesBase.java
new file mode 100644
index 0000000..5da460e
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/RulesBase.java
@@ -0,0 +1,293 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * <p>Default implementation of the <code>Rules</code> interface that supports
+ * the standard rule matching behavior.  This class can also be used as a
+ * base class for specialized <code>Rules</code> implementations.</p>
+ *
+ * <p>The matching policies implemented by this class support two different
+ * types of pattern matching rules:</p>
+ * <ul>
+ * <li><em>Exact Match</em> - A pattern "a/b/c" exactly matches a
+ *     <code>&lt;c&gt;</code> element, nested inside a <code>&lt;b&gt;</code>
+ *     element, which is nested inside an <code>&lt;a&gt;</code> element.</li>
+ * <li><em>Tail Match</em> - A pattern "&#42;/a/b" matches a
+ *     <code>&lt;b&gt;</code> element, nested inside an <code>&lt;a&gt;</code>
+ *      element, no matter how deeply the pair is nested.</li>
+ * </ul>
+ */
+
+public class RulesBase implements Rules {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The set of registered Rule instances, keyed by the matching pattern.
+     * Each value is a List containing the Rules for that pattern, in the
+     * order that they were orginally registered.
+     */
+    protected HashMap cache = new HashMap();
+
+
+    /**
+     * The Digester instance with which this Rules instance is associated.
+     */
+    protected Digester digester = null;
+
+
+    /**
+     * The namespace URI for which subsequently added <code>Rule</code>
+     * objects are relevant, or <code>null</code> for matching independent
+     * of namespaces.
+     */
+    protected String namespaceURI = null;
+
+
+    /**
+     * The set of registered Rule instances, in the order that they were
+     * originally registered.
+     */
+    protected ArrayList rules = new ArrayList();
+
+
+    // ------------------------------------------------------------- Properties
+
+
+    /**
+     * Return the Digester instance with which this Rules instance is
+     * associated.
+     */
+    public Digester getDigester() {
+
+        return (this.digester);
+
+    }
+
+
+    /**
+     * Set the Digester instance with which this Rules instance is associated.
+     *
+     * @param digester The newly associated Digester instance
+     */
+    public void setDigester(Digester digester) {
+
+        this.digester = digester;
+        Iterator items = rules.iterator();
+        while (items.hasNext()) {
+            Rule item = (Rule) items.next();
+            item.setDigester(digester);
+        }
+
+    }
+
+
+    /**
+     * Return the namespace URI that will be applied to all subsequently
+     * added <code>Rule</code> objects.
+     */
+    public String getNamespaceURI() {
+
+        return (this.namespaceURI);
+
+    }
+
+
+    /**
+     * Set the namespace URI that will be applied to all subsequently
+     * added <code>Rule</code> objects.
+     *
+     * @param namespaceURI Namespace URI that must match on all
+     *  subsequently added rules, or <code>null</code> for matching
+     *  regardless of the current namespace URI
+     */
+    public void setNamespaceURI(String namespaceURI) {
+
+        this.namespaceURI = namespaceURI;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Register a new Rule instance matching the specified pattern.
+     *
+     * @param pattern Nesting pattern to be matched for this Rule
+     * @param rule Rule instance to be registered
+     */
+    public void add(String pattern, Rule rule) {
+        // to help users who accidently add '/' to the end of their patterns
+        int patternLength = pattern.length();
+        if (patternLength>1 && pattern.endsWith("/")) {
+            pattern = pattern.substring(0, patternLength-1);
+        }
+        
+        
+        List list = (List) cache.get(pattern);
+        if (list == null) {
+            list = new ArrayList();
+            cache.put(pattern, list);
+        }
+        list.add(rule);
+        rules.add(rule);
+        if (this.digester != null) {
+            rule.setDigester(this.digester);
+        }
+        if (this.namespaceURI != null) {
+            rule.setNamespaceURI(this.namespaceURI);
+        }
+
+    }
+
+
+    /**
+     * Clear all existing Rule instance registrations.
+     */
+    public void clear() {
+
+        cache.clear();
+        rules.clear();
+
+    }
+
+
+    /**
+     * Return a List of all registered Rule instances that match the specified
+     * nesting pattern, or a zero-length List if there are no matches.  If more
+     * than one Rule instance matches, they <strong>must</strong> be returned
+     * in the order originally registered through the <code>add()</code>
+     * method.
+     *
+     * @param pattern Nesting pattern to be matched
+     *
+     * @deprecated Call match(namespaceURI,pattern) instead.
+     */
+    public List match(String pattern) {
+
+        return (match(null, pattern));
+
+    }
+
+
+    /**
+     * Return a List of all registered Rule instances that match the specified
+     * nesting pattern, or a zero-length List if there are no matches.  If more
+     * than one Rule instance matches, they <strong>must</strong> be returned
+     * in the order originally registered through the <code>add()</code>
+     * method.
+     *
+     * @param namespaceURI Namespace URI for which to select matching rules,
+     *  or <code>null</code> to match regardless of namespace URI
+     * @param pattern Nesting pattern to be matched
+     */
+    public List match(String namespaceURI, String pattern) {
+
+        // List rulesList = (List) this.cache.get(pattern);
+        List rulesList = lookup(namespaceURI, pattern);
+        if ((rulesList == null) || (rulesList.size() < 1)) {
+            // Find the longest key, ie more discriminant
+            String longKey = "";
+            Iterator keys = this.cache.keySet().iterator();
+            while (keys.hasNext()) {
+                String key = (String) keys.next();
+                if (key.startsWith("*/")) {
+                    if (pattern.equals(key.substring(2)) ||
+                        pattern.endsWith(key.substring(1))) {
+                        if (key.length() > longKey.length()) {
+                            // rulesList = (List) this.cache.get(key);
+                            rulesList = lookup(namespaceURI, key);
+                            longKey = key;
+                        }
+                    }
+                }
+            }
+        }
+        if (rulesList == null) {
+            rulesList = new ArrayList();
+        }
+        return (rulesList);
+
+    }
+
+
+    /**
+     * Return a List of all registered Rule instances, or a zero-length List
+     * if there are no registered Rule instances.  If more than one Rule
+     * instance has been registered, they <strong>must</strong> be returned
+     * in the order originally registered through the <code>add()</code>
+     * method.
+     */
+    public List rules() {
+
+        return (this.rules);
+
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Return a List of Rule instances for the specified pattern that also
+     * match the specified namespace URI (if any).  If there are no such
+     * rules, return <code>null</code>.
+     *
+     * @param namespaceURI Namespace URI to match, or <code>null</code> to
+     *  select matching rules regardless of namespace URI
+     * @param pattern Pattern to be matched
+     */
+    protected List lookup(String namespaceURI, String pattern) {
+
+        // Optimize when no namespace URI is specified
+        List list = (List) this.cache.get(pattern);
+        if (list == null) {
+            return (null);
+        }
+        if ((namespaceURI == null) || (namespaceURI.length() == 0)) {
+            return (list);
+        }
+
+        // Select only Rules that match on the specified namespace URI
+        ArrayList results = new ArrayList();
+        Iterator items = list.iterator();
+        while (items.hasNext()) {
+            Rule item = (Rule) items.next();
+            if ((namespaceURI.equals(item.getNamespaceURI())) ||
+                    (item.getNamespaceURI() == null)) {
+                results.add(item);
+            }
+        }
+        return (results);
+
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/SetNextRule.java b/connectors/util/java/org/apache/tomcat/util/digester/SetNextRule.java
new file mode 100644
index 0000000..bfe3ab9
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/SetNextRule.java
@@ -0,0 +1,214 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+import org.apache.tomcat.util.IntrospectionUtils;
+
+
+/**
+ * <p>Rule implementation that calls a method on the (top-1) (parent)
+ * object, passing the top object (child) as an argument.  It is
+ * commonly used to establish parent-child relationships.</p>
+ *
+ * <p>This rule now supports more flexible method matching by default.
+ * It is possible that this may break (some) code 
+ * written against release 1.1.1 or earlier.
+ * See {@link #isExactMatch()} for more details.</p> 
+ */
+
+public class SetNextRule extends Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a "set next" rule with the specified method name.  The
+     * method's argument type is assumed to be the class of the
+     * child object.
+     *
+     * @param digester The associated Digester
+     * @param methodName Method name of the parent method to call
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #SetNextRule(String methodName)} instead.
+     */
+    public SetNextRule(Digester digester, String methodName) {
+
+        this(methodName);
+
+    }
+
+
+    /**
+     * Construct a "set next" rule with the specified method name.
+     *
+     * @param digester The associated Digester
+     * @param methodName Method name of the parent method to call
+     * @param paramType Java class of the parent method's argument
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
+     *  for a <code>boolean</code> parameter)
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #SetNextRule(String methodName,String paramType)} instead.
+     */
+    public SetNextRule(Digester digester, String methodName,
+                       String paramType) {
+
+        this(methodName, paramType);
+
+    }
+
+    /**
+     * Construct a "set next" rule with the specified method name.  The
+     * method's argument type is assumed to be the class of the
+     * child object.
+     *
+     * @param methodName Method name of the parent method to call
+     */
+    public SetNextRule(String methodName) {
+
+        this(methodName, null);
+
+    }
+
+
+    /**
+     * Construct a "set next" rule with the specified method name.
+     *
+     * @param methodName Method name of the parent method to call
+     * @param paramType Java class of the parent method's argument
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
+     *  for a <code>boolean</code> parameter)
+     */
+    public SetNextRule(String methodName,
+                       String paramType) {
+
+        this.methodName = methodName;
+        this.paramType = paramType;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The method name to call on the parent object.
+     */
+    protected String methodName = null;
+
+
+    /**
+     * The Java class name of the parameter type expected by the method.
+     */
+    protected String paramType = null;
+
+    /**
+     * Should we use exact matching. Default is no.
+     */
+    protected boolean useExactMatch = false;
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Is exact matching being used.</p>
+     *
+     * <p>This rule uses <code>org.apache.commons.beanutils.MethodUtils</code> 
+     * to introspect the relevent objects so that the right method can be called.
+     * Originally, <code>MethodUtils.invokeExactMethod</code> was used.
+     * This matches methods very strictly 
+     * and so may not find a matching method when one exists.
+     * This is still the behaviour when exact matching is enabled.</p>
+     *
+     * <p>When exact matching is disabled, <code>MethodUtils.invokeMethod</code> is used.
+     * This method finds more methods but is less precise when there are several methods 
+     * with correct signatures.
+     * So, if you want to choose an exact signature you might need to enable this property.</p>
+     *
+     * <p>The default setting is to disable exact matches.</p>
+     *
+     * @return true iff exact matching is enabled
+     * @since Digester Release 1.1.1
+     */
+    public boolean isExactMatch() {
+    
+        return useExactMatch;
+    }
+    
+    /**
+     * <p>Set whether exact matching is enabled.</p>
+     *
+     * <p>See {@link #isExactMatch()}.</p>
+     *
+     * @param useExactMatch should this rule use exact method matching
+     * @since Digester Release 1.1.1
+     */    
+    public void setExactMatch(boolean useExactMatch) {
+
+        this.useExactMatch = useExactMatch;
+    }
+
+    /**
+     * Process the end of this element.
+     */
+    public void end() throws Exception {
+
+        // Identify the objects to be used
+        Object child = digester.peek(0);
+        Object parent = digester.peek(1);
+        if (digester.log.isDebugEnabled()) {
+            if (parent == null) {
+                digester.log.debug("[SetNextRule]{" + digester.match +
+                        "} Call [NULL PARENT]." +
+                        methodName + "(" + child + ")");
+            } else {
+                digester.log.debug("[SetNextRule]{" + digester.match +
+                        "} Call " + parent.getClass().getName() + "." +
+                        methodName + "(" + child + ")");
+            }
+        }
+
+        // Call the specified method
+        IntrospectionUtils.callMethod1(parent, methodName,
+                child, paramType, digester.getClassLoader());
+                
+    }
+
+
+    /**
+     * Render a printable version of this Rule.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("SetNextRule[");
+        sb.append("methodName=");
+        sb.append(methodName);
+        sb.append(", paramType=");
+        sb.append(paramType);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/SetPropertiesRule.java b/connectors/util/java/org/apache/tomcat/util/digester/SetPropertiesRule.java
new file mode 100644
index 0000000..514310f
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/SetPropertiesRule.java
@@ -0,0 +1,261 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.xml.sax.Attributes;
+
+
+/**
+ * <p>Rule implementation that sets properties on the object at the top of the
+ * stack, based on attributes with corresponding names.</p>
+ *
+ * <p>This rule supports custom mapping of attribute names to property names.
+ * The default mapping for particular attributes can be overridden by using 
+ * {@link #SetPropertiesRule(String[] attributeNames, String[] propertyNames)}.
+ * This allows attributes to be mapped to properties with different names.
+ * Certain attributes can also be marked to be ignored.</p>
+ */
+
+public class SetPropertiesRule extends Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Default constructor sets only the the associated Digester.
+     *
+     * @param digester The digester with which this rule is associated
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #SetPropertiesRule()} instead.
+     */
+    public SetPropertiesRule(Digester digester) {
+
+        this();
+
+    }
+    
+
+    /**
+     * Base constructor.
+     */
+    public SetPropertiesRule() {
+
+        // nothing to set up 
+
+    }
+    
+    /** 
+     * <p>Convenience constructor overrides the mapping for just one property.</p>
+     *
+     * <p>For details about how this works, see
+     * {@link #SetPropertiesRule(String[] attributeNames, String[] propertyNames)}.</p>
+     *
+     * @param attributeName map this attribute 
+     * @param propertyName to a property with this name
+     */
+    public SetPropertiesRule(String attributeName, String propertyName) {
+        
+        attributeNames = new String[1];
+        attributeNames[0] = attributeName;
+        propertyNames = new String[1];
+        propertyNames[0] = propertyName;
+    }
+    
+    /** 
+     * <p>Constructor allows attribute->property mapping to be overriden.</p>
+     *
+     * <p>Two arrays are passed in. 
+     * One contains the attribute names and the other the property names.
+     * The attribute name / property name pairs are match by position
+     * In order words, the first string in the attribute name list matches
+     * to the first string in the property name list and so on.</p>
+     *
+     * <p>If a property name is null or the attribute name has no matching
+     * property name, then this indicates that the attibute should be ignored.</p>
+     * 
+     * <h5>Example One</h5>
+     * <p> The following constructs a rule that maps the <code>alt-city</code>
+     * attribute to the <code>city</code> property and the <code>alt-state</code>
+     * to the <code>state</code> property. 
+     * All other attributes are mapped as usual using exact name matching.
+     * <code><pre>
+     *      SetPropertiesRule(
+     *                new String[] {"alt-city", "alt-state"}, 
+     *                new String[] {"city", "state"});
+     * </pre></code>
+     *
+     * <h5>Example Two</h5>
+     * <p> The following constructs a rule that maps the <code>class</code>
+     * attribute to the <code>className</code> property.
+     * The attribute <code>ignore-me</code> is not mapped.
+     * All other attributes are mapped as usual using exact name matching.
+     * <code><pre>
+     *      SetPropertiesRule(
+     *                new String[] {"class", "ignore-me"}, 
+     *                new String[] {"className"});
+     * </pre></code>
+     *
+     * @param attributeNames names of attributes to map
+     * @param propertyNames names of properties mapped to
+     */
+    public SetPropertiesRule(String[] attributeNames, String[] propertyNames) {
+        // create local copies
+        this.attributeNames = new String[attributeNames.length];
+        for (int i=0, size=attributeNames.length; i<size; i++) {
+            this.attributeNames[i] = attributeNames[i];
+        }
+        
+        this.propertyNames = new String[propertyNames.length];
+        for (int i=0, size=propertyNames.length; i<size; i++) {
+            this.propertyNames[i] = propertyNames[i];
+        } 
+    }
+        
+    // ----------------------------------------------------- Instance Variables
+    
+    /** 
+     * Attribute names used to override natural attribute->property mapping
+     */
+    private String [] attributeNames;
+    /** 
+     * Property names used to override natural attribute->property mapping
+     */    
+    private String [] propertyNames;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the beginning of this element.
+     *
+     * @param attributes The attribute list of this element
+     */
+    public void begin(Attributes attributes) throws Exception {
+        
+        // Populate the corresponding properties of the top object
+        Object top = digester.peek();
+        if (digester.log.isDebugEnabled()) {
+            if (top != null) {
+                digester.log.debug("[SetPropertiesRule]{" + digester.match +
+                                   "} Set " + top.getClass().getName() +
+                                   " properties");
+            } else {
+                digester.log.debug("[SetPropertiesRule]{" + digester.match +
+                                   "} Set NULL properties");
+            }
+        }
+        
+        // set up variables for custom names mappings
+        int attNamesLength = 0;
+        if (attributeNames != null) {
+            attNamesLength = attributeNames.length;
+        }
+        int propNamesLength = 0;
+        if (propertyNames != null) {
+            propNamesLength = propertyNames.length;
+        }
+        
+        for (int i = 0; i < attributes.getLength(); i++) {
+            String name = attributes.getLocalName(i);
+            if ("".equals(name)) {
+                name = attributes.getQName(i);
+            }
+            String value = attributes.getValue(i);
+            
+            // we'll now check for custom mappings
+            for (int n = 0; n<attNamesLength; n++) {
+                if (name.equals(attributeNames[n])) {
+                    if (n < propNamesLength) {
+                        // set this to value from list
+                        name = propertyNames[n];
+                    
+                    } else {
+                        // set name to null
+                        // we'll check for this later
+                        name = null;
+                    }
+                    break;
+                }
+            } 
+            
+            if (digester.log.isDebugEnabled()) {
+                digester.log.debug("[SetPropertiesRule]{" + digester.match +
+                        "} Setting property '" + name + "' to '" +
+                        value + "'");
+            }
+            IntrospectionUtils.setProperty(top, name, value);
+        }
+
+    }
+
+
+    /**
+     * <p>Add an additional attribute name to property name mapping.
+     * This is intended to be used from the xml rules.
+     */
+    public void addAlias(String attributeName, String propertyName) {
+        
+        // this is a bit tricky.
+        // we'll need to resize the array.
+        // probably should be synchronized but digester's not thread safe anyway
+        if (attributeNames == null) {
+            
+            attributeNames = new String[1];
+            attributeNames[0] = attributeName;
+            propertyNames = new String[1];
+            propertyNames[0] = propertyName;        
+            
+        } else {
+            int length = attributeNames.length;
+            String [] tempAttributes = new String[length + 1];
+            for (int i=0; i<length; i++) {
+                tempAttributes[i] = attributeNames[i];
+            }
+            tempAttributes[length] = attributeName;
+            
+            String [] tempProperties = new String[length + 1];
+            for (int i=0; i<length && i< propertyNames.length; i++) {
+                tempProperties[i] = propertyNames[i];
+            }
+            tempProperties[length] = propertyName;
+            
+            propertyNames = tempProperties;
+            attributeNames = tempAttributes;
+        }        
+    }
+  
+
+    /**
+     * Render a printable version of this Rule.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("SetPropertiesRule[");
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/SetPropertyRule.java b/connectors/util/java/org/apache/tomcat/util/digester/SetPropertyRule.java
new file mode 100644
index 0000000..77ca1cf
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/SetPropertyRule.java
@@ -0,0 +1,149 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.xml.sax.Attributes;
+
+
+/**
+ * Rule implementation that sets an individual property on the object at the
+ * top of the stack, based on attributes with specified names.
+ */
+
+public class SetPropertyRule extends Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a "set property" rule with the specified name and value
+     * attributes.
+     *
+     * @param digester The digester with which this rule is associated
+     * @param name Name of the attribute that will contain the name of the
+     *  property to be set
+     * @param value Name of the attribute that will contain the value to which
+     *  the property should be set
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #SetPropertyRule(String name, String value)} instead.
+     */
+    public SetPropertyRule(Digester digester, String name, String value) {
+
+        this(name, value);
+
+    }
+
+    /**
+     * Construct a "set property" rule with the specified name and value
+     * attributes.
+     *
+     * @param name Name of the attribute that will contain the name of the
+     *  property to be set
+     * @param value Name of the attribute that will contain the value to which
+     *  the property should be set
+     */
+    public SetPropertyRule(String name, String value) {
+
+        this.name = name;
+        this.value = value;
+
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The attribute that will contain the property name.
+     */
+    protected String name = null;
+
+
+    /**
+     * The attribute that will contain the property value.
+     */
+    protected String value = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Process the beginning of this element.
+     *
+     * @param attributes The attribute list of this element
+     *
+     * @exception NoSuchMethodException if the bean does not
+     *  have a writeable property of the specified name
+     */
+    public void begin(Attributes attributes) throws Exception {
+
+        // Identify the actual property name and value to be used
+        String actualName = null;
+        String actualValue = null;
+        for (int i = 0; i < attributes.getLength(); i++) {
+            String name = attributes.getLocalName(i);
+            if ("".equals(name)) {
+                name = attributes.getQName(i);
+            }
+            String value = attributes.getValue(i);
+            if (name.equals(this.name)) {
+                actualName = value;
+            } else if (name.equals(this.value)) {
+                actualValue = value;
+            }
+        }
+
+        // Get a reference to the top object
+        Object top = digester.peek();
+
+        // Log some debugging information
+        if (digester.log.isDebugEnabled()) {
+            digester.log.debug("[SetPropertyRule]{" + digester.match +
+                    "} Set " + top.getClass().getName() + " property " +
+                    actualName + " to " + actualValue);
+        }
+
+        // Set the property (with conversion as necessary)
+        // FIXME: Exception if property doesn't exist ?
+        IntrospectionUtils.setProperty(top, actualName, actualValue);
+
+    }
+
+
+    /**
+     * Render a printable version of this Rule.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("SetPropertyRule[");
+        sb.append("name=");
+        sb.append(name);
+        sb.append(", value=");
+        sb.append(value);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/SetRootRule.java b/connectors/util/java/org/apache/tomcat/util/digester/SetRootRule.java
new file mode 100644
index 0000000..6b07ab2
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/SetRootRule.java
@@ -0,0 +1,215 @@
+/* $Id$
+ *
+ * Copyright 2002-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+import org.apache.tomcat.util.IntrospectionUtils;
+
+
+/**
+ * <p>Rule implementation that calls a method on the root object on the stack,
+ * passing the top object (child) as an argument.
+ * It is important to remember that this rule acts on <code>end</code>.</p>
+ *
+ * <p>This rule now supports more flexible method matching by default.
+ * It is possible that this may break (some) code 
+ * written against release 1.1.1 or earlier.
+ * See {@link #isExactMatch()} for more details.</p>
+ */
+
+public class SetRootRule extends Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a "set root" rule with the specified method name.  The
+     * method's argument type is assumed to be the class of the
+     * child object.
+     *
+     * @param digester The associated Digester
+     * @param methodName Method name of the parent method to call
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #SetRootRule(String methodName)} instead.
+     */
+    public SetRootRule(Digester digester, String methodName) {
+
+        this(methodName);
+
+    }
+
+
+    /**
+     * Construct a "set root" rule with the specified method name.
+     *
+     * @param digester The associated Digester
+     * @param methodName Method name of the parent method to call
+     * @param paramType Java class of the parent method's argument
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
+     *  for a <code>boolean</code> parameter)
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #SetRootRule(String methodName,String paramType)} instead.
+     */
+    public SetRootRule(Digester digester, String methodName,
+                       String paramType) {
+
+        this(methodName, paramType);
+
+    }
+
+    /**
+     * Construct a "set root" rule with the specified method name.  The
+     * method's argument type is assumed to be the class of the
+     * child object.
+     *
+     * @param methodName Method name of the parent method to call
+     */
+    public SetRootRule(String methodName) {
+
+        this(methodName, null);
+
+    }
+
+
+    /**
+     * Construct a "set root" rule with the specified method name.
+     *
+     * @param methodName Method name of the parent method to call
+     * @param paramType Java class of the parent method's argument
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
+     *  for a <code>boolean</code> parameter)
+     */
+    public SetRootRule(String methodName,
+                       String paramType) {
+
+        this.methodName = methodName;
+        this.paramType = paramType;
+
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The method name to call on the parent object.
+     */
+    protected String methodName = null;
+
+
+    /**
+     * The Java class name of the parameter type expected by the method.
+     */
+    protected String paramType = null;
+    
+    /**
+     * Should we use exact matching. Default is no.
+     */
+    protected boolean useExactMatch = false;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * <p>Is exact matching being used.</p>
+     *
+     * <p>This rule uses <code>org.apache.commons.beanutils.MethodUtils</code> 
+     * to introspect the relevent objects so that the right method can be called.
+     * Originally, <code>MethodUtils.invokeExactMethod</code> was used.
+     * This matches methods very strictly 
+     * and so may not find a matching method when one exists.
+     * This is still the behaviour when exact matching is enabled.</p>
+     *
+     * <p>When exact matching is disabled, <code>MethodUtils.invokeMethod</code> is used.
+     * This method finds more methods but is less precise when there are several methods 
+     * with correct signatures.
+     * So, if you want to choose an exact signature you might need to enable this property.</p>
+     *
+     * <p>The default setting is to disable exact matches.</p>
+     *
+     * @return true iff exact matching is enabled
+     * @since Digester Release 1.1.1
+     */
+    public boolean isExactMatch() {
+    
+        return useExactMatch;
+    }
+    
+    
+    /**
+     * <p>Set whether exact matching is enabled.</p>
+     *
+     * <p>See {@link #isExactMatch()}.</p>
+     *
+     * @param useExactMatch should this rule use exact method matching
+     * @since Digester Release 1.1.1
+     */
+    public void setExactMatch(boolean useExactMatch) {
+
+        this.useExactMatch = useExactMatch;
+    }
+
+    /**
+     * Process the end of this element.
+     */
+    public void end() throws Exception {
+
+        // Identify the objects to be used
+        Object child = digester.peek(0);
+        Object parent = digester.root;
+        if (digester.log.isDebugEnabled()) {
+            if (parent == null) {
+                digester.log.debug("[SetRootRule]{" + digester.match +
+                        "} Call [NULL ROOT]." +
+                        methodName + "(" + child + ")");
+            } else {
+                digester.log.debug("[SetRootRule]{" + digester.match +
+                        "} Call " + parent.getClass().getName() + "." +
+                        methodName + "(" + child + ")");
+            }
+        }
+
+        // Call the specified method
+        IntrospectionUtils.callMethod1(parent, methodName,
+                child, paramType, digester.getClassLoader());
+
+    }
+
+
+    /**
+     * Render a printable version of this Rule.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("SetRootRule[");
+        sb.append("methodName=");
+        sb.append(methodName);
+        sb.append(", paramType=");
+        sb.append(paramType);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/SetTopRule.java b/connectors/util/java/org/apache/tomcat/util/digester/SetTopRule.java
new file mode 100644
index 0000000..266bcd2
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/SetTopRule.java
@@ -0,0 +1,215 @@
+/* $Id$
+ *
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+
+import org.apache.tomcat.util.IntrospectionUtils;
+
+
+/**
+ * <p>Rule implementation that calls a "set parent" method on the top (child)
+ * object, passing the (top-1) (parent) object as an argument.</p>
+ *
+ * <p>This rule now supports more flexible method matching by default.
+ * It is possible that this may break (some) code 
+ * written against release 1.1.1 or earlier.
+ * See {@link #isExactMatch()} for more details.</p>
+ */
+
+public class SetTopRule extends Rule {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct a "set parent" rule with the specified method name.  The
+     * "set parent" method's argument type is assumed to be the class of the
+     * parent object.
+     *
+     * @param digester The associated Digester
+     * @param methodName Method name of the "set parent" method to call
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #SetTopRule(String methodName)} instead.
+     */
+    public SetTopRule(Digester digester, String methodName) {
+
+        this(methodName);
+
+    }
+
+
+    /**
+     * Construct a "set parent" rule with the specified method name.
+     *
+     * @param digester The associated Digester
+     * @param methodName Method name of the "set parent" method to call
+     * @param paramType Java class of the "set parent" method's argument
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
+     *  for a <code>boolean</code> parameter)
+     *
+     * @deprecated The digester instance is now set in the {@link Digester#addRule} method. 
+     * Use {@link #SetTopRule(String methodName, String paramType)} instead.
+     */
+    public SetTopRule(Digester digester, String methodName,
+                      String paramType) {
+
+        this(methodName, paramType);
+
+    }
+
+    /**
+     * Construct a "set parent" rule with the specified method name.  The
+     * "set parent" method's argument type is assumed to be the class of the
+     * parent object.
+     *
+     * @param methodName Method name of the "set parent" method to call
+     */
+    public SetTopRule(String methodName) {
+
+        this(methodName, null);
+
+    }
+
+
+    /**
+     * Construct a "set parent" rule with the specified method name.
+     *
+     * @param methodName Method name of the "set parent" method to call
+     * @param paramType Java class of the "set parent" method's argument
+     *  (if you wish to use a primitive type, specify the corresonding
+     *  Java wrapper class instead, such as <code>java.lang.Boolean</code>
+     *  for a <code>boolean</code> parameter)
+     */
+    public SetTopRule(String methodName,
+                      String paramType) {
+
+        this.methodName = methodName;
+        this.paramType = paramType;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The method name to call on the child object.
+     */
+    protected String methodName = null;
+
+
+    /**
+     * The Java class name of the parameter type expected by the method.
+     */
+    protected String paramType = null;
+    
+    /**
+     * Should we use exact matching. Default is no.
+     */
+    protected boolean useExactMatch = false;
+
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * <p>Is exact matching being used.</p>
+     *
+     * <p>This rule uses <code>org.apache.commons.beanutils.MethodUtils</code> 
+     * to introspect the relevent objects so that the right method can be called.
+     * Originally, <code>MethodUtils.invokeExactMethod</code> was used.
+     * This matches methods very strictly 
+     * and so may not find a matching method when one exists.
+     * This is still the behaviour when exact matching is enabled.</p>
+     *
+     * <p>When exact matching is disabled, <code>MethodUtils.invokeMethod</code> is used.
+     * This method finds more methods but is less precise when there are several methods 
+     * with correct signatures.
+     * So, if you want to choose an exact signature you might need to enable this property.</p>
+     *
+     * <p>The default setting is to disable exact matches.</p>
+     *
+     * @return true iff exact matching is enabled
+     * @since Digester Release 1.1.1
+     */
+    public boolean isExactMatch() {
+    
+        return useExactMatch;
+    }
+    
+    /**
+     * <p>Set whether exact matching is enabled.</p>
+     *
+     * <p>See {@link #isExactMatch()}.</p>
+     *
+     * @param useExactMatch should this rule use exact method matching
+     * @since Digester Release 1.1.1
+     */
+    public void setExactMatch(boolean useExactMatch) {
+
+        this.useExactMatch = useExactMatch;
+    }
+    
+    /**
+     * Process the end of this element.
+     */
+    public void end() throws Exception {
+
+        // Identify the objects to be used
+        Object child = digester.peek(0);
+        Object parent = digester.peek(1);
+        
+        if (digester.log.isDebugEnabled()) {
+            if (child == null) {
+                digester.log.debug("[SetTopRule]{" + digester.match +
+                        "} Call [NULL CHILD]." +
+                        methodName + "(" + parent + ")");
+            } else {
+                digester.log.debug("[SetTopRule]{" + digester.match +
+                        "} Call " + child.getClass().getName() + "." +
+                        methodName + "(" + parent + ")");
+            }
+        }
+
+        // Call the specified method
+        IntrospectionUtils.callMethod1(child, methodName,
+                parent, paramType, digester.getClassLoader());
+
+    }
+
+
+    /**
+     * Render a printable version of this Rule.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("SetTopRule[");
+        sb.append("methodName=");
+        sb.append(methodName);
+        sb.append(", paramType=");
+        sb.append(paramType);
+        sb.append("]");
+        return (sb.toString());
+
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/WithDefaultsRulesWrapper.java b/connectors/util/java/org/apache/tomcat/util/digester/WithDefaultsRulesWrapper.java
new file mode 100644
index 0000000..7e495d7
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/WithDefaultsRulesWrapper.java
@@ -0,0 +1,163 @@
+/* $Id$
+ *
+ * Copyright 2003-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+package org.apache.tomcat.util.digester;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * <p><code>Rules</code> <em>Decorator</em> that returns default rules 
+ * when no matches are returned by the wrapped implementation.</p>
+ *
+ * <p>This allows default <code>Rule</code> instances to be added to any 
+ * existing <code>Rules</code> implementation. These default <code>Rule</code> 
+ * instances will be returned for any match for which the wrapped 
+ * implementation does not return any matches.</p>
+ * <p> For example,
+ * <pre>
+ *   Rule alpha;
+ *   ...
+ *   WithDefaultsRulesWrapper rules = new WithDefaultsRulesWrapper(new BaseRules());
+ *   rules.addDefault(alpha);
+ *   ...
+ *   digester.setRules(rules);
+ *   ...
+ * </pre>
+ * when a pattern does not match any other rule, then rule alpha will be called.
+ * </p>
+ * <p><code>WithDefaultsRulesWrapper</code> follows the <em>Decorator</em> pattern.</p>
+ *
+ * @since 1.6
+ */
+
+public class WithDefaultsRulesWrapper implements Rules {
+
+    // --------------------------------------------------------- Fields
+    
+    /** The Rules implementation that this class wraps. */
+    private Rules wrappedRules;
+    /** Rules to be fired when the wrapped implementations returns none. */
+    private List defaultRules = new ArrayList();
+    /** All rules (preserves order in which they were originally added) */
+    private List allRules = new ArrayList();
+    
+    // --------------------------------------------------------- Constructor
+    
+    /** 
+     * Base constructor.
+     *
+     * @param wrappedRules the wrapped <code>Rules</code> implementation, not null
+     * @throws IllegalArgumentException when <code>wrappedRules</code> is null
+     */
+    public WithDefaultsRulesWrapper(Rules wrappedRules) {
+        if (wrappedRules == null) {
+            throw new IllegalArgumentException("Wrapped rules must not be null");
+        }
+        this.wrappedRules = wrappedRules;
+    }
+
+    // --------------------------------------------------------- Properties
+    
+    /** Gets digester using these Rules */
+    public Digester getDigester() {
+        return wrappedRules.getDigester();
+    }
+    
+    /** Sets digeseter using these Rules */
+    public void setDigester(Digester digester) {
+        wrappedRules.setDigester(digester);
+        Iterator it = defaultRules.iterator();
+        while (it.hasNext()) {
+            Rule rule = (Rule) it.next();
+            rule.setDigester(digester);
+        }
+    }
+    
+    /** Gets namespace to apply to Rule's added */
+    public String getNamespaceURI() {
+        return wrappedRules.getNamespaceURI();
+    }
+    
+    /** Sets namespace to apply to Rule's added subsequently */
+    public void setNamespaceURI(String namespaceURI) {
+        wrappedRules.setNamespaceURI(namespaceURI);
+    }
+    
+    /** Gets Rule's which will be fired when the wrapped implementation returns no matches */
+    public List getDefaults() {
+        return defaultRules;
+    }	
+    
+    // --------------------------------------------------------- Public Methods
+    
+    public List match(String pattern) {
+        return match("", pattern);
+    }	
+    
+    /**
+     * Return list of rules matching given pattern.
+     * If wrapped implementation returns any matches return those.
+     * Otherwise, return default matches.
+     */
+    public List match(String namespaceURI, String pattern) {
+        List matches = wrappedRules.match(namespaceURI, pattern);
+        if (matches ==  null || matches.isEmpty()) {	
+            // a little bit of defensive programming
+            return new ArrayList(defaultRules);
+        }
+        // otherwise
+        return matches;
+    }
+    
+    /** Adds a rule to be fired when wrapped implementation returns no matches */
+    public void addDefault(Rule rule) {
+        // set up rule
+        if (wrappedRules.getDigester() != null) {
+            rule.setDigester(wrappedRules.getDigester());
+        }
+        
+        if (wrappedRules.getNamespaceURI() != null) {
+            rule.setNamespaceURI(wrappedRules.getNamespaceURI());
+        }
+        
+        defaultRules.add(rule);
+        allRules.add(rule);
+    }
+    
+    /** Gets all rules */
+    public List rules() {
+        return allRules;
+    }
+    
+    /** Clears all Rule's */
+    public void clear() {
+        wrappedRules.clear();
+        allRules.clear();
+        defaultRules.clear();
+    }
+    
+    /** 
+     * Adds a Rule to be fired on given pattern.
+     * Pattern matching is delegated to wrapped implementation.
+     */
+    public void add(String pattern, Rule rule) {
+        wrappedRules.add(pattern, rule);
+        allRules.add(rule);
+    }	
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/XercesParser.java b/connectors/util/java/org/apache/tomcat/util/digester/XercesParser.java
new file mode 100644
index 0000000..5a70b8c
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/XercesParser.java
@@ -0,0 +1,189 @@
+/* $Id$
+ *
+ * Copyright 2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+
+
+package org.apache.tomcat.util.digester;
+
+import java.lang.reflect.Method;
+import java.util.Properties;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+
+/**
+ * Create a <code>SAXParser</code> based on the underlying Xerces version.
+ * Currently, Xerces 2.3 and up doesn't implement schema validation the same way
+ * 2.1 was. In other to support schema validation in a portable way between 
+ * parser, some features/properties need to be set.
+ *
+ * @since 1.6
+ */
+
+public class XercesParser{
+
+    /**
+     * The Log to which all SAX event related logging calls will be made.
+     */
+    protected static Log log =
+        LogFactory.getLog("org.apache.commons.digester.Digester.sax");
+
+
+    /**
+     * The JAXP 1.2 property required to set up the schema location.
+     */
+    private static final String JAXP_SCHEMA_SOURCE =
+        "http://java.sun.com/xml/jaxp/properties/schemaSource";
+
+
+    /**
+     * The JAXP 1.2 property to set up the schemaLanguage used.
+     */
+    protected static String JAXP_SCHEMA_LANGUAGE =
+        "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
+
+
+    /**
+     * Xerces dynamic validation property
+     */
+    protected static String XERCES_DYNAMIC = 
+        "http://apache.org/xml/features/validation/dynamic";
+
+
+    /**
+     * Xerces schema validation property
+     */
+    protected static String XERCES_SCHEMA =
+        "http://apache.org/xml/features/validation/schema";
+
+
+    /**
+     * A <code>float</code> representing the underlying Xerces version
+     */
+    protected static float version;
+
+
+    /**
+     * The current Xerces version.
+     */
+    protected static String versionNumber = null;
+
+
+    /**
+     * Return the current Xerces version.
+     * @return the current Xerces version.
+     */
+    private static String getXercesVersion() {
+        // If for some reason we can't get the version, set it to 1.0.
+        String versionNumber = "1.0";
+        try{
+            // Use reflection to avoid a build dependency with Xerces.
+            Class versionClass = 
+                            Class.forName("org.apache.xerces.impl.Version");
+            // Will return Xerces-J 2.x.0
+            Method method = 
+                versionClass.getMethod("getVersion", (Class[]) null); 
+            String version = (String)method.invoke(null, (Object[]) null);
+            versionNumber = version.substring( "Xerces-J".length() , 
+                                               version.lastIndexOf(".") ); 
+        } catch (Exception ex){
+            // Do nothing.
+        }
+        return versionNumber;
+    }
+
+
+    /**
+     * Create a <code>SAXParser</code> based on the underlying
+     * <code>Xerces</code> version.
+     * @param properties parser specific properties/features
+     * @return an XML Schema/DTD enabled <code>SAXParser</code>
+     */
+    public static SAXParser newSAXParser(Properties properties) 
+            throws ParserConfigurationException, 
+                   SAXException,
+                   SAXNotSupportedException {
+
+        SAXParserFactory factory =  
+                        (SAXParserFactory)properties.get("SAXParserFactory");
+
+        if (versionNumber == null){
+            versionNumber = getXercesVersion();
+            version = new Float( versionNumber ).floatValue();
+        }
+
+        // Note: 2.2 is completely broken (with XML Schema). 
+        if (version > 2.1) {
+
+            configureXerces(factory);
+            return factory.newSAXParser();
+        } else {
+            SAXParser parser = factory.newSAXParser();
+            configureOldXerces(parser,properties);
+            return parser;
+        }
+    }
+
+
+    /**
+     * Configure schema validation as recommended by the JAXP 1.2 spec.
+     * The <code>properties</code> object may contains information about
+     * the schema local and language. 
+     * @param properties parser optional info
+     */
+    private static void configureOldXerces(SAXParser parser, 
+                                           Properties properties) 
+            throws ParserConfigurationException, 
+                   SAXNotSupportedException {
+
+        String schemaLocation = (String)properties.get("schemaLocation");
+        String schemaLanguage = (String)properties.get("schemaLanguage");
+
+        try{
+            if (schemaLocation != null) {
+                parser.setProperty(JAXP_SCHEMA_LANGUAGE, schemaLanguage);
+                parser.setProperty(JAXP_SCHEMA_SOURCE, schemaLocation);
+            }
+        } catch (SAXNotRecognizedException e){
+            log.info(parser.getClass().getName() + ": " 
+                                        + e.getMessage() + " not supported."); 
+        }
+
+    }
+
+
+    /**
+     * Configure schema validation as recommended by the Xerces spec. 
+     * Both DTD and Schema validation will be enabled simultaneously.
+     * @param factory SAXParserFactory to be configured
+     */
+    private static void configureXerces(SAXParserFactory factory)
+            throws ParserConfigurationException, 
+                   SAXNotRecognizedException, 
+                   SAXNotSupportedException {
+
+        factory.setFeature(XERCES_DYNAMIC, true);
+        factory.setFeature(XERCES_SCHEMA, true);
+
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/digester/package.html b/connectors/util/java/org/apache/tomcat/util/digester/package.html
new file mode 100644
index 0000000..c14e98e
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/digester/package.html
@@ -0,0 +1,1275 @@
+<html>
+<head>
+<title>Package Documentation for org.apache.commons.digester Package</title>
+</head>
+<body bgcolor="white">
+The Digester package provides for rules-based processing of arbitrary
+XML documents.
+<br><br>
+<a name="doc.Description"></a>
+<div align="center">
+<a href="#doc.Depend">[Dependencies]</a>
+<a href="#doc.Intro">[Introduction]</a>
+<a href="#doc.Properties">[Configuration Properties]</a>
+<a href="#doc.Stack">[The Object Stack]</a>
+<a href="#doc.Patterns">[Element Matching Patterns]</a>
+<a href="#doc.Rules">[Processing Rules]</a>
+<a href="#doc.Logging">[Logging]</a>
+<a href="#doc.Usage">[Usage Example]</a>
+<a href="#doc.Namespace">[Namespace Aware Parsing]</a>
+<a href="#doc.Pluggable">[Pluggable Rules Processing]</a>
+<a href="#doc.RuleSets">[Encapsulated Rule Sets]</a>
+<a href="#doc.NamedStacks">[Using Named Stacks For Inter-Rule Communication]</a>
+<a href="#doc.RegisteringDTDs">[Registering DTDs]</a>
+<a href="#doc.troubleshooting">[Troubleshooting]</a>
+<a href="#doc.FAQ">[FAQ]</a>
+<a href="#doc.Limits">[Known Limitations]</a>
+</div>
+
+<a name="doc.Depend"></a>
+<h3>External Dependencies</h3>
+
+<ul>
+  <li>An XML parser conforming to
+    <a href="http://java.sun.com/products/xml">JAXP
+    </a>, version 1.1 or later (the first one to support SAX 2.0)
+  </li>
+  <li>
+    <a href="http://jakarta.apache.org/builds/jakarta-commons/release/commons-beanutils">
+Beanutils Package (Jakarta Commons)</a>, version 1.5 or later
+  </li>
+  <li>
+    <a href="http://jakarta.apache.org/builds/jakarta-commons/release/commons-collections">
+Collections Package (Jakarta Commons)</a>, version 2.1 or later
+  </li>
+  <li>
+    <a href="http://jakarta.apache.org/builds/jakarta-commons/release/commons-logging">
+Commons Logging Package (Jakarta Commons)</a>, version 1.0.2 or later
+  </li>
+</ul>
+
+<a name="doc.Intro"></a>
+<h3>Introduction</h3>
+
+<p>In many application environments that deal with XML-formatted data, it is
+useful to be able to process an XML document in an "event driven" manner,
+where particular Java objects are created (or methods of existing objects
+are invoked) when particular patterns of nested XML elements have been
+recognized.  Developers familiar with the Simple API for XML Parsing (SAX)
+approach to processing XML documents will recognize that the Digester provides
+a higher level, more developer-friendly interface to SAX events, because most
+of the details of navigating the XML element hierarchy are hidden -- allowing
+the developer to focus on the processing to be performed.</p>
+
+<p>In order to use a Digester, the following basic steps are required:</p>
+<ul>
+<li>Create a new instance of the
+    <code>org.apache.commons.digester.Digester</code> class.  Previously
+    created Digester instances may be safely reused, as long as you have
+    completed any previously requested parse, and you do not try to utilize
+    a particular Digester instance from more than one thread at a time.</li>
+<li>Set any desired <a href="#doc.Properties">configuration properties</a>
+    that will customize the operation of the Digester when you next initiate
+    a parse operation.</li>
+<li>Optionally, push any desired initial object(s) onto the Digester's
+    <a href="#doc.Stack">object stack</a>.</li>
+<li>Register all of the <a href="#doc.Patterns">element matching patterns</a>
+    for which you wish to have <a href="#doc.Rules">processing rules</a>
+    fired when this pattern is recognized in an input document.  You may
+    register as many rules as you like for any particular pattern.  If there
+    is more than one rule for a given pattern, the rules will be executed in
+    the order that they were listed.</li>
+<li>Call the <code>digester.parse()</code> method, passing a reference to the
+    XML document to be parsed in one of a variety of forms.  See the
+    <a href="Digester.html#parse(java.io.File)">Digester.parse()</a>
+    documentation for details.  Note that you will need to be prepared to
+    catch any <code>IOException</code> or <code>SAXException</code> that is
+    thrown by the parser, or any runtime expression that is thrown by one of
+    the processing rules.</li>
+</ul>
+
+<p>For example code, see <a href="#doc.Usage"> the usage 
+examples</a>, and <a href="#doc.FAQ.Examples"> the FAQ </a>. </p>
+
+<a name="doc.Properties"></a>
+<h3>Digester Configuration Properties</h3>
+
+<p>A <code>org.apache.commons.digester.Digester</code> instance contains several
+configuration properties that can be used to customize its operation.  These
+properties <strong>must</strong> be configured before you call one of the
+<code>parse()</code> variants, in order for them to take effect on that
+parse.</p>
+
+<blockquote>
+  <table border="1">
+    <tr>
+      <th width="15%">Property</th>
+      <th width="85%">Description</th>
+    </tr>
+    <tr>
+      <td align="center">classLoader</td>
+      <td>You can optionally specify the class loader that will be used to
+          load classes when required by the <code>ObjectCreateRule</code>
+          and <code>FactoryCreateRule</code> rules.  If not specified,
+          application classes will be loaded from the thread's context
+          class loader (if the <code>useContextClassLoader</code> property
+          is set to <code>true</code>) or the same class loader that was
+          used to load the <code>Digester</code> class itself.</td>
+    </tr>
+    <tr>
+      <td align="center">errorHandler</td>
+      <td>You can optionally specify a SAX <code>ErrorHandler</code> that
+          is notified when parsing errors occur.  By default, any parsing
+          errors that are encountered are logged, but Digester will continue
+          processing as well.</td>
+    </tr>
+    <tr>
+      <td align="center">namespaceAware</td>
+      <td>A boolean that is set to <code>true</code> to perform parsing in a
+          manner that is aware of XML namespaces.  Among other things, this
+          setting affects how elements are matched to processing rules.  See
+          <a href="#doc.Namespace">Namespace Aware Parsing</a> for more
+          information.</td>
+    </tr>
+    <tr>
+      <td align="center">ruleNamespaceURI</td>
+      <td>The public URI of the namespace for which all subsequently added
+          rules are associated, or <code>null</code> for adding rules that
+          are not associated with any namespace.  See
+          <a href="#doc.Namespace">Namespace Aware Parsing</a> for more
+          information.</td>
+    </tr>
+    <tr>
+      <td align="center">rules</td>
+      <td>The <code>Rules</code> component that actually performs matching of
+          <code>Rule</code> instances against the current element nesting
+          pattern is pluggable.  By default, Digester includes a
+          <code>Rules</code> implementation that behaves as described in this
+          document.  See
+          <a href="#doc.Pluggable">Pluggable Rules Processing</a> for
+          more information.</td>
+    </tr>
+    <tr>
+      <td align="center">useContextClassLoader</code>
+      <td>A boolean that is set to <code>true</code> if you want application
+          classes required by <code>FactoryCreateRule</code> and
+          <code>ObjectCreateRule</code> to be loaded from the context class
+          loader of the current thread.  By default, classes will be loaded
+          from the class loader that loaded this <code>Digester</code> class.
+          <strong>NOTE</strong> - This property is ignored if you set a
+          value for the <code>classLoader</code> property; that class loader
+          will be used unconditionally.</td>
+    </tr>
+    <tr>
+      <td align="center">validating</td>
+      <td>A boolean that is set to <code>true</code> if you wish to validate
+          the XML document against a Document Type Definition (DTD) that is
+          specified in its <code>DOCTYPE</code> declaration.  The default
+          value of <code>false</code> requests a parse that only detects
+          "well formed" XML documents, rather than "valid" ones.</td>
+    </tr>
+  </table>
+</blockquote>
+
+<p>In addition to the scalar properties defined above, you can also register
+a local copy of a Document Type Definition (DTD) that is referenced in a
+<code>DOCTYPE</code> declaration.  Such a registration tells the XML parser
+that, whenever it encounters a <code>DOCTYPE</code> declaration with the
+specified public identifier, it should utilize the actual DTD content at the
+registered system identifier (a URL), rather than the one in the
+<code>DOCTYPE</code> declaration.</p>
+
+<p>For example, the Struts framework controller servlet uses the following
+registration in order to tell Struts to use a local copy of the DTD for the
+Struts configuration file.  This allows usage of Struts in environments that
+are not connected to the Internet, and speeds up processing even at Internet
+connected sites (because it avoids the need to go across the network).</p>
+
+<pre>
+    URL url = new URL("/org/apache/struts/resources/struts-config_1_0.dtd");
+    digester.register
+      ("-//Apache Software Foundation//DTD Struts Configuration 1.0//EN",
+       url.toString());
+</pre>
+
+<p>As a side note, the system identifier used in this example is the path
+that would be passed to <code>java.lang.ClassLoader.getResource()</code>
+or <code>java.lang.ClassLoader.getResourceAsStream()</code>.  The actual DTD
+resource is loaded through the same class loader that loads all of the Struts
+classes -- typically from the <code>struts.jar</code> file.</p>
+
+<a name="doc.Stack"></a>
+<h3>The Object Stack</h3>
+
+<p>One very common use of <code>org.apache.commons.digester.Digester</code>
+technology is to dynamically construct a tree of Java objects, whose internal
+organization, as well as the details of property settings on these objects,
+are configured based on the contents of the XML document.  In fact, the
+primary reason that the Digester package was created (it was originally part
+of Struts, and then moved to the Commons project because it was recognized
+as being generally useful) was to facilitate the
+way that the Struts controller servlet configures itself based on the contents
+of your application's <code>struts-config.xml</code> file.</p>
+
+<p>To facilitate this usage, the Digester exposes a stack that can be
+manipulated by processing rules that are fired when element matching patterns
+are satisfied.  The usual stack-related operations are made available,
+including the following:</p>
+<ul>
+<li><a href="Digester.html#clear()">clear()</a> - Clear the current contents
+    of the object stack.</li>
+<li><a href="Digester.html#peek()">peek()</a> - Return a reference to the top
+    object on the stack, without removing it.</li>
+<li><a href="Digester.html#pop()">pop()</a> - Remove the top object from the
+    stack and return it.</li>
+<li><a href="Digester.html#push(java.lang.Object)">push()</a> - Push a new
+    object onto the top of the stack.</li>
+</ul>
+
+<p>A typical design pattern, then, is to fire a rule that creates a new object
+and pushes it on the stack when the beginning of a particular XML element is
+encountered. The object will remain there while the nested content of this
+element is processed, and it will be popped off when the end of the element
+is encountered.  As we will see, the standard "object create" processing rule
+supports exactly this functionalility in a very convenient way.</p>
+
+<p>Several potential issues with this design pattern are addressed by other
+features of the Digester functionality:</p>
+<ul>
+<li><em>How do I relate the objects being created to each other?</em> - The
+    Digester supports standard processing rules that pass the top object on
+    the stack as an argument to a named method on the next-to-top object on
+    the stack (or vice versa).  This rule makes it easy to establish
+    parent-child relationships between these objects.  One-to-one and
+    one-to-many relationships are both easy to construct.</li>
+<li><em>How do I retain a reference to the first object that was created?</em>
+    As you review the description of what the "object create" processing rule
+    does, it would appear that the first object you create (i.e. the object
+    created by the outermost XML element you process) will disappear from the
+    stack by the time that XML parsing is completed, because the end of the
+    element would have been encountered.  However, Digester will maintain a
+    reference to the very first object ever pushed onto the object stack,
+    and will return it to you
+    as the return value from the <code>parse()</code> call.  Alternatively,
+    you can push a reference to some application object onto the stack before
+    calling <code>parse()</code>, and arrange that a parent-child relationship
+    be created (by appropriate processing rules) between this manually pushed
+    object and the ones that are dynamically created.  In this way,
+    the pushed object will retain a reference to the dynamically created objects
+    (and therefore all of their children), and will be returned to you after
+    the parse finishes as well.</li>
+</ul>
+
+<a name="doc.Patterns"></a>
+<h3>Element Matching Patterns</h3>
+
+<p>A primary feature of the <code>org.apache.commons.digester.Digester</code>
+parser is that the Digester automatically navigates the element hierarchy of
+the XML document you are parsing for you, without requiring any developer
+attention to this process.  Instead, you focus on deciding what functions you
+would like to have performed whenver a certain arrangement of nested elements
+is encountered in the XML document being parsed.  The mechanism for specifying
+such arrangements are called <em>element matching patterns</em>.
+
+<p>A very simple element matching pattern is a simple string like "a".  This
+pattern is matched whenever an <code>&lt;a&gt;</code> top-level element is
+encountered in the XML document, no matter how many times it occurs.  Note that
+nested <code>&lt;a&gt;</code> elements will <strong>not</strong> match this
+pattern -- we will describe means to support this kind of matching later.</li>
+
+<p>The next step up in matching pattern complexity is "a/b".  This pattern will
+be matched when a <code>&lt;b&gt;</code> element is found nested inside a
+top-level <code>&lt;a&gt;</code> element.  Again, this match can occur as many
+times as desired, depending on the content of the XML document being parsed.
+You can use multiple slashes to define a hierarchy of any desired depth that
+will be matched appropriately.</p>
+
+<p>For example, assume you have registered processing rules that match patterns
+"a", "a/b", and "a/b/c".  For an input XML document with the following
+contents, the indicated patterns will be matched when the corresponding element
+is parsed:</p>
+<pre>
+  &lt;a&gt;         -- Matches pattern "a"
+    &lt;b&gt;       -- Matches pattern "a/b"
+      &lt;c/&gt;    -- Matches pattern "a/b/c"
+      &lt;c/&gt;    -- Matches pattern "a/b/c"
+    &lt;/b&gt;
+    &lt;b&gt;       -- Matches pattern "a/b"
+      &lt;c/&gt;    -- Matches pattern "a/b/c"
+      &lt;c/&gt;    -- Matches pattern "a/b/c"
+      &lt;c/&gt;    -- Matches pattern "a/b/c"
+    &lt;/b&gt;
+  &lt;/a&gt;
+</pre>
+
+<p>It is also possible to match a particular XML element, no matter how it is
+nested (or not nested) in the XML document, by using the "*" wildcard character
+in your matching pattern strings.  For example, an element matching pattern
+of "*/a" will match an <code>&lt;a&gt;</code> element at any nesting position
+within the document.</p>
+
+<p>It is quite possible that, when a particular XML element is being parsed,
+the pattern for more than one registered processing rule will be matched
+ either because you registered more than one processing rule with the same
+matching pattern, or because one more more exact pattern matches and wildcard
+pattern matches are satisfied by the same element.</p>
+
+<p>When this occurs, the corresponding processing rules will all be fired in order. 
+<code>begin</code> (and <code>body</code>) method calls are executed in the 
+order that the <code>Rules</code> where initially registered with the 
+<code>Digester</code>, whilst <code>end</code> method calls are execute in 
+reverse order. In other words - the order is first in, last out.</p>
+
+<a name="doc.Rules"></a>
+<h3>Processing Rules</h3>
+
+<p>The <a href="#doc.Patterns">previous section</a> documented how you identify
+<strong>when</strong> you wish to have certain actions take place.  The purpose
+of processing rules is to define <strong>what</strong> should happen when the
+patterns are matched.</p>
+
+<p>Formally, a processing rule is a Java class that subclasses the
+<a href="Rule.html">org.apache.commons.digester.Rule</a> interface.  Each Rule
+implements one or more of the following event methods that are called at
+well-defined times when the matching patterns corresponding to this rule
+trigger it:</p>
+<ul>
+<li><a href="Rule.html#begin(org.xml.sax.AttributeList)">begin()</a> -
+    Called when the beginning of the matched XML element is encountered.  A
+    data structure containing all of the attributes corresponding to this
+    element are passed as well.</li>
+<li><a href="Rule.html#body(java.lang.String)">body()</a> -
+    Called when nested content (that is not itself XML elements) of the
+    matched element is encountered.  Any leading or trailing whitespace will
+    have been removed as part of the parsing process.</li>
+<li><a href="Rule.html#end()">end()</a> - Called when the ending of the matched
+    XML element is encountered.  If nested XML elements that matched other
+    processing rules was included in the body of this element, the appropriate
+    processing rules for the matched rules will have already been completed
+    before this method is called.</li>
+<li><a href="Rule.html#finish()">finish()</a> - Called when the parse has
+    been completed, to give each rule a chance to clean up any temporary data
+    they might have created and cached.</li>
+</ul>
+
+<p>As you are configuring your digester, you can call the
+<code>addRule()</code> method to register a specific element matching pattern,
+along with an instance of a <code>Rule</code> class that will have its event
+handling methods called at the appropriate times, as described above.  This
+mechanism allows you to create <code>Rule</code> implementation classes
+dynamically, to implement any desired application specific functionality.</p>
+
+<p>In addition, a set of processing rule implementation classes are provided,
+which deal with many common programming scenarios.  These classes include the
+following:</p>
+<ul>
+<li><a href="ObjectCreateRule.html">ObjectCreateRule</a> - When the
+    <code>begin()</code> method is called, this rule instantiates a new
+    instance of a specified Java class, and pushes it on the stack.  The
+    class name to be used is defaulted according to a parameter passed to
+    this rule's constructor, but can optionally be overridden by a classname
+    passed via the specified attribute to the XML element being processed.
+    When the <code>end()</code> method is called, the top object on the stack
+    (presumably, the one we added in the <code>begin()</code> method) will
+    be popped, and any reference to it (within the Digester) will be
+    discarded.</li>
+<li><a href="FactoryCreateRule.html">FactoryCreateRule</a> - A variation of
+    <code>ObjectCreateRule</code> that is useful when the Java class with
+    which you wish to create an object instance does not have a no-arguments
+    constructor, or where you wish to perform other setup processing before
+    the object is handed over to the Digester.</li>
+<li><a href="SetPropertiesRule.html">SetPropertiesRule</a> - When the
+    <code>begin()</code> method is called, the digester uses the standard
+    Java Reflection API to identify any JavaBeans property setter methods
+    (on the object at the top of the digester's stack)
+    who have property names that match the attributes specified on this XML
+    element, and then call them individually, passing the corresponding
+    attribute values. These natural mappings can be overridden. This allows
+    (for example) a <code>class</code> attribute to be mapped correctly.
+    It is recommended that this feature should not be overused - in most cases,
+    it's better to use the standard <code>BeanInfo</code> mechanism.
+    A very common idiom is to define an object create
+    rule, followed by a set properties rule, with the same element matching
+    pattern.  This causes the creation of a new Java object, followed by
+    "configuration" of that object's properties based on the attributes
+    of the same XML element that created this object.</li>
+<li><a href="SetPropertyRule.html">SetPropertyRule</a> - When the
+    <code>begin()</code> method is called, the digester calls a specified
+    property setter (where the property itself is named by an attribute)
+    with a specified value (where the value is named by another attribute),
+    on the object at the top of the digester's stack.
+    This is useful when your XML file conforms to a particular DTD, and
+    you wish to configure a particular property that does not have a
+    corresponding attribute in the DTD.</li>
+<li><a href="SetNextRule.html">SetNextRule</a> - When the
+    <code>end()</code> method is called, the digester analyzes the
+    next-to-top element on the stack, looking for a property setter method
+    for a specified property.  It then calls this method, passing the object
+    at the top of the stack as an argument.  This rule is commonly used to
+    establish one-to-many relationships between the two objects, with the
+    method name commonly being something like "addChild".</li>
+<li><a href="SetTopRule.html">SetTopRule</a> - When the
+    <code>end()</code> method is called, the digester analyzes the
+    top element on the stack, looking for a property setter method for a
+    specified property.  It then calls this method, passing the next-to-top
+    object on the stack as an argument.  This rule would be used as an
+    alternative to a SetNextRule, with a typical method name "setParent",
+    if the API supported by your object classes prefers this approach.</li>
+<li><a href="CallMethodRule.html">CallMethodRule</a> - This rule sets up a
+    method call to a named method of the top object on the digester's stack,
+    which will actually take place when the <code>end()</code> method is
+    called.  You configure this rule by specifying the name of the method
+    to be called, the number of arguments it takes, and (optionally) the
+    Java class name(s) defining the type(s) of the method's arguments.
+    The actual parameter values, if any, will typically be accumulated from
+    the body content of nested elements within the element that triggered
+    this rule, using the CallParamRule discussed next.</li>
+<li><a href="CallParamRule.html">CallParamRule</a> - This rule identifies
+    the source of a particular numbered (zero-relative) parameter for a
+    CallMethodRule within which we are nested.  You can specify that the
+    parameter value be taken from a particular named attribute, or from the
+    nested body content of this element.</li>
+<li><a href="NodeCreateRule.html">NodeCreateRule</a> - A specialized rule
+    that converts part of the tree into a <code>DOM Node</code> and then
+    pushes it onto the stack.</li>
+</ul>
+
+<p>You can create instances of the standard <code>Rule</code> classes and
+register them by calling <code>digester.addRule()</code>, as described above.
+However, because their usage is so common, shorthand registration methods are
+defined for each of the standard rules, directly on the <code>Digester</code>
+class.  For example, the following code sequence:</p>
+<pre>
+    Rule rule = new SetNextRule(digester, "addChild",
+                                "com.mycompany.mypackage.MyChildClass");
+    digester.addRule("a/b/c", rule);
+</pre>
+<p>can be replaced by:</p>
+<pre>
+    digester.addSetNext("a/b/c", "addChild",
+                        "com.mycompany.mypackage.MyChildClass");
+</pre>
+
+<a name="doc.Logging"></a>
+<h3>Logging</h3>
+
+<p>Logging is a vital tool for debugging Digester rulesets. Digester can log
+copious amounts of debugging information. So, you need to know how logging
+works before you start using Digester seriously.</p>
+
+<p>Digester uses
+<a href="http://jakarta.apache.org/commons/logging.html">Jakarta Commons
+Logging</a>.  This component is not really a logging framework - rather
+an extensible, configurable bridge. It can be configured to swallow all log 
+messages, to provide very basic logging by itself or to pass logging messages
+on to more sophisticated logging frameworks. Commons-Logging comes with
+connectors for many popular logging frameworks. Consult the commons-logging
+documentation for more information.</p>
+
+<p>Two main logs are used by Digester:</p>
+<ul>
+<li>SAX-related messages are logged to
+    <strong><code>org.apache.commons.digester.Digester.sax</code></strong>.
+    This log gives information about the basic SAX events received by
+    Digester.</li>
+<li><strong><code>org.apache.commons.digester.Digester</code></strong> is used
+    for everything else. You'll probably want to have this log turned up during
+    debugging but turned down during production due to the high message
+    volume.</li>
+</ul>
+
+<p>Complete documentation of how to configure Commons-Logging can be found
+in the Commons Logging package documentation.  However, as a simple example,
+let's assume that you want to use the <code>SimpleLog</code> implementation
+that is included in Commons-Logging, and set up Digester to log events from
+the <code>Digester</code> logger at the DEBUG level, while you want to log
+events from the <code>Digester.log</code> logger at the INFO level.  You can
+accomplish this by creating a <code>commons-logging.properties</code> file
+in your classpath (or setting corresponding system properties on the command
+line that starts your application) with the following contents:</p>
+<pre>
+  org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
+  org.apache.commons.logging.simplelog.log.org.apache.commons.digester.Digester=debug
+  org.apache.commons.logging.simplelog.log.org.apache.commons.digester.Digester.sax=info
+</pre>
+
+<a name="doc.Usage"></a>
+<h3>Usage Examples</h3>
+
+
+<h5>Creating a Simple Object Tree</h5>
+
+<p>Let's assume that you have two simple JavaBeans, <code>Foo</code> and
+<code>Bar</code>, with the following method signatures:</p>
+<pre>
+  package mypackage;
+  public class Foo {
+    public void addBar(Bar bar);
+    public Bar findBar(int id);
+    public Iterator getBars();
+    public String getName();
+    public void setName(String name);
+  }
+
+  public mypackage;
+  public class Bar {
+    public int getId();
+    public void setId(int id);
+    public String getTitle();
+    public void setTitle(String title);
+  }
+</pre>
+
+<p>and you wish to use Digester to parse the following XML document:</p>
+
+<pre>
+  &lt;foo name="The Parent"&gt;
+    &lt;bar id="123" title="The First Child"/&gt;
+    &lt;bar id="456" title="The Second Child"/&gt;
+  &lt;/foo&gt;
+</pre>
+
+<p>A simple approach will be to use the following Digester in the following way
+to set up the parsing rules, and then process an input file containing this
+document:</p>
+
+<pre>
+  Digester digester = new Digester();
+  digester.setValidating(false);
+  digester.addObjectCreate("foo", "mypackage.Foo");
+  digester.addSetProperties("foo");
+  digester.addObjectCreate("foo/bar", "mypackage.Bar");
+  digester.addSetProperties("foo/bar");
+  digester.addSetNext("foo/bar", "addBar", "mypackage.Bar");
+  Foo foo = (Foo) digester.parse();
+</pre>
+
+<p>In order, these rules do the following tasks:</p>
+<ol>
+<li>When the outermost <code>&lt;foo&gt;</code> element is encountered,
+    create a new instance of <code>mypackage.Foo</code> and push it
+    on to the object stack.  At the end of the <code>&lt;foo&gt;</code>
+    element, this object will be popped off of the stack.</li>
+<li>Cause properties of the top object on the stack (i.e. the <code>Foo</code>
+    object that was just created and pushed) to be set based on the values
+    of the attributes of this XML element.</li>
+<li>When a nested <code>&lt;bar&gt;</code> element is encountered,
+    create a new instance of <code>mypackage.Bar</code> and push it
+    on to the object stack.  At the end of the <code>&lt;bar&gt;</code>
+    element, this object will be popped off of the stack (i.e. after the
+    remaining rules matching <code>foo/bar</code> are processed).</li>
+<li>Cause properties of the top object on the stack (i.e. the <code>Bar</code>
+    object that was just created and pushed) to be set based on the values
+    of the attributes of this XML element.  Note that type conversions
+    are automatically performed (such as String to int for the <code>id</code>
+    property), for all converters registered with the <code>ConvertUtils</code>
+    class from <code>commons-beanutils</code> package.</li>
+<li>Cause the <code>addBar</code> method of the next-to-top element on the
+    object stack (which is why this is called the "set <em>next</em>" rule)
+    to be called, passing the element that is on the top of the stack, which
+    must be of type <code>mypackage.Bar</code>.  This is the rule that causes
+    the parent/child relationship to be created.</li>
+</ol>
+
+<p>Once the parse is completed, the first object that was ever pushed on to the
+stack (the <code>Foo</code> object in this case) is returned to you.  It will
+have had its properties set, and all of its child <code>Bar</code> objects
+created for you.</p>
+
+
+<h5>Processing A Struts Configuration File</h5>
+
+<p>As stated earlier, the primary reason that the
+<code>Digester</code> package was created is because the
+Struts controller servlet itself needed a robust, flexible, easy to extend
+mechanism for processing the contents of the <code>struts-config.xml</code>
+configuration that describes nearly every aspect of a Struts-based application.
+Because of this, the controller servlet contains a comprehensive, real world,
+example of how the Digester can be employed for this type of a use case.
+See the <code>initDigester()</code> method of class
+<code>org.apache.struts.action.ActionServlet</code> for the code that creates
+and configures the Digester to be used, and the <code>initMapping()</code>
+method for where the parsing actually takes place.</p>
+
+<p>(Struts binary and source distributions can be acquired at
+<a href="http://jakarta.apache.org/struts/">http://jakarta.apache.org/struts/</a>.)</p>
+
+<p>The following discussion highlights a few of the matching patterns and
+processing rules that are configured, to illustrate the use of some of the
+Digester features.  First, let's look at how the Digester instance is
+created and initialized:</p>
+<pre>
+    Digester digester = new Digester();
+    digester.push(this); // Push controller servlet onto the stack
+    digester.setValidating(true);
+</pre>
+
+<p>We see that a new Digester instance is created, and is configured to use
+a validating parser.  Validation will occur against the struts-config_1_0.dtd
+DTD that is included with Struts (as discussed earlier).  In order to provide
+a means of tracking the configured objects, the controller servlet instance
+itself will be added to the digester's stack.</p>
+
+<pre>
+    digester.addObjectCreate("struts-config/global-forwards/forward",
+                             forwardClass, "className");
+    digester.addSetProperties("struts-config/global-forwards/forward");
+    digester.addSetNext("struts-config/global-forwards/forward",
+                        "addForward",
+                        "org.apache.struts.action.ActionForward");
+    digester.addSetProperty
+      ("struts-config/global-forwards/forward/set-property",
+       "property", "value");
+</pre>
+
+<p>The rules created by these lines are used to process the global forward
+declarations.  When a <code>&lt;forward&gt;</code> element is encountered,
+the following actions take place:</p>
+<ul>
+<li>A new object instance is created -- the <code>ActionForward</code>
+    instance that will represent this definition.  The Java class name
+    defaults to that specified as an initialization parameter (which
+    we have stored in the String variable <code>forwardClass</code>), but can
+    be overridden by using the "className" attribute (if it is present in the
+    XML element we are currently parsing).  The new <code>ActionForward</code>
+    instance is pushed onto the stack.</li>
+<li>The properties of the <code>ActionForward</code> instance (at the top of
+    the stack) are configured based on the attributes of the
+    <code>&lt;forward&gt;</code> element.</li>
+<li>Nested occurrences of the <code>&lt;set-property&gt;</code> element
+    cause calls to additional property setter methods to occur.  This is
+    required only if you have provided a custom implementation of the
+    <code>ActionForward</code> class with additional properties that are
+    not included in the DTD.</li>
+<li>The <code>addForward()</code> method of the next-to-top object on
+    the stack (i.e. the controller servlet itself) will be called, passing
+    the object at the top of the stack (i.e. the <code>ActionForward</code>
+    instance) as an argument.  This causes the global forward to be
+    registered, and as a result of this it will be remembered even after
+    the stack is popped.</li>
+<li>At the end of the <code>&lt;forward&gt;</code> element, the top element
+    (i.e. the <code>ActionForward</code> instance) will be popped off the
+    stack.</li>
+</ul>
+
+<p>Later on, the digester is actually executed as follows:</p>
+<pre>
+    InputStream input =
+      getServletContext().getResourceAsStream(config);
+    ...
+    try {
+        digester.parse(input);
+        input.close();
+    } catch (SAXException e) {
+        ... deal with the problem ...
+    }
+</pre>
+
+<p>As a result of the call to <code>parse()</code>, all of the configuration
+information that was defined in the <code>struts-config.xml</code> file is
+now represented as collections of objects cached within the Struts controller
+servlet, as well as being exposed as servlet context attributes.</p>
+
+
+<h5>Parsing Body Text In XML Files</h5>
+
+<p>The Digester module also allows you to process the nested body text in an
+XML file, not just the elements and attributes that are encountered.  The
+following example is based on an assumed need to parse the web application
+deployment descriptor (<code>/WEB-INF/web.xml</code>) for the current web
+application, and record the configuration information for a particular
+servlet.  To record this information, assume the existence of a bean class
+with the following method signatures (among others):</p>
+<pre>
+  package com.mycompany;
+  public class ServletBean {
+    public void setServletName(String servletName);
+    public void setServletClass(String servletClass);
+    public void addInitParam(String name, String value);
+  }
+</pre>
+
+<p>We are going to process the <code>web.xml</code> file that declares the
+controller servlet in a typical Struts-based application (abridged for
+brevity in this example):</p>
+<pre>
+  &lt;web-app&gt;
+    ...
+    &lt;servlet&gt;
+      &lt;servlet-name&gt;action&lt;/servlet-name&gt;
+      &lt;servlet-class&gt;org.apache.struts.action.ActionServlet&lt;servlet-class&gt;
+      &lt;init-param&gt;
+        &lt;param-name&gt;application&lt;/param-name&gt;
+        &lt;param-value&gt;org.apache.struts.example.ApplicationResources&lt;param-value&gt;
+      &lt;/init-param&gt;
+      &lt;init-param&gt;
+        &lt;param-name&gt;config&lt;/param-name&gt;
+        &lt;param-value&gt;/WEB-INF/struts-config.xml&lt;param-value&gt;
+      &lt;/init-param&gt;
+    &lt;/servlet&gt;
+    ...
+  &lt;/web-app&gt;
+</pre>
+
+<p>Next, lets define some Digester processing rules for this input file:</p>
+<pre>
+  digester.addObjectCreate("web-app/servlet",
+                           "com.mycompany.ServletBean");
+  digester.addCallMethod("web-app/servlet/servlet-name", "setServletName", 0);
+  digester.addCallMethod("web-app/servlet/servlet-class",
+                         "setServletClass", 0);
+  digester.addCallMethod("web-app/servlet/init-param",
+                         "addInitParam", 2);
+  digester.addCallParam("web-app/servlet/init-param/param-name", 0);
+  digester.addCallParam("web-app/servlet/init-param/param-value", 1);
+</pre>
+
+<p>Now, as elements are parsed, the following processing occurs:</p>
+<ul>
+<li><em>&lt;servlet&gt;</em> - A new <code>com.mycompany.ServletBean</code>
+    object is created, and pushed on to the object stack.</li>
+<li><em>&lt;servlet-name&gt;</em> - The <code>setServletName()</code> method
+    of the top object on the stack (our <code>ServletBean</code>) is called,
+    passing the body content of this element as a single parameter.</li>
+<li><em>&lt;servlet-class&gt;</em> - The <code>setServletClass()</code> method
+    of the top object on the stack (our <code>ServletBean</code>) is called,
+    passing the body content of this element as a single parameter.</li>
+<li><em>&lt;init-param&gt;</em> - A call to the <code>addInitParam</code>
+    method of the top object on the stack (our <code>ServletBean</code>) is
+    set up, but it is <strong>not</strong> called yet.  The call will be
+    expecting two <code>String</code> parameters, which must be set up by
+    subsequent call parameter rules.</li>
+<li><em>&lt;param-name&gt;</em> - The body content of this element is assigned
+    as the first (zero-relative) argument to the call we are setting up.</li>
+<li><em>&lt;param-value&gt;</em> - The body content of this element is assigned
+    as the second (zero-relative) argument to the call we are setting up.</li>
+<li><em>&lt;/init-param&gt;</em> - The call to <code>addInitParam()</code>
+    that we have set up is now executed, which will cause a new name-value
+    combination to be recorded in our bean.</li>
+<li><em>&lt;init-param&gt;</em> - The same set of processing rules are fired
+    again, causing a second call to <code>addInitParam()</code> with the
+    second parameter's name and value.</li>
+<li><em>&lt;/servlet&gt;</em> - The element on the top of the object stack
+    (which should be the <code>ServletBean</code> we pushed earlier) is
+    popped off the object stack.</li>
+</ul>
+
+
+<a name="doc.Namespace"></a>
+<h3>Namespace Aware Parsing</h3>
+
+<p>For digesting XML documents that do not use XML namespaces, the default
+behavior of <code>Digester</code>, as described above, is generally sufficient.
+However, if the document you are processing uses namespaces, it is often
+convenient to have sets of <code>Rule</code> instances that are <em>only</em>
+matched on elements that use the prefix of a particular namespace.  This
+approach, for example, makes it possible to deal with element names that are 
+the same in different namespaces, but where you want to perform different 
+processing for each namespace. </p>
+
+<p>Digester does not provide full support for namespaces, but does provide
+sufficient to accomplish most tasks. Enabling digester's namespace support
+is done by following these steps:</p>
+
+<ol>
+<li>Tell <code>Digester</code> that you will be doing namespace
+    aware parsing, by adding this statement in your initalization
+    of the Digester's properties:
+    <pre>
+    digester.setNamespaceAware(true);
+    </pre></li>
+<li>Declare the public namespace URI of the namespace with which
+    following rules will be associated.  Note that you do <em>not</em>
+    make any assumptions about the prefix - the XML document author
+    is free to pick whatever prefix they want:
+    <pre>
+    digester.setRuleNamespaceURI("http://www.mycompany.com/MyNamespace");
+    </pre></li>
+<li>Add the rules that correspond to this namespace, in the usual way,
+    by calling methods like <code>addObjectCreate()</code> or
+    <code>addSetProperties()</code>.  In the matching patterns you specify,
+    use only the <em>local name</em> portion of the elements (i.e. the
+    part after the prefix and associated colon (":") character:
+    <pre>
+    digester.addObjectCreate("foo/bar", "com.mycompany.MyFoo");
+    digester.addSetProperties("foo/bar");
+    </pre></li>
+<li>Repeat the previous two steps for each additional public namespace URI
+    that should be recognized on this <code>Digester</code> run.</li>
+</ol>
+
+<p>Now, consider that you might wish to digest the following document, using
+the rules that were set up in the steps above:</p>
+<pre>
+&lt;m:foo
+   xmlns:m="http://www.mycompany.com/MyNamespace"
+   xmlns:y="http://www.yourcompany.com/YourNamespace"&gt;
+
+  &lt;m:bar name="My Name" value="My Value"/&gt;
+
+  &lt;y:bar id="123" product="Product Description"/&gt;L
+
+&lt;/x:foo&gt;
+</pre>
+
+<p>Note that your object create and set properties rules will be fired for the
+<em>first</em> occurrence of the <code>bar</code> element, but not the
+<em>second</em> one.  This is because we declared that our rules only matched
+for the particular namespace we are interested in.  Any elements in the
+document that are associated with other namespaces (or no namespaces at all)
+will not be processed.  In this way, you can easily create rules that digest
+only the portions of a compound document that they understand, without placing
+any restrictions on what other content is present in the document.</p>
+
+<p>You might also want to look at <a href="#doc.RuleSets">Encapsulated
+Rule Sets</a> if you wish to reuse a particular set of rules, associated
+with a particular namespace, in more than one application context.</p>
+
+<h4>Using Namespace Prefixes In Pattern Matching</h4>
+
+<p>Using rules with namespaces is very useful when you have orthogonal rulesets. 
+One ruleset applies to a namespace and is independent of other rulesets applying
+to other namespaces. However, if your rule logic requires mixed namespaces, then 
+matching namespace prefix patterns might be a better strategy.</p>
+
+<p>When you set the <code>NamespaceAware</code> property to false, digester uses
+the qualified element name (which includes the namespace prefix) rather than the
+local name as the patten component for the element. This means that your pattern
+matches can include namespace prefixes as well as element names. So, rather than
+create namespace-aware rules, create pattern matches including the namespace
+prefixes.</p>
+
+<p>For example, (with <code>NamespaceAware</code> false), the pattern <code>
+'foo:bar'</code> will match a top level element named <code>'bar'</code> in the 
+namespace with (local) prefix <code>'foo'</code>.</p>
+
+<h4>Limitations of Digester Namespace support</h4>
+<p>Digester does not provide general "xpath-compliant" matching;
+only the namespace attached to the <i>last</i> element in the match path
+is involved in the matching process. Namespaces attached to parent
+elements are ignored for matching purposes.</p>
+
+
+<a name="doc.Pluggable"></a>
+<h3>Pluggable Rules Processing</h3>
+
+<p>By default, <code>Digester</code> selects the rules that match a particular
+pattern of nested elements as described under
+<a href="#doc.Patterns">Element Matching Patterns</a>.  If you prefer to use
+different selection policies, however, you can create your own implementation
+of the <a href="Rules.html">org.apache.commons.digester.Rules</a> interface,
+or subclass the corresponding convenience base class
+<a href="RulesBase.html">org.apache.commons.digester.RulesBase</a>.
+Your implementation of the <code>match()</code> method will be called when the
+processing for a particular element is started or ended, and you must return
+a <code>List</code> of the rules that are relevant for the current nesting
+pattern.  The order of the rules you return <strong>is</strong> significant,
+and should match the order in which rules were initally added.</p>
+
+<p>Your policy for rule selection should generally be sensitive to whether
+<a href="#doc.Namespace">Namespace Aware Parsing</a> is taking place.  In
+general, if <code>namespaceAware</code> is true, you should select only rules
+that:</p>
+<ul>
+<li>Are registered for the public namespace URI that corresponds to the
+    prefix being used on this element.</li>
+<li>Match on the "local name" portion of the element (so that the document
+    creator can use any prefix that they like).</li>
+</ul>
+
+<h4>ExtendedBaseRules</h4>
+<p><a href="ExtendedBaseRules.html">ExtendedBaseRules</a>,
+adds some additional expression syntax for pattern matching
+to the default mechanism, but it also executes more slowly.  See the
+JavaDocs for more details on the new pattern matching syntax, and suggestions
+on when this implementation should be used.  To use it, simply do the
+following as part of your Digester initialization:</p>
+
+<pre>
+  Digester digester = ...
+  ...
+  digester.setRules(new ExtendedBaseRules());
+  ...
+</pre>
+
+<h4>RegexRules</h4>
+<p><a href="RegexRules.html">RegexRules</a> is an advanced <code>Rules</code> 
+implementation which does not build on the default pattern matching rules.
+It uses a pluggable <a href="RegexMatcher.html">RegexMatcher</a> implementation to test 
+if a path matches the pattern for a Rule. All matching rules are returned 
+(note that this behaviour differs from longest matching rule of the default
+ pattern matching rules). See the Java Docs for more details.
+</p>
+<p>
+Example usage:
+</p>
+
+<pre>
+  Digester digester = ...
+  ...
+  digester.setRules(new RegexRules(new SimpleRegexMatcher()));
+  ...
+</pre>
+<h5>RegexMatchers</h5>
+<p>
+<code>Digester</code> ships only with one <code>RegexMatcher</code>
+implementation: <a href='SimpleRegexMatcher.html'>SimpleRegexMatcher</a>.
+This implementation is unsophisticated and lacks many good features
+lacking in more power Regex libraries. There are some good reasons
+why this approach was adopted. The first is that <code>SimpleRegexMatcher</code>
+is simple, it is easy to write and runs quickly. The second has to do with 
+the way that <code>RegexRules</code> is intended to be used.
+</p>
+<p>
+There are many good regex libraries available. (For example 
+<a href='http://jakarta.apache.org/oro/index.html'>Jakarta ORO</a>,
+<a href='http://jakarta.apache.org/regexp/index.html'>Jakarta Regex</a>,
+<a href='http://www.cacas.org/java/gnu/regexp/'>GNU Regex</a> and
+<a href='http://java.sun.com/j2se/1.4.2/docs/api/java/util/regex/package-summary.html'>
+Java 1.4 Regex</a>)
+Not only do different people have different personal tastes when it comes to
+regular expression matching but these products all offer different functionality
+and different strengths.
+</p>
+<p>
+The pluggable <code>RegexMatcher</code> is a thin bridge
+designed to adapt other Regex systems. This allows any Regex library the user
+desires to be plugged in and used just by creating one class.
+<code>Digester</code> does not (currently) ship with bridges to the major
+regex (to allow the dependencies required by <code>Digester</code>
+to be kept to a minimum).
+</p>
+
+<h4>WithDefaultsRulesWrapper</h4>
+<p>
+<a href="WithDefaultsRulesWrapper.html"> WithDefaultsRulesWrapper</a> allows 
+default <code>Rule</code> instances to be added to any existing 
+<code>Rules</code> implementation. These default <code>Rule</code> instances 
+will be returned for any match for which the wrapped implementation does not 
+return any matches. 
+</p>
+<p>
+For example,
+<pre>
+    Rule alpha;
+    ...
+    WithDefaultsRulesWrapper rules = new WithDefaultsRulesWrapper(new BaseRules());
+    rules.addDefault(alpha);
+    ...
+    digester.setRules(rules);
+    ...
+</pre>
+when a pattern does not match any other rule, then rule alpha will be called.
+</p>
+<p>
+<code>WithDefaultsRulesWrapper</code> follows the <em>Decorator</em> pattern.
+</p>
+
+<a name="doc.RuleSets"></a>
+<h3>Encapsulated Rule Sets</h3>
+
+<p>All of the examples above have described a scenario where the rules to be
+processed are registered with a <code>Digester</code> instance immediately
+after it is created.  However, this approach makes it difficult to reuse the
+same set of rules in more than one application environment.  Ideally, one
+could package a set of rules into a single class, which could be easily
+loaded and registered with a <code>Digester</code> instance in one easy step.
+</p>
+
+<p>The <a href="RuleSet.html">RuleSet</a> interface (and the convenience base
+class <a href="RuleSetBase.html">RuleSetBase</a>) make it possible to do this.
+In addition, the rule instances registered with a particular
+<code>RuleSet</code> can optionally be associated with a particular namespace,
+as described under <a href="#doc.Namespace">Namespace Aware Processing</a>.</p>
+
+<p>An example of creating a <code>RuleSet</code> might be something like this:
+</p>
+<pre>
+public class MyRuleSet extends RuleSetBase {
+
+  public MyRuleSet() {
+    this("");
+  }
+
+  public MyRuleSet(String prefix) {
+    super();
+    this.prefix = prefix;
+    this.namespaceURI = "http://www.mycompany.com/MyNamespace";
+  }
+
+  protected String prefix = null;
+
+  public void addRuleInstances(Digester digester) {
+    digester.addObjectCreate(prefix + "foo/bar",
+      "com.mycompany.MyFoo");
+    digester.addSetProperties(prefix + "foo/bar");
+  }
+
+}
+</pre>
+
+<p>You might use this <code>RuleSet</code> as follow to initialize a
+<code>Digester</code> instance:</p>
+<pre>
+  Digester digester = new Digester();
+  ... configure Digester properties ...
+  digester.addRuleSet(new MyRuleSet("baz/"));
+</pre>
+
+<p>A couple of interesting notes about this approach:</p>
+<ul>
+<li>The application that is using these rules does not need to know anything
+    about the fact that the <code>RuleSet</code> being used is associated
+    with a particular namespace URI.  That knowledge is emedded inside the
+    <code>RuleSet</code> class itself.</li>
+<li>If desired, you could make a set of rules work for more than one
+    namespace URI by providing constructors on the <code>RuleSet</code> to
+    allow this to be specified dynamically.</li>
+<li>The <code>MyRuleSet</code> example above illustrates another technique
+    that increases reusability -- you can specify (as an argument to the
+    constructor) the leading portion of the matching pattern to be used.
+    In this way, you can construct a <code>Digester</code> that recognizes
+    the same set of nested elements at different nesting levels within an
+    XML document.</li>
+</ul>
+<a name="doc.NamedStacks"></a>
+<h3>Using Named Stacks For Inter-Rule Communication</h3>
+<p>
+<code>Digester</code> is based on <code>Rule</code> instances working together 
+to process xml. For anything other than the most trival processing, 
+communication between <code>Rule</code> instances is necessary. Since <code>Rule</code>
+instances are processed in sequence, this usually means storing an Object 
+somewhere where later instances can retrieve it.
+</p>
+<p>
+<code>Digester</code> is based on SAX. The most natural data structure to use with 
+SAX based xml processing is the stack. This allows more powerful processes to be
+specified more simply since the pushing and popping of objects can mimic the 
+nested structure of the xml. 
+</p>
+<p>
+<code>Digester</code> uses two basic stacks: one for the main beans and the other 
+for parameters for method calls. These are inadequate for complex processing 
+where many different <code>Rule</code> instances need to communicate through 
+different channels.
+</p>
+<p>
+In this case, it is recommended that named stacks are used. In addition to the
+two basic stacks, <code>Digester</code> allows rules to use an unlimited number
+of other stacks referred two by an identifying string (the name). (That's where
+the term <em>named stack</em> comes from.) These stacks are 
+accessed through calls to:
+</p>
+<ul>
+    <li><a href='Digester.html#push(java.lang.String, java.lang.Object)'>
+        void push(String stackName, Object value)</a></li>
+    <li><a href='Digester.html#pop(java.lang.String)'>
+        Object pop(String stackName)</a></li>
+    <li><a href='Digester.html#peek(java.lang.String)'>
+        Object peek(String stackName)</a></li>
+</ul>
+<p>
+<strong>Note:</strong> all stack names beginning with <code>org.apache.commons.digester</code>
+are reserved for future use by the <code>Digester</code> component. It is also recommended
+that users choose stack names perfixed by the name of their own domain to avoid conflicts
+with other <code>Rule</code> implementations.
+</p>
+<a name="doc.RegisteringDTDs"></a>
+<h3>Registering DTDs</h3>
+
+<h4>Brief (But Still Too Long) Introduction To System and Public Identifiers</h4>
+<p>A definition for an external entity comes in one of two forms:
+</p>
+<ol>
+    <li><code>SYSTEM <em>system-identifier</em></code></li>
+    <li><code>PUBLIC <em>public-identifier</em> <em>system-identifier</em></code></li>
+</ol>
+<p>
+The <code><em>system-identifier</em></code> is an URI from which the resource can be obtained
+(either directly or indirectly). Many valid URIs may identify the same resource.
+The <code><em>public-identifier</em></code> is an additional free identifier which may be used
+(by the parser) to locate the resource. 
+</p>
+<p>
+In practice, the weakness with a <code><em>system-identifier</em></code> is that most parsers
+will attempt to interprete this URI as an URL, try to download the resource directly
+from the URL and stop the parsing if this download fails. So, this means that 
+almost always the URI will have to be an URL from which the declaration
+can be downloaded.
+</p>
+<p>
+URLs may be local or remote but if the URL is chosen to be local, it is likely only
+to function correctly on a small number of machines (which are configured precisely
+to allow the xml to be parsed). This is usually unsatisfactory and so a universally
+accessable URL is preferred. This usually means an internet URL.
+</p>
+<p>
+To recap, in practice the <code><em>system-identifier</em></code> will (most likely) be an 
+internet URL. Unfortunately downloading from an internet URL is not only slow
+but unreliable (since successfully downloading a document from the internet 
+relies on the client being connect to the internet and the server being
+able to satisfy the request).
+</p>
+<p>
+The <code><em>public-identifier</em></code> is a freely defined name but (in practice) it is 
+strongly recommended that a unique, readable and open format is used (for reasons
+that should become clear later). A Formal Public Identifier (FPI) is a very
+common choice. This public identifier is often used to provide a unique and location
+independent key which can be used to subsistute local resources for remote ones 
+(hint: this is why ;).
+</p>
+<p>
+By using the second (<code>PUBLIC</code>) form combined with some form of local
+catalog (which matches <code><em>public-identifiers</em></code> to local resources) and where
+the <code><em>public-identifier</em></code> is a unique name and the <code><em>system-identifier</em></code> 
+is an internet URL, the practical disadvantages of specifying just a 
+<code><em>system-identifier</em></code> can be avoided. Those external entities which have been 
+store locally (on the machine parsing the document) can be identified and used.
+Only when no local copy exists is it necessary to download the document
+from the internet URL. This naming scheme is recommended when using <code>Digester</code>.
+</p>
+
+<h4>External Entity Resolution Using Digester</h4>
+<p>
+SAX factors out the resolution of external entities into an <code>EntityResolver</code>.
+<code>Digester</code> supports the use of custom <code>EntityResolver</code> 
+but ships with a simple internal implementation. This implementation allows local URLs
+to be easily associated with <code><em>public-identifiers</em></code>. 
+</p>
+<p>For example:</p>
+<code><pre>
+    digester.register("-//Example Dot Com //DTD Sample Example//EN", "assets/sample.dtd");
+</pre></code>
+<p>
+will make digester return the relative file path <code>assets/sample.dtd</code> 
+whenever an external entity with public id 
+<code>-//Example Dot Com //DTD Sample Example//EN</code> is needed.
+</p>
+<p><strong>Note:</strong> This is a simple (but useful) implementation. 
+Greater sophistication requires a custom <code>EntityResolver</code>.</p>
+    
+<a name="doc.troubleshooting"></a>
+<h3>Troubleshooting</h3>
+<h4>Debugging Exceptions</h4>
+<p>
+<code>Digester</code> is based on <a href='http://www.saxproject.org'>SAX</a>.
+Digestion throws two kinds of <code>Exception</code>:
+</p>
+<ul>
+    <li><code>java.io.IOException</code></li>
+    <li><code>org.xml.sax.SAXException</code></li>
+</ul>
+<p>
+The first is rarely thrown and indicates the kind of fundemental IO exception
+that developers know all about. The second is thrown by SAX parsers when the processing
+of the XML cannot be completed. So, to diagnose the cause a certain familiarity with 
+the way that SAX error handling works is very useful. 
+</p>
+<h5>Diagnosing SAX Exceptions</h5>
+<p>
+This is a short, potted guide to SAX error handling strategies. It's not intended as a
+proper guide to error handling in SAX.
+</p>
+<p>
+When a SAX parser encounters a problem with the xml (well, ok - sometime after it 
+encounters a problem) it will throw a 
+<a href='http://www.saxproject.org/apidoc/org/xml/sax/SAXParseException.html'>
+SAXParseException</a>. This is a subclass of <code>SAXException</code> and contains
+a bit of extra information about what exactly when wrong - and more importantly,
+where it went wrong. If you catch an exception of this sort, you can be sure that
+the problem is with the XML and not <code>Digester</code> or your rules.
+It is usually a good idea to catch this exception and log the extra information
+to help with diagnosing the reason for the failure.
+</p>
+<p>
+General <a href='http://www.saxproject.org/apidoc/org/xml/sax/SAXException.html'>
+SAXException</a> instances may wrap a causal exception. When exceptions are
+throw by <code>Digester</code> each of these will be wrapped into a 
+<code>SAXException</code> and rethrown. So, catch these and examine the wrapped
+exception to diagnose what went wrong.
+</p>
+<a name="doc.FAQ"></a>
+<h3>Frequently Asked Questions</h3>
+<p><ul>
+<li><strong>Why do I get warnings when using a JAXP 1.1 parser?</strong>
+<p>If you're using a JAXP 1.1 parser, you might see the following warning (in your log):
+<code><pre>
+[WARN] Digester - -Error: JAXP SAXParser property not recognized: http://java.sun.com/xml/jaxp/properties/schemaLanguage
+</pre></code>
+This property is needed for JAXP 1.2 (XML Schema support) as required
+for the Servlet Spec. 2.4 but is not recognized by JAXP 1.1 parsers.
+This warning is harmless.</p>
+<p>
+</li>
+<li><strong>Why Doesn't Schema Validation Work With Parser XXX Out Of The Box?</strong>
+<p>
+Schema location and language settings are often need for validation using schemas.
+Unfortunately, there isn't a single standard approach to how these properties are
+configured on a parser.
+Digester tries to guess the parser being used and configure it appropriately
+but it's not infallible.
+You might need to grab an instance, configure it and pass it to Digester.
+</p>
+<p>
+If you want to support more than one parser in a portable manner, 
+then you'll probably want to take a look at the 
+<code>org.apache.commons.digester.parsers</code> package
+and add a new class to support the particular parser that's causing problems.
+</p>
+</li>
+<li><strong>Help! 
+I'm Validating Against Schema But Digester Ignores Errors!</strong>
+<p>
+Digester is based on <a href='http://www.saxproject.org'>SAX</a>. The convention for
+SAX parsers is that all errors are reported (to any registered 
+<code>ErrorHandler</code>) but processing continues. Digester (by default) 
+registers its own <code>ErrorHandler</code> implementation. This logs details 
+but does not stop the processing (following the usual convention for SAX 
+based processors). 
+</p>
+<p>
+This means that the errors reported by the validation of the schema will appear in the
+Digester logs but the processing will continue. To change this behaviour, call
+<code>digester.setErrorHandler</code> with a more suitable implementation.
+</p>
+
+<li><strong>Where Can I Find Example Code?</strong>
+<a name="doc.FAQ.Examples">
+<p>Digester ships with a sample application: a mapping for the <em>Rich Site 
+Summary</em> format used by many newsfeeds. Download the source distribution 
+to see how it works.</p>
+<p>Digester also ships with a set of examples demonstrating most of the 
+features described in this document. See the "src/examples" subdirectory 
+of the source distribution.</p>
+</li>
+<li><strong>When Are You Going To Support <em>Rich Site Summary</em> Version x.y.z?</strong>
+<p>
+The <em>Rich Site Summary</em> application is intended to be a sample application. 
+It works but we have no plans to add support for other versions of the format.
+</p>
+<p>
+We would consider donations of standard digester applications but it's unlikely that
+these would ever be shipped with the base digester distribution.
+If you want to discuss this, please post to <a href='http://jakarta.apache.org/site/mail.html'>
+common-dev mailing list</a>
+</p>
+</li>
+</ul>
+<a name="doc.Limits"></a>
+<h3>Known Limitations</h3>
+<h4>Accessing Public Methods In A Default Access Superclass</h4>
+<p>There is an issue when invoking public methods contained in a default access superclass.
+Reflection locates these methods fine and correctly assigns them as public.
+However, an <code>IllegalAccessException</code> is thrown if the method is invoked.</p>
+
+<p><code>MethodUtils</code> contains a workaround for this situation. 
+It will attempt to call <code>setAccessible</code> on this method.
+If this call succeeds, then the method can be invoked as normal.
+This call will only succeed when the application has sufficient security privilages. 
+If this call fails then a warning will be logged and the method may fail.</p>
+
+<p><code>Digester</code> uses <code>MethodUtils</code> and so there may be an issue accessing methods
+of this kind from a high security environment. If you think that you might be experiencing this 
+problem, please ask on the mailing list.</p>
+</body>
+</html>
diff --git a/connectors/util/java/org/apache/tomcat/util/http/AcceptLanguage.java b/connectors/util/java/org/apache/tomcat/util/http/AcceptLanguage.java
new file mode 100644
index 0000000..9b02439
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/AcceptLanguage.java
@@ -0,0 +1,147 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.http;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+/**
+ * Util to process the "Accept-Language" header. Used by facade to implement
+ * getLocale() and by StaticInterceptor.
+ *
+ * Not optimized - it's very slow.
+ * 
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Jason Hunter [jch@eng.sun.com]
+ * @author Harish Prabandham
+ * @author costin@eng.sun.com
+ */
+public class AcceptLanguage {
+
+    public static Locale getLocale(String acceptLanguage) {
+	if( acceptLanguage == null ) return Locale.getDefault();
+
+        Hashtable languages = new Hashtable();
+        Vector quality=new Vector();
+        processAcceptLanguage(acceptLanguage, languages,quality);
+
+        if (languages.size() == 0) return Locale.getDefault();
+
+        Vector l = new Vector();
+        extractLocales( languages,quality, l);
+
+        return (Locale)l.elementAt(0);
+    }
+
+    public static Enumeration getLocales(String acceptLanguage) {
+    	// Short circuit with an empty enumeration if null header
+        if (acceptLanguage == null) {
+            Vector v = new Vector();
+            v.addElement(Locale.getDefault());
+            return v.elements();
+        }
+	
+        Hashtable languages = new Hashtable();
+        Vector quality=new Vector();
+    	processAcceptLanguage(acceptLanguage, languages , quality);
+
+        if (languages.size() == 0) {
+            Vector v = new Vector();
+            v.addElement(Locale.getDefault());
+            return v.elements();
+        }
+    	Vector l = new Vector();
+    	extractLocales( languages, quality , l);
+    	return l.elements();
+    }
+
+    private static void processAcceptLanguage( String acceptLanguage,
+					      Hashtable languages, Vector q)
+    {
+        StringTokenizer languageTokenizer =
+            new StringTokenizer(acceptLanguage, ",");
+
+        while (languageTokenizer.hasMoreTokens()) {
+            String language = languageTokenizer.nextToken().trim();
+            int qValueIndex = language.indexOf(';');
+            int qIndex = language.indexOf('q');
+            int equalIndex = language.indexOf('=');
+            Double qValue = new Double(1);
+
+            if (qValueIndex > -1 &&
+                    qValueIndex < qIndex &&
+                    qIndex < equalIndex) {
+    	        String qValueStr = language.substring(qValueIndex + 1);
+                language = language.substring(0, qValueIndex);
+                qValueStr = qValueStr.trim().toLowerCase();
+                qValueIndex = qValueStr.indexOf('=');
+                qValue = new Double(0);
+                if (qValueStr.startsWith("q") &&
+                    qValueIndex > -1) {
+                    qValueStr = qValueStr.substring(qValueIndex + 1);
+                    try {
+                        qValue = new Double(qValueStr.trim());
+                    } catch (NumberFormatException nfe) {
+                    }
+                }
+            }
+
+            // XXX
+            // may need to handle "*" at some point in time
+
+            if (! language.equals("*")) {
+                String key = qValue.toString();
+                Vector v;
+                if (languages.containsKey(key)) {
+                    v = (Vector)languages.get(key) ;
+                } else {
+                    v= new Vector();
+                    q.addElement(qValue);
+                }
+                v.addElement(language);
+                languages.put(key, v);
+            }
+        }
+    }
+
+    private static void extractLocales(Hashtable languages, Vector q,Vector l)
+    {
+        // XXX We will need to order by q value Vector in the Future ?
+        Enumeration e = q.elements();
+        while (e.hasMoreElements()) {
+            Vector v =
+                (Vector)languages.get(((Double)e.nextElement()).toString());
+            Enumeration le = v.elements();
+            while (le.hasMoreElements()) {
+    	        String language = (String)le.nextElement();
+	        	String country = "";
+        		int countryIndex = language.indexOf("-");
+                if (countryIndex > -1) {
+                    country = language.substring(countryIndex + 1).trim();
+                    language = language.substring(0, countryIndex).trim();
+                }
+                l.addElement(new Locale(language, country));
+            }
+        }
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/http/BaseRequest.java b/connectors/util/java/org/apache/tomcat/util/http/BaseRequest.java
new file mode 100644
index 0000000..691c027
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/BaseRequest.java
@@ -0,0 +1,345 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/***************************************************************************
+ * Description: Base http request object.                                  *
+ * Author:      Keving Seguin [seguin@apache.org]                          *
+ * Version:     $Revision$                                           *
+ ***************************************************************************/
+
+package org.apache.tomcat.util.http;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.apache.tomcat.util.buf.MessageBytes;
+
+/**
+ * A general-purpose object for representing an HTTP
+ * request.
+ */
+public class BaseRequest {
+
+    // scheme constants
+    public static final String SCHEME_HTTP = "http";
+    public static final String SCHEME_HTTPS = "https";
+
+    // request attributes
+    MessageBytes method = MessageBytes.newInstance();
+    MessageBytes protocol = MessageBytes.newInstance();
+    MessageBytes requestURI = MessageBytes.newInstance();
+    MessageBytes remoteAddr = MessageBytes.newInstance();
+    MessageBytes remoteHost = MessageBytes.newInstance();
+    MessageBytes serverName = MessageBytes.newInstance();
+    int serverPort = 80;
+    MessageBytes remoteUser = MessageBytes.newInstance();
+    MessageBytes authType = MessageBytes.newInstance();
+    MessageBytes queryString = MessageBytes.newInstance();
+    MessageBytes authorization = MessageBytes.newInstance();
+    String scheme = SCHEME_HTTP;
+    boolean secure = false;
+    int contentLength = 0;
+    MessageBytes contentType = MessageBytes.newInstance();
+    MimeHeaders headers = new MimeHeaders();
+    Cookies cookies = new Cookies();
+    HashMap attributes = new HashMap();
+
+    MessageBytes tomcatInstanceId = MessageBytes.newInstance();
+    
+    /**
+     * Recycles this object and readies it further use.
+     */
+    public void recycle() {
+        method.recycle();
+        protocol.recycle();
+        requestURI.recycle();
+        remoteAddr.recycle();
+        remoteHost.recycle();
+        serverName.recycle();
+        serverPort = 80;
+        remoteUser.recycle();
+        authType.recycle();
+        queryString.recycle();
+        authorization.recycle();
+        scheme = SCHEME_HTTP;
+        secure = false;
+        contentLength = 0;
+        contentType.recycle();
+        headers.recycle();
+        cookies.recycle();
+        attributes.clear();
+        tomcatInstanceId.recycle();
+    }
+
+    /**
+     * Get the method.
+     * @return the method
+     */
+    public MessageBytes method() {
+        return method;
+    }
+
+    /**
+     * Get the protocol
+     * @return the protocol
+     */
+    public MessageBytes protocol() {
+        return protocol;
+    }
+
+    /**
+     * Get the request uri
+     * @return the request uri
+     */
+    public MessageBytes requestURI() {
+        return requestURI;
+    }
+
+    /**
+     * Get the remote address
+     * @return the remote address
+     */
+    public MessageBytes remoteAddr() {
+        return remoteAddr;
+    }
+
+    /**
+     * Get the remote host
+     * @return the remote host
+     */
+    public MessageBytes remoteHost() {
+        return remoteHost;
+    }
+
+    /**
+     * Get the server name
+     * @return the server name
+     */
+    public MessageBytes serverName() {
+        return serverName;
+    }
+
+    /**
+     * Get the server port
+     * @return the server port
+     */
+    public int getServerPort() {
+        return serverPort;
+    }
+
+    /**
+     * Set the server port
+     * @param i the server port
+     */
+    public void setServerPort(int i) {
+        serverPort = i;
+    }
+
+    /**
+     * Get the remote user
+     * @return the remote user
+     */
+    public MessageBytes remoteUser() {
+        return remoteUser;
+    }
+
+    /**
+     * Get the auth type
+     * @return the auth type
+     */
+    public MessageBytes authType() {
+        return authType;
+    }
+
+    /**
+     * Get the query string
+     * @return the query string
+     */
+    public MessageBytes queryString() {
+        return queryString;
+    }
+
+    /**
+     * Get the authorization credentials
+     * @return the authorization credentials
+     */
+    public MessageBytes authorization() {
+        return authorization;
+    }
+
+    /**
+     * Get the scheme
+     * @return the scheme
+     */
+    public String getScheme() {
+        return scheme;
+    }
+
+    /**
+     * Set the scheme.
+     * @param s the scheme
+     */
+    public void setScheme(String s) {
+        scheme = s;
+    }
+
+    /**
+     * Get whether the request is secure or not.
+     * @return <code>true</code> if the request is secure.
+     */
+    public boolean getSecure() {
+        return secure;
+    }
+
+    /**
+     * Set whether the request is secure or not.
+     * @param b <code>true</code> if the request is secure.
+     */
+    public void setSecure(boolean b) {
+        secure = b;
+    }
+
+    /**
+     * Get the content length
+     * @return the content length
+     */
+    public int getContentLength() {
+        return contentLength;
+    }
+
+    /**
+     * Set the content length
+     * @param i the content length
+     */
+    public void setContentLength(int i) {
+        contentLength = i;
+    }
+
+    /**
+     * Get the content type
+     * @return the content type
+     */
+    public MessageBytes contentType() {
+        return contentType;
+    }
+
+    /**
+     * Get this request's headers
+     * @return request headers
+     */
+    public MimeHeaders headers() {
+        return headers;
+    }
+
+    /**
+     * Get cookies.
+     * @return request cookies.
+     */
+    public Cookies cookies() {
+        return cookies;
+    }
+
+    /**
+     * Set an attribute on the request
+     * @param name attribute name
+     * @param value attribute value
+     */
+    public void setAttribute(String name, Object value) {
+        if (name == null || value == null) {
+            return;
+        }
+        attributes.put(name, value);
+    }
+
+    /**
+     * Get an attribute on the request
+     * @param name attribute name
+     * @return attribute value
+     */
+    public Object getAttribute(String name) {
+        if (name == null) {
+            return null;
+        }
+
+        return attributes.get(name);
+    }
+
+    /**
+     * Get iterator over attribute names
+     * @return iterator over attribute names
+     */
+    public Iterator getAttributeNames() {
+        return attributes.keySet().iterator();
+    }
+
+    /**
+     * Get the host id ( or jvmRoute )
+     * @return the jvm route
+     */
+    public MessageBytes instanceId() {
+        return tomcatInstanceId;
+    }
+
+    // backward compat - jvmRoute is the id of this tomcat instance,
+    // used by a load balancer on the server side to implement sticky
+    // sessions, and on the tomcat side to format the session ids.
+    public MessageBytes jvmRoute() {
+        return tomcatInstanceId;
+    }
+
+    private Object notes[]=new Object[16];
+    
+    public final Object getNote(int id) {
+        return notes[id];
+    }
+
+    public final void setNote(int id, Object cr) {
+        notes[id]=cr;
+    }
+    
+    /**
+     * ** SLOW ** for debugging only!
+     */
+    public String toString() {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+
+        pw.println("=== BaseRequest ===");
+        pw.println("method          = " + method.toString());
+        pw.println("protocol        = " + protocol.toString());
+        pw.println("requestURI      = " + requestURI.toString());
+        pw.println("remoteAddr      = " + remoteAddr.toString());
+        pw.println("remoteHost      = " + remoteHost.toString());
+        pw.println("serverName      = " + serverName.toString());
+        pw.println("serverPort      = " + serverPort);
+        pw.println("remoteUser      = " + remoteUser.toString());
+        pw.println("authType        = " + authType.toString());
+        pw.println("queryString     = " + queryString.toString());
+        pw.println("scheme          = " + scheme.toString());
+        pw.println("secure          = " + secure);
+        pw.println("contentLength   = " + contentLength);
+        pw.println("contentType     = " + contentType);
+        pw.println("attributes      = " + attributes.toString());
+        pw.println("headers         = " + headers.toString());
+        pw.println("cookies         = " + cookies.toString());
+        pw.println("jvmRoute        = " + tomcatInstanceId.toString());
+        return sw.toString();
+    }
+    
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/http/ContentType.java b/connectors/util/java/org/apache/tomcat/util/http/ContentType.java
new file mode 100644
index 0000000..b9018da
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/ContentType.java
@@ -0,0 +1,92 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.http;
+
+
+/**
+ * Usefull methods for Content-Type processing
+ * 
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Jason Hunter [jch@eng.sun.com]
+ * @author Harish Prabandham
+ * @author costin@eng.sun.com
+ */
+public class ContentType {
+
+    // Basically return everything after ";charset="
+    // If no charset specified, use the HTTP default (ASCII) character set.
+    public static String getCharsetFromContentType(String type) {
+        if (type == null) {
+            return null;
+        }
+        int semi = type.indexOf(";");
+        if (semi == -1) {
+            return null;
+        }
+        int charsetLocation = type.indexOf("charset=", semi);
+        if (charsetLocation == -1) {
+            return null;
+        }
+	String afterCharset = type.substring(charsetLocation + 8);
+        // The charset value in a Content-Type header is allowed to be quoted
+        // and charset values can't contain quotes.  Just convert any quote
+        // chars into spaces and let trim clean things up.
+        afterCharset = afterCharset.replace('"', ' ');
+        String encoding = afterCharset.trim();
+        return encoding;
+    }
+
+
+    /**
+     * Returns true if the given content type contains a charset component,
+     * false otherwise.
+     *
+     * @param type Content type
+     * @return true if the given content type contains a charset component,
+     * false otherwise
+     */
+    public static boolean hasCharset(String type) {
+
+        boolean hasCharset = false;
+
+        int len = type.length();
+        int index = type.indexOf(';');
+        while (index != -1) {
+            index++;
+            while (index < len && Character.isSpace(type.charAt(index))) {
+                index++;
+            }
+            if (index+8 < len
+                    && type.charAt(index) == 'c'
+                    && type.charAt(index+1) == 'h'
+                    && type.charAt(index+2) == 'a'
+                    && type.charAt(index+3) == 'r'
+                    && type.charAt(index+4) == 's'
+                    && type.charAt(index+5) == 'e'
+                    && type.charAt(index+6) == 't'
+                    && type.charAt(index+7) == '=') {
+                hasCharset = true;
+                break;
+            }
+            index = type.indexOf(';', index);
+        }
+
+        return hasCharset;
+    }
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/http/Cookies.java b/connectors/util/java/org/apache/tomcat/util/http/Cookies.java
new file mode 100644
index 0000000..6d7667b
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/Cookies.java
@@ -0,0 +1,478 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.http;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.StringTokenizer;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+
+/**
+ * A collection of cookies - reusable and tuned for server side performance.
+ * Based on RFC2965 ( and 2109 )
+ *
+ * This class is not synchronized.
+ *
+ * @author Costin Manolache
+ * @author kevin seguin
+ */
+public final class Cookies { // extends MultiMap {
+
+    // expected average number of cookies per request
+    public static final int INITIAL_SIZE=4; 
+    ServerCookie scookies[]=new ServerCookie[INITIAL_SIZE];
+    int cookieCount=0;
+    boolean unprocessed=true;
+
+    MimeHeaders headers;
+    
+    /**
+     *  Construct a new cookie collection, that will extract
+     *  the information from headers.
+     *
+     * @param headers Cookies are lazy-evaluated and will extract the
+     *     information from the provided headers.
+     */
+    public Cookies(MimeHeaders headers) {
+        this.headers=headers;
+    }
+
+    /**
+     * Construct a new uninitialized cookie collection.
+     * Use {@link #setHeaders} to initialize.
+     */
+    // [seguin] added so that an empty Cookies object could be
+    // created, have headers set, then recycled.
+    public Cookies() {
+    }
+
+    /**
+     * Set the headers from which cookies will be pulled.
+     * This has the side effect of recycling the object.
+     *
+     * @param headers Cookies are lazy-evaluated and will extract the
+     *     information from the provided headers.
+     */
+    // [seguin] added so that an empty Cookies object could be
+    // created, have headers set, then recycled.
+    public void setHeaders(MimeHeaders headers) {
+        recycle();
+        this.headers=headers;
+    }
+
+    /**
+     * Recycle.
+     */
+    public void recycle() {
+            for( int i=0; i< cookieCount; i++ ) {
+            if( scookies[i]!=null )
+                scookies[i].recycle();
+        }
+        cookieCount=0;
+        unprocessed=true;
+    }
+
+    /**
+     * EXPENSIVE!!!  only for debugging.
+     */
+    public String toString() {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        pw.println("=== Cookies ===");
+        int count = getCookieCount();
+        for (int i = 0; i < count; ++i) {
+            pw.println(getCookie(i).toString());
+        }
+        return sw.toString();
+    }
+
+    // -------------------- Indexed access --------------------
+    
+    public ServerCookie getCookie( int idx ) {
+        if( unprocessed ) {
+            getCookieCount(); // will also update the cookies
+        }
+        return scookies[idx];
+    }
+
+    public int getCookieCount() {
+        if( unprocessed ) {
+            unprocessed=false;
+            processCookies(headers);
+        }
+        return cookieCount;
+    }
+
+    // -------------------- Adding cookies --------------------
+
+    /** Register a new, unitialized cookie. Cookies are recycled, and
+     *  most of the time an existing ServerCookie object is returned.
+     *  The caller can set the name/value and attributes for the cookie
+     */
+    public ServerCookie addCookie() {
+        if( cookieCount >= scookies.length  ) {
+            ServerCookie scookiesTmp[]=new ServerCookie[2*cookieCount];
+            System.arraycopy( scookies, 0, scookiesTmp, 0, cookieCount);
+            scookies=scookiesTmp;
+        }
+        
+        ServerCookie c = scookies[cookieCount];
+        if( c==null ) {
+            c= new ServerCookie();
+            scookies[cookieCount]=c;
+        }
+        cookieCount++;
+        return c;
+    }
+
+
+    // code from CookieTools 
+
+    /** Add all Cookie found in the headers of a request.
+     */
+    public  void processCookies( MimeHeaders headers ) {
+        if( headers==null )
+            return;// nothing to process
+        // process each "cookie" header
+        int pos=0;
+        while( pos>=0 ) {
+            // Cookie2: version ? not needed
+            pos=headers.findHeader( "Cookie", pos );
+            // no more cookie headers headers
+            if( pos<0 ) break;
+
+            MessageBytes cookieValue=headers.getValue( pos );
+            if( cookieValue==null || cookieValue.isNull() ) {
+                pos++;
+                continue;
+            }
+
+            // Uncomment to test the new parsing code
+            if( cookieValue.getType() == MessageBytes.T_BYTES ) {
+                if( dbg>0 ) log( "Parsing b[]: " + cookieValue.toString());
+                ByteChunk bc=cookieValue.getByteChunk();
+                processCookieHeader( bc.getBytes(),
+                                     bc.getOffset(),
+                                     bc.getLength());
+            } else {
+                if( dbg>0 ) log( "Parsing S: " + cookieValue.toString());
+                processCookieHeader( cookieValue.toString() );
+            }
+            pos++;// search from the next position
+        }
+    }
+
+    /** Process a byte[] header - allowing fast processing of the
+     *  raw data
+     */
+    void processCookieHeader(  byte bytes[], int off, int len )
+    {
+        if( len<=0 || bytes==null ) return;
+        int end=off+len;
+        int pos=off;
+        
+        int version=0; //sticky
+        ServerCookie sc=null;
+        
+
+        while( pos<end ) {
+            byte cc;
+            // [ skip_spaces name skip_spaces "=" skip_spaces value EXTRA ; ] *
+            if( dbg>0 ) log( "Start: " + pos + " " + end );
+            
+            pos=skipSpaces(bytes, pos, end);
+            if( pos>=end )
+                return; // only spaces
+            int startName=pos;
+            if( dbg>0 ) log( "SN: " + pos );
+            
+            // Version should be the first token
+            boolean isSpecial=false;
+            if(bytes[pos]=='$') { pos++; isSpecial=true; }
+
+            pos= findDelim1( bytes, startName, end); // " =;,"
+            int endName=pos;
+            // current = "=" or " " or DELIM
+            pos= skipSpaces( bytes, endName, end ); 
+            if( dbg>0 ) log( "DELIM: " + endName + " " + (char)bytes[pos]);
+
+            if(pos >= end ) {
+                // it's a name-only cookie ( valid in RFC2109 )
+                if( ! isSpecial ) {
+                    sc=addCookie();
+                    sc.getName().setBytes( bytes, startName,
+                                           endName-startName );
+                    sc.getValue().setString("");
+                    sc.setVersion( version );
+                    if( dbg>0 ) log( "Name only, end: " + startName + " " +
+                                     endName);
+                }
+                return;
+            }
+
+            cc=bytes[pos];
+            pos++;
+            if( cc==';' || cc==',' || pos>=end ) {
+                if( ! isSpecial && startName!= endName ) {
+                    sc=addCookie();
+                    sc.getName().setBytes( bytes, startName,
+                                           endName-startName );
+                    sc.getValue().setString("");
+                    sc.setVersion( version );
+                    if( dbg>0 ) log( "Name only: " + startName + " " + endName);
+                }
+                continue;
+            }
+            
+            // we should have "=" ( tested all other alternatives )
+            int startValue=skipSpaces( bytes, pos, end);
+            int endValue=startValue;
+            
+            // quote is valid only in version=1 cookies
+            cc=bytes[pos];
+            if( ( version == 1 || isSpecial ) && ( cc== '\'' || cc=='"' ) ) {
+                startValue++;
+                endValue=indexOf( bytes, startValue, end, cc );
+                pos=endValue+1; // to skip to next cookie
+             } else {
+                endValue=findDelim2( bytes, startValue, end );
+                pos=endValue+1;
+            }
+            
+            // if not $Version, etc
+            if( ! isSpecial ) {
+                sc=addCookie();
+                sc.getName().setBytes( bytes, startName, endName-startName );
+                sc.getValue().setBytes( bytes, startValue, endValue-startValue);
+                sc.setVersion( version );
+                if( dbg>0 ) {
+                    log( "New: " + sc.getName() + "X=X" + sc.getValue());
+                }
+                continue;
+            }
+            
+            // special - Path, Version, Domain, Port
+            if( dbg>0 ) log( "Special: " + startName + " " + endName);
+            // XXX TODO
+            if( equals( "$Version", bytes, startName, endName ) ) {
+                if(dbg>0 ) log( "Found version " );
+                if( bytes[startValue]=='1' && endValue==startValue+1 ) {
+                    version=1;
+                    if(dbg>0 ) log( "Found version=1" );
+                }
+                continue;
+            }
+            if( sc==null ) {
+                // Path, etc without a previous cookie
+                continue;
+            }
+            if( equals( "$Path", bytes, startName, endName ) ) {
+                sc.getPath().setBytes( bytes,
+                                       startValue,
+                                       endValue-startValue );
+            }
+            if( equals( "$Domain", bytes, startName, endName ) ) {
+                sc.getDomain().setBytes( bytes,
+                                         startValue,
+                                         endValue-startValue );
+            }
+            if( equals( "$Port", bytes, startName, endName ) ) {
+                // sc.getPort().setBytes( bytes,
+                //                        startValue,
+                //                        endValue-startValue );
+            }
+        }
+    }
+
+    // -------------------- Utils --------------------
+    public static int skipSpaces(  byte bytes[], int off, int end ) {
+        while( off < end ) {
+            byte b=bytes[off];
+            if( b!= ' ' ) return off;
+            off ++;
+        }
+        return off;
+    }
+
+    public static int findDelim1( byte bytes[], int off, int end )
+    {
+        while( off < end ) {
+            byte b=bytes[off];
+            if( b==' ' || b=='=' || b==';' || b==',' )
+                return off;
+            off++;
+        }
+        return off;
+    }
+
+    public static int findDelim2( byte bytes[], int off, int end )
+    {
+        while( off < end ) {
+            byte b=bytes[off];
+            if( b==';' || b==',' )
+                return off;
+            off++;
+        }
+        return off;
+    }
+
+    public static int indexOf( byte bytes[], int off, int end, byte qq )
+    {
+        while( off < end ) {
+            byte b=bytes[off];
+            if( b==qq )
+                return off;
+            off++;
+        }
+        return off;
+    }
+
+    public static int indexOf( byte bytes[], int off, int end, char qq )
+    {
+        while( off < end ) {
+            byte b=bytes[off];
+            if( b==qq )
+                return off;
+            off++;
+        }
+        return off;
+    }
+    
+    // XXX will be refactored soon!
+    public static boolean equals( String s, byte b[], int start, int end) {
+        int blen = end-start;
+        if (b == null || blen != s.length()) {
+            return false;
+        }
+        int boff = start;
+        for (int i = 0; i < blen; i++) {
+            if (b[boff++] != s.charAt(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+    
+
+    // ---------------------------------------------------------
+    // -------------------- DEPRECATED, OLD --------------------
+    
+    private void processCookieHeader(  String cookieString )
+    {
+        if( dbg>0 ) log( "Parsing cookie header " + cookieString );
+        // normal cookie, with a string value.
+        // This is the original code, un-optimized - it shouldn't
+        // happen in normal case
+
+        StringTokenizer tok = new StringTokenizer(cookieString,
+                                                  ";", false);
+        while (tok.hasMoreTokens()) {
+            String token = tok.nextToken();
+            int i = token.indexOf("=");
+            if (i > -1) {
+                
+                // XXX
+                // the trims here are a *hack* -- this should
+                // be more properly fixed to be spec compliant
+                
+                String name = token.substring(0, i).trim();
+                String value = token.substring(i+1, token.length()).trim();
+                // RFC 2109 and bug 
+                value=stripQuote( value );
+                ServerCookie cookie = addCookie();
+                
+                cookie.getName().setString(name);
+                cookie.getValue().setString(value);
+                if( dbg > 0 ) log( "Add cookie " + name + "=" + value);
+            } else {
+                // we have a bad cookie.... just let it go
+            }
+        }
+    }
+
+    /**
+     *
+     * Strips quotes from the start and end of the cookie string
+     * This conforms to RFC 2109
+     * 
+     * @param value            a <code>String</code> specifying the cookie 
+     *                         value (possibly quoted).
+     *
+     * @see #setValue
+     *
+     */
+    private static String stripQuote( String value )
+    {
+        //        log("Strip quote from " + value );
+        if (((value.startsWith("\"")) && (value.endsWith("\""))) ||
+            ((value.startsWith("'") && (value.endsWith("'"))))) {
+            try {
+                return value.substring(1,value.length()-1);
+            } catch (Exception ex) { 
+            }
+        }
+        return value;
+    }  
+
+
+    // log
+    static final int dbg=0;
+    public void log(String s ) {
+        System.out.println("Cookies: " + s);
+    }
+
+    /*
+    public static void main( String args[] ) {
+        test("foo=bar; a=b");
+        test("foo=bar;a=b");
+        test("foo=bar;a=b;");
+        test("foo=bar;a=b; ");
+        test("foo=bar;a=b; ;");
+        test("foo=;a=b; ;");
+        test("foo;a=b; ;");
+        // v1 
+        test("$Version=1; foo=bar;a=b"); 
+        test("$Version=\"1\"; foo='bar'; $Path=/path; $Domain=\"localhost\"");
+        test("$Version=1;foo=bar;a=b; ; ");
+        test("$Version=1;foo=;a=b; ; ");
+        test("$Version=1;foo= ;a=b; ; ");
+        test("$Version=1;foo;a=b; ; ");
+        test("$Version=1;foo=\"bar\";a=b; ; ");
+        test("$Version=1;foo=\"bar\";$Path=/examples;a=b; ; ");
+        test("$Version=1;foo=\"bar\";$Domain=apache.org;a=b");
+        test("$Version=1;foo=\"bar\";$Domain=apache.org;a=b;$Domain=yahoo.com");
+        // rfc2965
+        test("$Version=1;foo=\"bar\";$Domain=apache.org;$Port=8080;a=b");
+
+        // wrong
+        test("$Version=1;foo=\"bar\";$Domain=apache.org;$Port=8080;a=b");
+    }
+
+    public static void test( String s ) {
+        System.out.println("Processing " + s );
+        Cookies cs=new Cookies(null);
+        cs.processCookieHeader( s.getBytes(), 0, s.length());
+        for( int i=0; i< cs.getCookieCount() ; i++ ) {
+            System.out.println("Cookie: " + cs.getCookie( i ));
+        }
+            
+    }
+    */
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/http/FastHttpDateFormat.java b/connectors/util/java/org/apache/tomcat/util/http/FastHttpDateFormat.java
new file mode 100644
index 0000000..7711cb3
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/FastHttpDateFormat.java
@@ -0,0 +1,221 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.http;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
+/**
+ * Utility class to generate HTTP dates.
+ * 
+ * @author Remy Maucherat
+ */
+public final class FastHttpDateFormat {
+
+
+    // -------------------------------------------------------------- Variables
+
+
+    /**
+     * HTTP date format.
+     */
+    protected static final SimpleDateFormat format = 
+        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+
+
+    /**
+     * The set of SimpleDateFormat formats to use in getDateHeader().
+     */
+    protected static final SimpleDateFormat formats[] = {
+        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
+        new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
+        new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
+    };
+
+
+    protected final static TimeZone gmtZone = TimeZone.getTimeZone("GMT");
+
+
+    /**
+     * GMT timezone - all HTTP dates are on GMT
+     */
+    static {
+
+        format.setTimeZone(gmtZone);
+
+        formats[0].setTimeZone(gmtZone);
+        formats[1].setTimeZone(gmtZone);
+        formats[2].setTimeZone(gmtZone);
+
+    }
+
+
+    /**
+     * Instant on which the currentDate object was generated.
+     */
+    protected static long currentDateGenerated = 0L;
+
+
+    /**
+     * Current formatted date.
+     */
+    protected static String currentDate = null;
+
+
+    /**
+     * Formatter cache.
+     */
+    protected static final HashMap formatCache = new HashMap();
+
+
+    /**
+     * Parser cache.
+     */
+    protected static final HashMap parseCache = new HashMap();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Get the current date in HTTP format.
+     */
+    public static final String getCurrentDate() {
+
+        long now = System.currentTimeMillis();
+        if ((now - currentDateGenerated) > 1000) {
+            synchronized (format) {
+                if ((now - currentDateGenerated) > 1000) {
+                    currentDateGenerated = now;
+                    currentDate = format.format(new Date(now));
+                }
+            }
+        }
+        return currentDate;
+
+    }
+
+
+    /**
+     * Get the HTTP format of the specified date.
+     */
+    public static final String formatDate
+        (long value, DateFormat threadLocalformat) {
+
+        String cachedDate = null;
+        Long longValue = new Long(value);
+        try {
+            cachedDate = (String) formatCache.get(longValue);
+        } catch (Exception e) {
+        }
+        if (cachedDate != null)
+            return cachedDate;
+
+        String newDate = null;
+        Date dateValue = new Date(value);
+        if (threadLocalformat != null) {
+            newDate = threadLocalformat.format(dateValue);
+            synchronized (formatCache) {
+                updateCache(formatCache, longValue, newDate);
+            }
+        } else {
+            synchronized (formatCache) {
+                newDate = format.format(dateValue);
+                updateCache(formatCache, longValue, newDate);
+            }
+        }
+        return newDate;
+
+    }
+
+
+    /**
+     * Try to parse the given date as a HTTP date.
+     */
+    public static final long parseDate(String value, 
+                                       DateFormat[] threadLocalformats) {
+
+        Long cachedDate = null;
+        try {
+            cachedDate = (Long) parseCache.get(value);
+        } catch (Exception e) {
+        }
+        if (cachedDate != null)
+            return cachedDate.longValue();
+
+        Long date = null;
+        if (threadLocalformats != null) {
+            date = internalParseDate(value, threadLocalformats);
+            synchronized (parseCache) {
+                updateCache(parseCache, value, date);
+            }
+        } else {
+            synchronized (parseCache) {
+                date = internalParseDate(value, formats);
+                updateCache(parseCache, value, date);
+            }
+        }
+        if (date == null) {
+            return (-1L);
+        } else {
+            return date.longValue();
+        }
+
+    }
+
+
+    /**
+     * Parse date with given formatters.
+     */
+    private static final Long internalParseDate
+        (String value, DateFormat[] formats) {
+        Date date = null;
+        for (int i = 0; (date == null) && (i < formats.length); i++) {
+            try {
+                date = formats[i].parse(value);
+            } catch (ParseException e) {
+                ;
+            }
+        }
+        if (date == null) {
+            return null;
+        }
+        return new Long(date.getTime());
+    }
+
+
+    /**
+     * Update cache.
+     */
+    private static final void updateCache(HashMap cache, Object key, 
+                                          Object value) {
+        if (value == null) {
+            return;
+        }
+        if (cache.size() > 1000) {
+            cache.clear();
+        }
+        cache.put(key, value);
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/http/HttpMessages.java b/connectors/util/java/org/apache/tomcat/util/http/HttpMessages.java
new file mode 100644
index 0000000..006a3ab
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/HttpMessages.java
@@ -0,0 +1,106 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.http;
+
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Handle (internationalized) HTTP messages.
+ * 
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Jason Hunter [jch@eng.sun.com]
+ * @author Harish Prabandham
+ * @author costin@eng.sun.com
+ */
+public class HttpMessages {
+    // XXX move message resources in this package
+    protected static StringManager sm =
+        StringManager.getManager("org.apache.tomcat.util.http.res");
+	
+    static String st_200=null;
+    static String st_302=null;
+    static String st_400=null;
+    static String st_404=null;
+    
+    /** Get the status string associated with a status code.
+     *  No I18N - return the messages defined in the HTTP spec.
+     *  ( the user isn't supposed to see them, this is the last
+     *  thing to translate)
+     *
+     *  Common messages are cached.
+     *
+     */
+    public static String getMessage( int status ) {
+	// method from Response.
+	
+	// Does HTTP requires/allow international messages or
+	// are pre-defined? The user doesn't see them most of the time
+	switch( status ) {
+	case 200:
+	    if( st_200==null ) st_200=sm.getString( "sc.200");
+	    return st_200;
+	case 302:
+	    if( st_302==null ) st_302=sm.getString( "sc.302");
+	    return st_302;
+	case 400:
+	    if( st_400==null ) st_400=sm.getString( "sc.400");
+	    return st_400;
+	case 404:
+	    if( st_404==null ) st_404=sm.getString( "sc.404");
+	    return st_404;
+	}
+	return sm.getString("sc."+ status);
+    }
+
+    /**
+     * Filter the specified message string for characters that are sensitive
+     * in HTML.  This avoids potential attacks caused by including JavaScript
+     * codes in the request URL that is often reported in error messages.
+     *
+     * @param message The message string to be filtered
+     */
+    public static String filter(String message) {
+
+	if (message == null)
+	    return (null);
+
+	char content[] = new char[message.length()];
+	message.getChars(0, message.length(), content, 0);
+	StringBuffer result = new StringBuffer(content.length + 50);
+	for (int i = 0; i < content.length; i++) {
+	    switch (content[i]) {
+	    case '<':
+		result.append("&lt;");
+		break;
+	    case '>':
+		result.append("&gt;");
+		break;
+	    case '&':
+		result.append("&amp;");
+		break;
+	    case '"':
+		result.append("&quot;");
+		break;
+	    default:
+		result.append(content[i]);
+	    }
+	}
+	return (result.toString());
+    }
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/http/LocaleToCharsetMap.java b/connectors/util/java/org/apache/tomcat/util/http/LocaleToCharsetMap.java
new file mode 100644
index 0000000..3eb7f18
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/LocaleToCharsetMap.java
@@ -0,0 +1,106 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/*
+ *
+ * This class was originally written by Jason Hunter <jhunter@acm.org>
+ * as part of the book "Java Servlet Programming" (O'Reilly).  
+ * See http://www.servlets.com/book for more information.
+ * Used by Sun Microsystems with permission.
+ *
+ */
+
+package org.apache.tomcat.util.http;
+
+import java.util.*;
+
+/** 
+ * A mapping to determine the (somewhat arbitrarily) preferred charset for 
+ * a given locale.  Supports all locales recognized in JDK 1.1.
+ * This class was originally written by Jason Hunter [jhunter@acm.org]
+ * as part of the book "Java Servlet Programming" (O'Reilly).
+ * See <a href="http://www.servlets.com/book">
+ * http://www.servlets.com/book</a> for more information.
+ * Used by Sun Microsystems with permission.
+ */
+public class LocaleToCharsetMap {
+
+  private static Hashtable map;
+
+  static {
+    map = new Hashtable();
+
+    map.put("ar", "ISO-8859-6");
+    map.put("be", "ISO-8859-5");
+    map.put("bg", "ISO-8859-5");
+    map.put("ca", "ISO-8859-1");
+    map.put("cs", "ISO-8859-2");
+    map.put("da", "ISO-8859-1");
+    map.put("de", "ISO-8859-1");
+    map.put("el", "ISO-8859-7");
+    map.put("en", "ISO-8859-1");
+    map.put("es", "ISO-8859-1");
+    map.put("et", "ISO-8859-1");
+    map.put("fi", "ISO-8859-1");
+    map.put("fr", "ISO-8859-1");
+    map.put("hr", "ISO-8859-2");
+    map.put("hu", "ISO-8859-2");
+    map.put("is", "ISO-8859-1");
+    map.put("it", "ISO-8859-1");
+    map.put("iw", "ISO-8859-8");
+    map.put("ja", "Shift_JIS");
+    map.put("ko", "EUC-KR");     // Requires JDK 1.1.6
+    map.put("lt", "ISO-8859-2");
+    map.put("lv", "ISO-8859-2");
+    map.put("mk", "ISO-8859-5");
+    map.put("nl", "ISO-8859-1");
+    map.put("no", "ISO-8859-1");
+    map.put("pl", "ISO-8859-2");
+    map.put("pt", "ISO-8859-1");
+    map.put("ro", "ISO-8859-2");
+    map.put("ru", "ISO-8859-5");
+    map.put("sh", "ISO-8859-5");
+    map.put("sk", "ISO-8859-2");
+    map.put("sl", "ISO-8859-2");
+    map.put("sq", "ISO-8859-2");
+    map.put("sr", "ISO-8859-5");
+    map.put("sv", "ISO-8859-1");
+    map.put("tr", "ISO-8859-9");
+    map.put("uk", "ISO-8859-5");
+    map.put("zh", "GB2312");
+    map.put("zh_TW", "Big5");
+
+  }
+
+  /**
+   * Gets the preferred charset for the given locale, or null if the locale
+   * is not recognized.
+   *
+   * @param loc the locale
+   * @return the preferred charset
+   */
+  public static String getCharset(Locale loc) {
+    String charset;
+
+    // Try for an full name match (may include country)
+    charset = (String) map.get(loc.toString());
+    if (charset != null) return charset;
+
+    // If a full name didn't match, try just the language
+    charset = (String) map.get(loc.getLanguage());
+    return charset;  // may be null
+  }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/http/MimeHeaders.java b/connectors/util/java/org/apache/tomcat/util/http/MimeHeaders.java
new file mode 100644
index 0000000..4ef4c15
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/MimeHeaders.java
@@ -0,0 +1,444 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.http;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Enumeration;
+
+import org.apache.tomcat.util.buf.MessageBytes;
+
+/* XXX XXX XXX Need a major rewrite  !!!!
+ */
+
+/**
+ * This class is used to contain standard internet message headers,
+ * used for SMTP (RFC822) and HTTP (RFC2068) messages as well as for
+ * MIME (RFC 2045) applications such as transferring typed data and
+ * grouping related items in multipart message bodies.
+ *
+ * <P> Message headers, as specified in RFC822, include a field name
+ * and a field body.  Order has no semantic significance, and several
+ * fields with the same name may exist.  However, most fields do not
+ * (and should not) exist more than once in a header.
+ *
+ * <P> Many kinds of field body must conform to a specified syntax,
+ * including the standard parenthesized comment syntax.  This class
+ * supports only two simple syntaxes, for dates and integers.
+ *
+ * <P> When processing headers, care must be taken to handle the case of
+ * multiple same-name fields correctly.  The values of such fields are
+ * only available as strings.  They may be accessed by index (treating
+ * the header as an array of fields), or by name (returning an array
+ * of string values).
+ */
+
+/* Headers are first parsed and stored in the order they are
+   received. This is based on the fact that most servlets will not
+   directly access all headers, and most headers are single-valued.
+   ( the alternative - a hash or similar data structure - will add
+   an overhead that is not needed in most cases )
+   
+   Apache seems to be using a similar method for storing and manipulating
+   headers.
+       
+   Future enhancements:
+   - hash the headers the first time a header is requested ( i.e. if the
+   servlet needs direct access to headers).
+   - scan "common" values ( length, cookies, etc ) during the parse
+   ( addHeader hook )
+   
+*/
+    
+
+
+/**
+ *  Memory-efficient repository for Mime Headers. When the object is recycled, it
+ *  will keep the allocated headers[] and all the MimeHeaderField - no GC is generated.
+ *
+ *  For input headers it is possible to use the MessageByte for Fileds - so no GC
+ *  will be generated.
+ *
+ *  The only garbage is generated when using the String for header names/values -
+ *  this can't be avoided when the servlet calls header methods, but is easy
+ *  to avoid inside tomcat. The goal is to use _only_ MessageByte-based Fields,
+ *  and reduce to 0 the memory overhead of tomcat.
+ *
+ *  TODO:
+ *  XXX one-buffer parsing - for http ( other protocols don't need that )
+ *  XXX remove unused methods
+ *  XXX External enumerations, with 0 GC.
+ *  XXX use HeaderName ID
+ *  
+ * 
+ * @author dac@eng.sun.com
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Costin Manolache
+ * @author kevin seguin
+ */
+public class MimeHeaders {
+    /** Initial size - should be == average number of headers per request
+     *  XXX  make it configurable ( fine-tuning of web-apps )
+     */
+    public static final int DEFAULT_HEADER_SIZE=8;
+    
+    /**
+     * The header fields.
+     */
+    private MimeHeaderField[] headers = new
+	MimeHeaderField[DEFAULT_HEADER_SIZE];
+
+    /**
+     * The current number of header fields.
+     */
+    private int count;
+
+    /**
+     * Creates a new MimeHeaders object using a default buffer size.
+     */
+    public MimeHeaders() {
+    }
+
+    /**
+     * Clears all header fields.
+     */
+    // [seguin] added for consistency -- most other objects have recycle().
+    public void recycle() {
+        clear();
+    }
+
+    /**
+     * Clears all header fields.
+     */
+    public void clear() {
+	for (int i = 0; i < count; i++) {
+	    headers[i].recycle();
+	}
+	count = 0;
+    }
+
+    /**
+     * EXPENSIVE!!!  only for debugging.
+     */
+    public String toString() {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        pw.println("=== MimeHeaders ===");
+        Enumeration e = names();
+        while (e.hasMoreElements()) {
+            String n = (String)e.nextElement();
+            pw.println(n + " = " + getHeader(n));
+        }
+        return sw.toString();
+    }
+
+    // -------------------- Idx access to headers ----------
+    
+    /**
+     * Returns the current number of header fields.
+     */
+    public int size() {
+	return count;
+    }
+
+    /**
+     * Returns the Nth header name, or null if there is no such header.
+     * This may be used to iterate through all header fields.
+     */
+    public MessageBytes getName(int n) {
+	return n >= 0 && n < count ? headers[n].getName() : null;
+    }
+
+    /**
+     * Returns the Nth header value, or null if there is no such header.
+     * This may be used to iterate through all header fields.
+     */
+    public MessageBytes getValue(int n) {
+	return n >= 0 && n < count ? headers[n].getValue() : null;
+    }
+
+    /** Find the index of a header with the given name.
+     */
+    public int findHeader( String name, int starting ) {
+	// We can use a hash - but it's not clear how much
+	// benefit you can get - there is an  overhead 
+	// and the number of headers is small (4-5 ?)
+	// Another problem is that we'll pay the overhead
+	// of constructing the hashtable
+
+	// A custom search tree may be better
+        for (int i = starting; i < count; i++) {
+	    if (headers[i].getName().equalsIgnoreCase(name)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+    
+    // -------------------- --------------------
+
+    /**
+     * Returns an enumeration of strings representing the header field names.
+     * Field names may appear multiple times in this enumeration, indicating
+     * that multiple fields with that name exist in this header.
+     */
+    public Enumeration names() {
+	return new NamesEnumerator(this);
+    }
+
+    public Enumeration values(String name) {
+	return new ValuesEnumerator(this, name);
+    }
+
+    // -------------------- Adding headers --------------------
+    
+
+    /**
+     * Adds a partially constructed field to the header.  This
+     * field has not had its name or value initialized.
+     */
+    private MimeHeaderField createHeader() {
+	MimeHeaderField mh;
+	int len = headers.length;
+	if (count >= len) {
+	    // expand header list array
+	    MimeHeaderField tmp[] = new MimeHeaderField[count * 2];
+	    System.arraycopy(headers, 0, tmp, 0, len);
+	    headers = tmp;
+	}
+	if ((mh = headers[count]) == null) {
+	    headers[count] = mh = new MimeHeaderField();
+	}
+	count++;
+	return mh;
+    }
+
+    /** Create a new named header , return the MessageBytes
+	container for the new value
+    */
+    public MessageBytes addValue( String name ) {
+ 	MimeHeaderField mh = createHeader();
+	mh.getName().setString(name);
+	return mh.getValue();
+    }
+
+    /** Create a new named header using un-translated byte[].
+	The conversion to chars can be delayed until
+	encoding is known.
+     */
+    public MessageBytes addValue(byte b[], int startN, int len)
+    {
+	MimeHeaderField mhf=createHeader();
+	mhf.getName().setBytes(b, startN, len);
+	return mhf.getValue();
+    }
+
+    /** Create a new named header using translated char[].
+     */
+    public MessageBytes addValue(char c[], int startN, int len)
+    {
+	MimeHeaderField mhf=createHeader();
+	mhf.getName().setChars(c, startN, len);
+	return mhf.getValue();
+    }
+
+    /** Allow "set" operations - 
+        return a MessageBytes container for the
+	header value ( existing header or new
+	if this .
+    */
+    public MessageBytes setValue( String name ) {
+ 	MessageBytes value=getValue(name);
+	if( value == null ) {
+	    MimeHeaderField mh = createHeader();
+	    mh.getName().setString(name);
+	    value=mh.getValue();
+	}
+	return value;
+    }
+
+    //-------------------- Getting headers --------------------
+    /**
+     * Finds and returns a header field with the given name.  If no such
+     * field exists, null is returned.  If more than one such field is
+     * in the header, an arbitrary one is returned.
+     */
+    public MessageBytes getValue(String name) {
+        for (int i = 0; i < count; i++) {
+	    if (headers[i].getName().equalsIgnoreCase(name)) {
+                return headers[i].getValue();
+            }
+        }
+        return null;
+    }
+
+    // bad shortcut - it'll convert to string ( too early probably,
+    // encoding is guessed very late )
+    public String getHeader(String name) {
+	MessageBytes mh = getValue(name);
+	return mh != null ? mh.toString() : null;
+    }
+
+    // -------------------- Removing --------------------
+    /**
+     * Removes a header field with the specified name.  Does nothing
+     * if such a field could not be found.
+     * @param name the name of the header field to be removed
+     */
+    public void removeHeader(String name) {
+        // XXX
+        // warning: rather sticky code; heavily tuned
+
+        for (int i = 0; i < count; i++) {
+	    if (headers[i].getName().equalsIgnoreCase(name)) {
+	        // reset and swap with last header
+	        MimeHeaderField mh = headers[i];
+
+		mh.recycle();
+		headers[i] = headers[count - 1];
+		headers[count - 1] = mh;
+
+		count--;
+		i--;
+	    }
+	}
+    }
+}
+
+/** Enumerate the distinct header names.
+    Each nextElement() is O(n) ( a comparation is
+    done with all previous elements ).
+
+    This is less frequesnt than add() -
+    we want to keep add O(1).
+*/
+class NamesEnumerator implements Enumeration {
+    int pos;
+    int size;
+    String next;
+    MimeHeaders headers;
+
+    NamesEnumerator(MimeHeaders headers) {
+	this.headers=headers;
+	pos=0;
+	size = headers.size();
+	findNext();
+    }
+
+    private void findNext() {
+	next=null;
+	for(  ; pos< size; pos++ ) {
+	    next=headers.getName( pos ).toString();
+	    for( int j=0; j<pos ; j++ ) {
+		if( headers.getName( j ).equalsIgnoreCase( next )) {
+		    // duplicate.
+		    next=null;
+		    break;
+		}
+	    }
+	    if( next!=null ) {
+		// it's not a duplicate
+		break;
+	    }
+	}
+	// next time findNext is called it will try the
+	// next element
+	pos++;
+    }
+    
+    public boolean hasMoreElements() {
+	return next!=null;
+    }
+
+    public Object nextElement() {
+	String current=next;
+	findNext();
+	return current;
+    }
+}
+
+/** Enumerate the values for a (possibly ) multiple
+    value element.
+*/
+class ValuesEnumerator implements Enumeration {
+    int pos;
+    int size;
+    MessageBytes next;
+    MimeHeaders headers;
+    String name;
+
+    ValuesEnumerator(MimeHeaders headers, String name) {
+        this.name=name;
+	this.headers=headers;
+	pos=0;
+	size = headers.size();
+	findNext();
+    }
+
+    private void findNext() {
+	next=null;
+	for( ; pos< size; pos++ ) {
+	    MessageBytes n1=headers.getName( pos );
+	    if( n1.equalsIgnoreCase( name )) {
+		next=headers.getValue( pos );
+		break;
+	    }
+	}
+	pos++;
+    }
+    
+    public boolean hasMoreElements() {
+	return next!=null;
+    }
+
+    public Object nextElement() {
+	MessageBytes current=next;
+	findNext();
+	return current.toString();
+    }
+}
+
+class MimeHeaderField {
+    // multiple headers with same name - a linked list will
+    // speed up name enumerations and search ( both cpu and
+    // GC)
+    MimeHeaderField next;
+    MimeHeaderField prev; 
+    
+    protected final MessageBytes nameB = MessageBytes.newInstance();
+    protected final MessageBytes valueB = MessageBytes.newInstance();
+
+    /**
+     * Creates a new, uninitialized header field.
+     */
+    public MimeHeaderField() {
+    }
+
+    public void recycle() {
+	nameB.recycle();
+	valueB.recycle();
+	next=null;
+    }
+
+    public MessageBytes getName() {
+	return nameB;
+    }
+
+    public MessageBytes getValue() {
+	return valueB;
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/http/MimeMap.java b/connectors/util/java/org/apache/tomcat/util/http/MimeMap.java
new file mode 100644
index 0000000..fad0991
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/MimeMap.java
@@ -0,0 +1,186 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.http;
+
+import java.net.*;
+import java.util.*;
+
+
+/**
+ * A mime type map that implements the java.net.FileNameMap interface.
+ *
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author Jason Hunter [jch@eng.sun.com]
+ */
+public class MimeMap implements FileNameMap {
+
+    // Defaults - all of them are "well-known" types,
+    // you can add using normal web.xml.
+    
+    public static Hashtable defaultMap=new Hashtable(101);
+    static {
+        defaultMap.put("txt", "text/plain");
+        defaultMap.put("html","text/html");
+        defaultMap.put("htm", "text/html");
+        defaultMap.put("gif", "image/gif");
+        defaultMap.put("jpg", "image/jpeg");
+        defaultMap.put("jpe", "image/jpeg");
+        defaultMap.put("jpeg", "image/jpeg");
+		defaultMap.put("java", "text/plain");
+        defaultMap.put("body", "text/html");
+        defaultMap.put("rtx", "text/richtext");
+        defaultMap.put("tsv", "text/tab-separated-values");
+        defaultMap.put("etx", "text/x-setext");
+        defaultMap.put("ps", "application/x-postscript");
+        defaultMap.put("class", "application/java");
+        defaultMap.put("csh", "application/x-csh");
+        defaultMap.put("sh", "application/x-sh");
+        defaultMap.put("tcl", "application/x-tcl");
+        defaultMap.put("tex", "application/x-tex");
+        defaultMap.put("texinfo", "application/x-texinfo");
+        defaultMap.put("texi", "application/x-texinfo");
+        defaultMap.put("t", "application/x-troff");
+        defaultMap.put("tr", "application/x-troff");
+        defaultMap.put("roff", "application/x-troff");
+        defaultMap.put("man", "application/x-troff-man");
+        defaultMap.put("me", "application/x-troff-me");
+        defaultMap.put("ms", "application/x-wais-source");
+        defaultMap.put("src", "application/x-wais-source");
+        defaultMap.put("zip", "application/zip");
+        defaultMap.put("bcpio", "application/x-bcpio");
+        defaultMap.put("cpio", "application/x-cpio");
+        defaultMap.put("gtar", "application/x-gtar");
+        defaultMap.put("shar", "application/x-shar");
+        defaultMap.put("sv4cpio", "application/x-sv4cpio");
+        defaultMap.put("sv4crc", "application/x-sv4crc");
+        defaultMap.put("tar", "application/x-tar");
+        defaultMap.put("ustar", "application/x-ustar");
+        defaultMap.put("dvi", "application/x-dvi");
+        defaultMap.put("hdf", "application/x-hdf");
+        defaultMap.put("latex", "application/x-latex");
+        defaultMap.put("bin", "application/octet-stream");
+        defaultMap.put("oda", "application/oda");
+        defaultMap.put("pdf", "application/pdf");
+        defaultMap.put("ps", "application/postscript");
+        defaultMap.put("eps", "application/postscript");
+        defaultMap.put("ai", "application/postscript");
+        defaultMap.put("rtf", "application/rtf");
+        defaultMap.put("nc", "application/x-netcdf");
+        defaultMap.put("cdf", "application/x-netcdf");
+        defaultMap.put("cer", "application/x-x509-ca-cert");
+        defaultMap.put("exe", "application/octet-stream");
+        defaultMap.put("gz", "application/x-gzip");
+        defaultMap.put("Z", "application/x-compress");
+        defaultMap.put("z", "application/x-compress");
+        defaultMap.put("hqx", "application/mac-binhex40");
+        defaultMap.put("mif", "application/x-mif");
+        defaultMap.put("ief", "image/ief");
+        defaultMap.put("tiff", "image/tiff");
+        defaultMap.put("tif", "image/tiff");
+        defaultMap.put("ras", "image/x-cmu-raster");
+        defaultMap.put("pnm", "image/x-portable-anymap");
+        defaultMap.put("pbm", "image/x-portable-bitmap");
+        defaultMap.put("pgm", "image/x-portable-graymap");
+        defaultMap.put("ppm", "image/x-portable-pixmap");
+        defaultMap.put("rgb", "image/x-rgb");
+        defaultMap.put("xbm", "image/x-xbitmap");
+        defaultMap.put("xpm", "image/x-xpixmap");
+        defaultMap.put("xwd", "image/x-xwindowdump");
+        defaultMap.put("au", "audio/basic");
+        defaultMap.put("snd", "audio/basic");
+        defaultMap.put("aif", "audio/x-aiff");
+        defaultMap.put("aiff", "audio/x-aiff");
+        defaultMap.put("aifc", "audio/x-aiff");
+        defaultMap.put("wav", "audio/x-wav");
+        defaultMap.put("mpeg", "video/mpeg");
+        defaultMap.put("mpg", "video/mpeg");
+        defaultMap.put("mpe", "video/mpeg");
+        defaultMap.put("qt", "video/quicktime");
+        defaultMap.put("mov", "video/quicktime");
+        defaultMap.put("avi", "video/x-msvideo");
+        defaultMap.put("movie", "video/x-sgi-movie");
+        defaultMap.put("avx", "video/x-rad-screenplay");
+        defaultMap.put("wrl", "x-world/x-vrml");
+        defaultMap.put("mpv2", "video/mpeg2");
+        
+        /* Add XML related MIMEs */
+        
+        defaultMap.put("xml", "text/xml");
+        defaultMap.put("xsl", "text/xml");        
+        defaultMap.put("svg", "image/svg+xml");
+        defaultMap.put("svgz", "image/svg+xml");
+        defaultMap.put("wbmp", "image/vnd.wap.wbmp");
+        defaultMap.put("wml", "text/vnd.wap.wml");
+        defaultMap.put("wmlc", "application/vnd.wap.wmlc");
+        defaultMap.put("wmls", "text/vnd.wap.wmlscript");
+        defaultMap.put("wmlscriptc", "application/vnd.wap.wmlscriptc");
+    }
+    
+
+    private Hashtable map = new Hashtable();
+
+    public void addContentType(String extn, String type) {
+        map.put(extn, type.toLowerCase());
+    }
+
+    public Enumeration getExtensions() {
+        return map.keys();
+    }
+
+    public String getContentType(String extn) {
+        String type = (String)map.get(extn.toLowerCase());
+	if( type == null ) type=(String)defaultMap.get( extn );
+	return type;
+    }
+
+    public void removeContentType(String extn) {
+        map.remove(extn.toLowerCase());
+    }
+
+    /** Get extension of file, without fragment id
+     */
+    public static String getExtension( String fileName ) {
+        // play it safe and get rid of any fragment id
+        // that might be there
+	int length=fileName.length();
+	
+        int newEnd = fileName.lastIndexOf('#');
+	if( newEnd== -1 ) newEnd=length;
+	// Instead of creating a new string.
+	//         if (i != -1) {
+	//             fileName = fileName.substring(0, i);
+	//         }
+        int i = fileName.lastIndexOf('.', newEnd );
+        if (i != -1) {
+             return  fileName.substring(i + 1, newEnd );
+        } else {
+            // no extension, no content type
+            return null;
+        }
+    }
+    
+    public String getContentTypeFor(String fileName) {
+	String extn=getExtension( fileName );
+        if (extn!=null) {
+            return getContentType(extn);
+        } else {
+            // no extension, no content type
+            return null;
+        }
+    }
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/http/Parameters.java b/connectors/util/java/org/apache/tomcat/util/http/Parameters.java
new file mode 100644
index 0000000..aa571bb
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/Parameters.java
@@ -0,0 +1,606 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.http;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.buf.UDecoder;
+import org.apache.tomcat.util.collections.MultiMap;
+
+/**
+ * 
+ * @author Costin Manolache
+ */
+public final class Parameters extends MultiMap {
+
+    // Transition: we'll use the same Hashtable( String->String[] )
+    // for the beginning. When we are sure all accesses happen through
+    // this class - we can switch to MultiMap
+    private Hashtable paramHashStringArray=new Hashtable();
+    private boolean didQueryParameters=false;
+    private boolean didMerge=false;
+    
+    MessageBytes queryMB;
+    MimeHeaders  headers;
+
+    UDecoder urlDec;
+    MessageBytes decodedQuery=MessageBytes.newInstance();
+    
+    public static final int INITIAL_SIZE=4;
+
+    // Garbage-less parameter merging.
+    // In a sub-request with parameters, the new parameters
+    // will be stored in child. When a getParameter happens,
+    // the 2 are merged togheter. The child will be altered
+    // to contain the merged values - the parent is allways the
+    // original request.
+    private Parameters child=null;
+    private Parameters parent=null;
+    private Parameters currentChild=null;
+
+    String encoding=null;
+    String queryStringEncoding=null;
+    
+    /**
+     * 
+     */
+    public Parameters() {
+	super( INITIAL_SIZE );
+    }
+
+    public void setQuery( MessageBytes queryMB ) {
+	this.queryMB=queryMB;
+    }
+
+    public void setHeaders( MimeHeaders headers ) {
+	this.headers=headers;
+    }
+
+    public void setEncoding( String s ) {
+	encoding=s;
+	if(debug>0) log( "Set encoding to " + s );
+    }
+
+    public void setQueryStringEncoding( String s ) {
+	queryStringEncoding=s;
+	if(debug>0) log( "Set query string encoding to " + s );
+    }
+
+    public void recycle() {
+	super.recycle();
+	paramHashStringArray.clear();
+	didQueryParameters=false;
+	currentChild=null;
+	didMerge=false;
+	encoding=null;
+	decodedQuery.recycle();
+    }
+    
+    // -------------------- Sub-request support --------------------
+
+    public Parameters getCurrentSet() {
+	if( currentChild==null )
+	    return this;
+	return currentChild;
+    }
+    
+    /** Create ( or reuse ) a child that will be used during a sub-request.
+	All future changes ( setting query string, adding parameters )
+	will affect the child ( the parent request is never changed ).
+	Both setters and getters will return the data from the deepest
+	child, merged with data from parents.
+    */
+    public void push() {
+	// We maintain a linked list, that will grow to the size of the
+	// longest include chain.
+	// The list has 2 points of interest:
+	// - request.parameters() is the original request and head,
+	// - request.parameters().currentChild() is the current set.
+	// The ->child and parent<- links are preserved ( currentChild is not
+	// the last in the list )
+	
+	// create a new element in the linked list
+	// note that we reuse the child, if any - pop will not
+	// set child to null !
+	if( currentChild==null ) {
+	    currentChild=new Parameters();
+	    currentChild.setURLDecoder( urlDec );
+	    currentChild.parent=this;
+	    return;
+	}
+	if( currentChild.child==null ) {
+	    currentChild.child=new Parameters();
+	    currentChild.setURLDecoder( urlDec );
+	    currentChild.child.parent=currentChild;
+	} // it is not null if this object already had a child
+	// i.e. a deeper include() ( we keep it )
+
+	// the head will be the new element.
+	currentChild=currentChild.child;
+	currentChild.setEncoding( encoding );
+    }
+
+    /** Discard the last child. This happens when we return from a
+	sub-request and the parameters are locally modified.
+     */
+    public void pop() {
+	if( currentChild==null ) {
+	    throw new RuntimeException( "Attempt to pop without a push" );
+	}
+	currentChild.recycle();
+	currentChild=currentChild.parent;
+	// don't remove the top.
+    }
+    
+    // -------------------- Data access --------------------
+    // Access to the current name/values, no side effect ( processing ).
+    // You must explicitely call handleQueryParameters and the post methods.
+    
+    // This is the original data representation ( hash of String->String[])
+
+    public void addParameterValues( String key, String[] newValues) {
+        if ( key==null ) return;
+        String values[];
+        if (paramHashStringArray.containsKey(key)) {
+            String oldValues[] = (String[])paramHashStringArray.get(key);
+            values = new String[oldValues.length + newValues.length];
+            for (int i = 0; i < oldValues.length; i++) {
+                values[i] = oldValues[i];
+            }
+            for (int i = 0; i < newValues.length; i++) {
+                values[i+ oldValues.length] = newValues[i];
+            }
+        } else {
+            values = newValues;
+        }
+
+        paramHashStringArray.put(key, values);
+    }
+
+    public String[] getParameterValues(String name) {
+	handleQueryParameters();
+	// sub-request
+	if( currentChild!=null ) {
+	    currentChild.merge();
+	    return (String[])currentChild.paramHashStringArray.get(name);
+	}
+
+	// no "facade"
+	String values[]=(String[])paramHashStringArray.get(name);
+	return values;
+    }
+ 
+    public Enumeration getParameterNames() {
+	handleQueryParameters();
+	// Slow - the original code
+	if( currentChild!=null ) {
+	    currentChild.merge();
+	    return currentChild.paramHashStringArray.keys();
+	}
+
+	// merge in child
+        return paramHashStringArray.keys();
+    }
+
+    /** Combine the parameters from parent with our local ones
+     */
+    private void merge() {
+	// recursive
+	if( debug > 0 ) {
+	    log("Before merging " + this + " " + parent + " " + didMerge );
+	    log(  paramsAsString());
+	}
+	// Local parameters first - they take precedence as in spec.
+	handleQueryParameters();
+
+	// we already merged with the parent
+	if( didMerge ) return;
+
+	// we are the top level
+	if( parent==null ) return;
+
+	// Add the parent props to the child ( lower precedence )
+	parent.merge();
+	Hashtable parentProps=parent.paramHashStringArray;
+	merge2( paramHashStringArray , parentProps);
+	didMerge=true;
+	if(debug > 0 )
+	    log("After " + paramsAsString());
+    }
+
+
+    // Shortcut.
+    public String getParameter(String name ) {
+	String[] values = getParameterValues(name);
+        if (values != null) {
+	    if( values.length==0 ) return "";
+            return values[0];
+        } else {
+	    return null;
+        }
+    }
+    // -------------------- Processing --------------------
+    /** Process the query string into parameters
+     */
+    public void handleQueryParameters() {
+	if( didQueryParameters ) return;
+
+	didQueryParameters=true;
+
+	if( queryMB==null || queryMB.isNull() )
+	    return;
+	
+	if( debug > 0  )
+	    log( "Decoding query " + decodedQuery + " " + queryStringEncoding);
+
+        try {
+            decodedQuery.duplicate( queryMB );
+        } catch (IOException e) {
+            // Can't happen, as decodedQuery can't overflow
+            e.printStackTrace();
+        }
+        processParameters( decodedQuery, queryStringEncoding );
+    }
+
+    // --------------------
+    
+    /** Combine 2 hashtables into a new one.
+     *  ( two will be added to one ).
+     *  Used to combine child parameters ( RequestDispatcher's query )
+     *  with parent parameters ( original query or parent dispatcher )
+     */
+    private static void merge2(Hashtable one, Hashtable two ) {
+        Enumeration e = two.keys();
+
+	while (e.hasMoreElements()) {
+	    String name = (String) e.nextElement();
+	    String[] oneValue = (String[]) one.get(name);
+	    String[] twoValue = (String[]) two.get(name);
+	    String[] combinedValue;
+
+	    if (twoValue == null) {
+		continue;
+	    } else {
+		if( oneValue==null ) {
+		    combinedValue = new String[twoValue.length];
+		    System.arraycopy(twoValue, 0, combinedValue,
+				     0, twoValue.length);
+		} else {
+		    combinedValue = new String[oneValue.length +
+					       twoValue.length];
+		    System.arraycopy(oneValue, 0, combinedValue, 0,
+				     oneValue.length);
+		    System.arraycopy(twoValue, 0, combinedValue,
+				     oneValue.length, twoValue.length);
+		}
+		one.put(name, combinedValue);
+	    }
+	}
+    }
+
+    // incredibly inefficient data representation for parameters,
+    // until we test the new one
+    private void addParam( String key, String value ) {
+	if( key==null ) return;
+	String values[];
+	if (paramHashStringArray.containsKey(key)) {
+	    String oldValues[] = (String[])paramHashStringArray.
+		get(key);
+	    values = new String[oldValues.length + 1];
+	    for (int i = 0; i < oldValues.length; i++) {
+		values[i] = oldValues[i];
+	    }
+	    values[oldValues.length] = value;
+	} else {
+	    values = new String[1];
+	    values[0] = value;
+	}
+	
+	
+	paramHashStringArray.put(key, values);
+    }
+
+    public void setURLDecoder( UDecoder u ) {
+	urlDec=u;
+    }
+
+    // -------------------- Parameter parsing --------------------
+
+    // This code is not used right now - it's the optimized version
+    // of the above.
+
+    // we are called from a single thread - we can do it the hard way
+    // if needed
+    ByteChunk tmpName=new ByteChunk();
+    ByteChunk tmpValue=new ByteChunk();
+    CharChunk tmpNameC=new CharChunk(1024);
+    CharChunk tmpValueC=new CharChunk(1024);
+    
+    public void processParameters( byte bytes[], int start, int len ) {
+        processParameters(bytes, start, len, encoding);
+    }
+
+    public void processParameters( byte bytes[], int start, int len, 
+                                   String enc ) {
+	int end=start+len;
+	int pos=start;
+	
+	if( debug>0 ) 
+	    log( "Bytes: " + new String( bytes, start, len ));
+
+        do {
+	    boolean noEq=false;
+	    int valStart=-1;
+	    int valEnd=-1;
+	    
+	    int nameStart=pos;
+	    int nameEnd=ByteChunk.indexOf(bytes, nameStart, end, '=' );
+	    // Workaround for a&b&c encoding
+	    int nameEnd2=ByteChunk.indexOf(bytes, nameStart, end, '&' );
+	    if( (nameEnd2!=-1 ) &&
+		( nameEnd==-1 || nameEnd > nameEnd2) ) {
+		nameEnd=nameEnd2;
+		noEq=true;
+		valStart=nameEnd;
+		valEnd=nameEnd;
+		if( debug>0) log("no equal " + nameStart + " " + nameEnd + " " + new String(bytes, nameStart, nameEnd-nameStart) );
+	    }
+	    if( nameEnd== -1 ) 
+		nameEnd=end;
+
+	    if( ! noEq ) {
+		valStart= (nameEnd < end) ? nameEnd+1 : end;
+		valEnd=ByteChunk.indexOf(bytes, valStart, end, '&');
+		if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart;
+	    }
+	    
+	    pos=valEnd+1;
+	    
+	    if( nameEnd<=nameStart ) {
+		continue;
+		// invalid chunk - it's better to ignore
+		// XXX log it ?
+	    }
+	    tmpName.setBytes( bytes, nameStart, nameEnd-nameStart );
+	    tmpValue.setBytes( bytes, valStart, valEnd-valStart );
+
+            try {
+                addParam( urlDecode(tmpName, enc), urlDecode(tmpValue, enc) );
+            } catch (IOException e) {
+                // Exception during character decoding: skip parameter
+            }
+
+	    tmpName.recycle();
+	    tmpValue.recycle();
+
+	} while( pos<end );
+    }
+
+    private String urlDecode(ByteChunk bc, String enc)
+        throws IOException {
+        if( urlDec==null ) {
+            urlDec=new UDecoder();   
+        }
+        urlDec.convert(bc);
+        String result = null;
+        if (enc != null) {
+            bc.setEncoding(enc);
+            result = bc.toString();
+        } else {
+            CharChunk cc = tmpNameC;
+            cc.allocate(bc.getLength(), -1);
+            // Default encoding: fast conversion
+            byte[] bbuf = bc.getBuffer();
+            char[] cbuf = cc.getBuffer();
+            int start = bc.getStart();
+            for (int i = 0; i < bc.getLength(); i++) {
+                cbuf[i] = (char) (bbuf[i + start] & 0xff);
+            }
+            cc.setChars(cbuf, 0, bc.getLength());
+            result = cc.toString();
+            cc.recycle();
+        }
+        return result;
+    }
+
+    public void processParameters( char chars[], int start, int len ) {
+	int end=start+len;
+	int pos=start;
+	
+	if( debug>0 ) 
+	    log( "Chars: " + new String( chars, start, len ));
+        do {
+	    boolean noEq=false;
+	    int nameStart=pos;
+	    int valStart=-1;
+	    int valEnd=-1;
+	    
+	    int nameEnd=CharChunk.indexOf(chars, nameStart, end, '=' );
+	    int nameEnd2=CharChunk.indexOf(chars, nameStart, end, '&' );
+	    if( (nameEnd2!=-1 ) &&
+		( nameEnd==-1 || nameEnd > nameEnd2) ) {
+		nameEnd=nameEnd2;
+		noEq=true;
+		valStart=nameEnd;
+		valEnd=nameEnd;
+		if( debug>0) log("no equal " + nameStart + " " + nameEnd + " " + new String(chars, nameStart, nameEnd-nameStart) );
+	    }
+	    if( nameEnd== -1 ) nameEnd=end;
+	    
+	    if( ! noEq ) {
+		valStart= (nameEnd < end) ? nameEnd+1 : end;
+		valEnd=CharChunk.indexOf(chars, valStart, end, '&');
+		if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart;
+	    }
+	    
+	    pos=valEnd+1;
+	    
+	    if( nameEnd<=nameStart ) {
+		continue;
+		// invalid chunk - no name, it's better to ignore
+		// XXX log it ?
+	    }
+	    
+	    try {
+		tmpNameC.append( chars, nameStart, nameEnd-nameStart );
+		tmpValueC.append( chars, valStart, valEnd-valStart );
+
+		if( debug > 0 )
+		    log( tmpNameC + "= " + tmpValueC);
+
+		if( urlDec==null ) {
+		    urlDec=new UDecoder();   
+		}
+
+		urlDec.convert( tmpNameC );
+		urlDec.convert( tmpValueC );
+
+		if( debug > 0 )
+		    log( tmpNameC + "= " + tmpValueC);
+		
+		addParam( tmpNameC.toString(), tmpValueC.toString() );
+	    } catch( IOException ex ) {
+		ex.printStackTrace();
+	    }
+
+	    tmpNameC.recycle();
+	    tmpValueC.recycle();
+
+	} while( pos<end );
+    }
+    
+    public void processParameters( MessageBytes data ) {
+        processParameters(data, encoding);
+    }
+
+    public void processParameters( MessageBytes data, String encoding ) {
+	if( data==null || data.isNull() || data.getLength() <= 0 ) return;
+
+	if( data.getType() == MessageBytes.T_BYTES ) {
+	    ByteChunk bc=data.getByteChunk();
+	    processParameters( bc.getBytes(), bc.getOffset(),
+			       bc.getLength(), encoding);
+	} else {
+	    if (data.getType()!= MessageBytes.T_CHARS ) 
+		data.toChars();
+	    CharChunk cc=data.getCharChunk();
+	    processParameters( cc.getChars(), cc.getOffset(),
+			       cc.getLength());
+	}
+    }
+
+    /** Debug purpose
+     */
+    public String paramsAsString() {
+	StringBuffer sb=new StringBuffer();
+	Enumeration en= paramHashStringArray.keys();
+	while( en.hasMoreElements() ) {
+	    String k=(String)en.nextElement();
+	    sb.append( k ).append("=");
+	    String v[]=(String[])paramHashStringArray.get( k );
+	    for( int i=0; i<v.length; i++ )
+		sb.append( v[i] ).append(",");
+	    sb.append("\n");
+	}
+	return sb.toString();
+    }
+
+    private static int debug=0;
+    private void log(String s ) {
+	System.out.println("Parameters: " + s );
+    }
+   
+    // -------------------- Old code, needs rewrite --------------------
+    
+    /** Used by RequestDispatcher
+     */
+    public void processParameters( String str ) {
+	int end=str.length();
+	int pos=0;
+	if( debug > 0)
+	    log("String: " + str );
+	
+        do {
+	    boolean noEq=false;
+	    int valStart=-1;
+	    int valEnd=-1;
+	    
+	    int nameStart=pos;
+	    int nameEnd=str.indexOf('=', nameStart );
+	    int nameEnd2=str.indexOf('&', nameStart );
+	    if( nameEnd2== -1 ) nameEnd2=end;
+	    if( (nameEnd2!=-1 ) &&
+		( nameEnd==-1 || nameEnd > nameEnd2) ) {
+		nameEnd=nameEnd2;
+		noEq=true;
+		valStart=nameEnd;
+		valEnd=nameEnd;
+		if( debug>0) log("no equal " + nameStart + " " + nameEnd + " " + str.substring(nameStart, nameEnd) );
+	    }
+
+	    if( nameEnd== -1 ) nameEnd=end;
+
+	    if( ! noEq ) {
+		valStart=nameEnd+1;
+		valEnd=str.indexOf('&', valStart);
+		if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart;
+	    }
+	    
+	    pos=valEnd+1;
+	    
+	    if( nameEnd<=nameStart ) {
+		continue;
+	    }
+	    if( debug>0)
+		log( "XXX " + nameStart + " " + nameEnd + " "
+		     + valStart + " " + valEnd );
+	    
+	    try {
+		tmpNameC.append(str, nameStart, nameEnd-nameStart );
+		tmpValueC.append(str, valStart, valEnd-valStart );
+	    
+		if( debug > 0 )
+		    log( tmpNameC + "= " + tmpValueC);
+
+		if( urlDec==null ) {
+		    urlDec=new UDecoder();   
+		}
+
+		urlDec.convert( tmpNameC );
+		urlDec.convert( tmpValueC );
+
+		if( debug > 0 )
+		    log( tmpNameC + "= " + tmpValueC);
+		
+		addParam( tmpNameC.toString(), tmpValueC.toString() );
+	    } catch( IOException ex ) {
+		ex.printStackTrace();
+	    }
+
+	    tmpNameC.recycle();
+	    tmpValueC.recycle();
+
+	} while( pos<end );
+    }
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/http/ServerCookie.java b/connectors/util/java/org/apache/tomcat/util/http/ServerCookie.java
new file mode 100644
index 0000000..27f6393
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/ServerCookie.java
@@ -0,0 +1,293 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.http;
+
+import java.io.Serializable;
+import java.text.FieldPosition;
+import java.util.Date;
+
+import org.apache.tomcat.util.buf.DateTool;
+import org.apache.tomcat.util.buf.MessageBytes;
+
+
+/**
+ *  Server-side cookie representation.
+ *   Allows recycling and uses MessageBytes as low-level
+ *  representation ( and thus the byte-> char conversion can be delayed
+ *  until we know the charset ).
+ *
+ *  Tomcat.core uses this recyclable object to represent cookies,
+ *  and the facade will convert it to the external representation.
+ */
+public class ServerCookie implements Serializable {
+    private MessageBytes name=MessageBytes.newInstance();
+    private MessageBytes value=MessageBytes.newInstance();
+
+    private MessageBytes comment=MessageBytes.newInstance();    // ;Comment=VALUE
+    private MessageBytes domain=MessageBytes.newInstance();    // ;Domain=VALUE ...
+
+    private int maxAge = -1;	// ;Max-Age=VALUE
+				// ;Discard ... implied by maxAge < 0
+    // RFC2109: maxAge=0 will end a session
+    private MessageBytes path=MessageBytes.newInstance();	// ;Path=VALUE .
+    private boolean secure;	// ;Secure
+    private int version = 0;	// ;Version=1
+
+    //XXX CommentURL, Port -> use notes ?
+    
+    public ServerCookie() {
+
+    }
+
+    public void recycle() {
+        path.recycle();
+    	name.recycle();
+    	value.recycle();
+    	comment.recycle();
+    	maxAge=-1;
+    	path.recycle();
+        domain.recycle();
+    	version=0;
+    	secure=false;
+    }
+
+    public MessageBytes getComment() {
+	return comment;
+    }
+
+    public MessageBytes getDomain() {
+	return domain;
+    }
+
+    public void setMaxAge(int expiry) {
+	maxAge = expiry;
+    }
+
+    public int getMaxAge() {
+	return maxAge;
+    }
+
+
+    public MessageBytes getPath() {
+	return path;
+    }
+
+    public void setSecure(boolean flag) {
+	secure = flag;
+    }
+
+    public boolean getSecure() {
+	return secure;
+    }
+
+    public MessageBytes getName() {
+	return name;
+    }
+
+    public MessageBytes getValue() {
+	return value;
+    }
+
+    public int getVersion() {
+	return version;
+    }
+
+
+    public void setVersion(int v) {
+	version = v;
+    }
+
+
+    // -------------------- utils --------------------
+
+    public String toString() {
+	return "Cookie " + getName() + "=" + getValue() + " ; "
+	    + getVersion() + " " + getPath() + " " + getDomain();
+    }
+    
+    // Note -- disabled for now to allow full Netscape compatibility
+    // from RFC 2068, token special case characters
+    //
+    // private static final String tspecials = "()<>@,;:\\\"/[]?={} \t";
+    private static final String tspecials = ",;";
+
+    /*
+     * Tests a string and returns true if the string counts as a
+     * reserved token in the Java language.
+     *
+     * @param value		the <code>String</code> to be tested
+     *
+     * @return			<code>true</code> if the <code>String</code> is
+     *				a reserved token; <code>false</code>
+     *				if it is not
+     */
+    public static boolean isToken(String value) {
+	if( value==null) return true;
+	int len = value.length();
+
+	for (int i = 0; i < len; i++) {
+	    char c = value.charAt(i);
+
+	    if (c < 0x20 || c >= 0x7f || tspecials.indexOf(c) != -1)
+		return false;
+	}
+	return true;
+    }
+
+    public static boolean checkName( String name ) {
+	if (!isToken(name)
+		|| name.equalsIgnoreCase("Comment")	// rfc2019
+		|| name.equalsIgnoreCase("Discard")	// 2019++
+		|| name.equalsIgnoreCase("Domain")
+		|| name.equalsIgnoreCase("Expires")	// (old cookies)
+		|| name.equalsIgnoreCase("Max-Age")	// rfc2019
+		|| name.equalsIgnoreCase("Path")
+		|| name.equalsIgnoreCase("Secure")
+		|| name.equalsIgnoreCase("Version")
+	    ) {
+	    return false;
+	}
+	return true;
+    }
+
+    // -------------------- Cookie parsing tools
+
+    
+    /** Return the header name to set the cookie, based on cookie
+     *  version
+     */
+    public String getCookieHeaderName() {
+	return getCookieHeaderName(version);
+    }
+
+    /** Return the header name to set the cookie, based on cookie
+     *  version
+     */
+    public static String getCookieHeaderName(int version) {
+	if( dbg>0 ) log( (version==1) ? "Set-Cookie2" : "Set-Cookie");
+        if (version == 1) {
+	    // RFC2109
+	    return "Set-Cookie";
+	    // XXX RFC2965 is not standard yet, and Set-Cookie2
+	    // is not supported by Netscape 4, 6, IE 3, 5 .
+	    // It is supported by Lynx, and there is hope 
+	    //	    return "Set-Cookie2";
+        } else {
+	    // Old Netscape
+	    return "Set-Cookie";
+        }
+    }
+
+    private static final String ancientDate=DateTool.formatOldCookie(new Date(10000));
+
+    public static void appendCookieValue( StringBuffer buf,
+					  int version,
+					  String name,
+					  String value,
+					  String path,
+					  String domain,
+					  String comment,
+					  int maxAge,
+					  boolean isSecure )
+    {
+        // this part is the same for all cookies
+	buf.append( name );
+        buf.append("=");
+        maybeQuote(version, buf, value);
+
+	// XXX Netscape cookie: "; "
+ 	// add version 1 specific information
+	if (version == 1) {
+	    // Version=1 ... required
+	    buf.append ("; Version=1");
+
+	    // Comment=comment
+	    if ( comment!=null ) {
+		buf.append ("; Comment=");
+		maybeQuote (version, buf, comment);
+	    }
+	}
+	
+	// add domain information, if present
+
+	if (domain!=null) {
+	    buf.append("; Domain=");
+	    maybeQuote (version, buf, domain);
+	}
+
+	// Max-Age=secs/Discard ... or use old "Expires" format
+	if (maxAge >= 0) {
+	    if (version == 0) {
+		// XXX XXX XXX We need to send both, for
+		// interoperatibility (long word )
+		buf.append ("; Expires=");
+		// Wdy, DD-Mon-YY HH:MM:SS GMT ( Expires netscape format )
+		// To expire we need to set the time back in future
+		// ( pfrieden@dChain.com )
+                if (maxAge == 0)
+		    buf.append( ancientDate );
+		else
+                    DateTool.formatOldCookie
+                        (new Date( System.currentTimeMillis() +
+                                   maxAge *1000L), buf,
+                         new FieldPosition(0));
+
+	    } else {
+		buf.append ("; Max-Age=");
+		buf.append (maxAge);
+	    }
+	}
+
+	// Path=path
+	if (path!=null) {
+	    buf.append ("; Path=");
+	    maybeQuote (version, buf, path);
+	}
+
+	// Secure
+	if (isSecure) {
+	  buf.append ("; Secure");
+	}
+	
+	
+    }
+
+    public static void maybeQuote (int version, StringBuffer buf,
+                                    String value)
+    {
+	// special case - a \n or \r  shouldn't happen in any case
+	if ( isToken (value))
+	  buf.append (value);
+	else {
+	    if(version==0)
+		throw new IllegalArgumentException( value );
+	    else {
+		buf.append ('"');
+		buf.append (value);
+		buf.append ('"');
+	    }
+	}
+    }
+
+    // log
+    static final int dbg=1;
+    public static void log(String s ) {
+	System.out.println("ServerCookie: " + s);
+    }
+
+}
+
diff --git a/connectors/util/java/org/apache/tomcat/util/http/mapper/Mapper.java b/connectors/util/java/org/apache/tomcat/util/http/mapper/Mapper.java
new file mode 100644
index 0000000..20c68df
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/mapper/Mapper.java
@@ -0,0 +1,1393 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.http.mapper;
+
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+
+import org.apache.tomcat.util.buf.CharChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.buf.Ascii;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Mapper, which implements the servlet API mapping rules (which are derived
+ * from the HTTP rules).
+ *
+ * @author Remy Maucherat
+ */
+public final class Mapper {
+
+
+    private static org.apache.commons.logging.Log logger =
+        org.apache.commons.logging.LogFactory.getLog(Mapper.class);
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Array containing the virtual hosts definitions.
+     */
+    protected Host[] hosts = new Host[0];
+
+
+    /**
+     * Default host name.
+     */
+    protected String defaultHostName = null;
+
+    /**
+     * Context associated with this wrapper, used for wrapper mapping.
+     */
+    protected Context context = new Context();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Get default host.
+     *
+     * @return Default host name
+     */
+    public String getDefaultHostName() {
+        return defaultHostName;
+    }
+
+
+    /**
+     * Set default host.
+     *
+     * @param defaultHostName Default host name
+     */
+    public void setDefaultHostName(String defaultHostName) {
+        this.defaultHostName = defaultHostName;
+    }
+
+    /**
+     * Add a new host to the mapper.
+     *
+     * @param name Virtual host name
+     * @param host Host object
+     */
+    public synchronized void addHost(String name, String[] aliases,
+                                     Object host) {
+        Host[] newHosts = new Host[hosts.length + 1];
+        Host newHost = new Host();
+        ContextList contextList = new ContextList();
+        newHost.name = name;
+        newHost.contextList = contextList;
+        newHost.object = host;
+        if (insertMap(hosts, newHosts, newHost)) {
+            hosts = newHosts;
+        }
+        for (int i = 0; i < aliases.length; i++) {
+            newHosts = new Host[hosts.length + 1];
+            newHost = new Host();
+            newHost.name = aliases[i];
+            newHost.contextList = contextList;
+            newHost.object = host;
+            if (insertMap(hosts, newHosts, newHost)) {
+                hosts = newHosts;
+            }
+        }
+    }
+
+
+    /**
+     * Remove a host from the mapper.
+     *
+     * @param name Virtual host name
+     */
+    public synchronized void removeHost(String name) {
+        // Find and remove the old host
+        int pos = find(hosts, name);
+        if (pos < 0) {
+            return;
+        }
+        Object host = hosts[pos].object;
+        Host[] newHosts = new Host[hosts.length - 1];
+        if (removeMap(hosts, newHosts, name)) {
+            hosts = newHosts;
+        }
+        // Remove all aliases (they will map to the same host object)
+        for (int i = 0; i < newHosts.length; i++) {
+            if (newHosts[i].object == host) {
+                Host[] newHosts2 = new Host[hosts.length - 1];
+                if (removeMap(hosts, newHosts2, newHosts[i].name)) {
+                    hosts = newHosts2;
+                }
+            }
+        }
+    }
+
+    public String[] getHosts() {
+        String hostN[] = new String[hosts.length];
+        for( int i = 0; i < hosts.length; i++ ) {
+            hostN[i] = hosts[i].name;
+        }
+        return hostN;
+    }
+
+
+    /**
+     * Set context, used for wrapper mapping (request dispatcher).
+     *
+     * @param welcomeResources Welcome files defined for this context
+     * @param resources Static resources of the context
+     */
+    public void setContext(String path, String[] welcomeResources,
+                           javax.naming.Context resources) {
+        context.name = path;
+        context.welcomeResources = welcomeResources;
+        context.resources = resources;
+    }
+
+
+    /**
+     * Add a new Context to an existing Host.
+     *
+     * @param hostName Virtual host name this context belongs to
+     * @param path Context path
+     * @param context Context object
+     * @param welcomeResources Welcome files defined for this context
+     * @param resources Static resources of the context
+     */
+    public void addContext
+        (String hostName, String path, Object context,
+         String[] welcomeResources, javax.naming.Context resources) {
+
+        Host[] hosts = this.hosts;
+        int pos = find(hosts, hostName);
+        if( pos <0 ) {
+            addHost(hostName, new String[0], "");
+            hosts = this.hosts;
+            pos = find(hosts, hostName);
+        }
+        if (pos < 0) {
+            logger.error("No host found: " + hostName);
+        }
+        Host host = hosts[pos];
+        if (host.name.equals(hostName)) {
+            Context[] contexts = host.contextList.contexts;
+            // Update nesting
+            int slashCount = slashCount(path);
+            if (slashCount > host.contextList.nesting) {
+                host.contextList.nesting = slashCount;
+            }
+            synchronized (host) {
+                Context[] newContexts = new Context[contexts.length + 1];
+                Context newContext = new Context();
+                newContext.name = path;
+                newContext.object = context;
+                newContext.welcomeResources = welcomeResources;
+                newContext.resources = resources;
+                if (insertMap(contexts, newContexts, newContext)) {
+                    host.contextList.contexts = newContexts;
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Remove a context from an existing host.
+     *
+     * @param hostName Virtual host name this context belongs to
+     * @param path Context path
+     */
+    public void removeContext(String hostName, String path) {
+        Host[] hosts = this.hosts;
+        int pos = find(hosts, hostName);
+        if (pos < 0) {
+            return;
+        }
+        Host host = hosts[pos];
+        if (host.name.equals(hostName)) {
+            Context[] contexts = host.contextList.contexts;
+            if( contexts.length == 0 ){
+                return;
+            }
+            synchronized (host) {
+                Context[] newContexts = new Context[contexts.length - 1];
+                if (removeMap(contexts, newContexts, path)) {
+                    host.contextList.contexts = newContexts;
+                    // Recalculate nesting
+                    host.contextList.nesting = 0;
+                    for (int i = 0; i < newContexts.length; i++) {
+                        int slashCount = slashCount(newContexts[i].name);
+                        if (slashCount > host.contextList.nesting) {
+                            host.contextList.nesting = slashCount;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Return all contexts, in //HOST/PATH form
+     *
+     * @return The context names
+     */
+    public String[] getContextNames() {
+        List list=new ArrayList();
+        for( int i=0; i<hosts.length; i++ ) {
+            for( int j=0; j<hosts[i].contextList.contexts.length; j++ ) {
+                String cname=hosts[i].contextList.contexts[j].name;
+                list.add("//" + hosts[i].name +
+                        (cname.startsWith("/") ? cname : "/"));
+            }
+        }
+        String res[] = new String[list.size()];
+        return (String[])list.toArray(res);
+    }
+
+
+    /**
+     * Add a new Wrapper to an existing Context.
+     *
+     * @param hostName Virtual host name this wrapper belongs to
+     * @param contextPath Context path this wrapper belongs to
+     * @param path Wrapper mapping
+     * @param wrapper Wrapper object
+     */
+    public void addWrapper(String hostName, String contextPath, String path,
+                           Object wrapper) {
+        addWrapper(hostName, contextPath, path, wrapper, false);
+    }
+
+
+    public void addWrapper(String hostName, String contextPath, String path,
+                           Object wrapper, boolean jspWildCard) {
+        Host[] hosts = this.hosts;
+        int pos = find(hosts, hostName);
+        if (pos < 0) {
+            return;
+        }
+        Host host = hosts[pos];
+        if (host.name.equals(hostName)) {
+            Context[] contexts = host.contextList.contexts;
+            int pos2 = find(contexts, contextPath);
+            if( pos2<0 ) {
+                logger.error("No context found: " + contextPath );
+                return;
+            }
+            Context context = contexts[pos2];
+            if (context.name.equals(contextPath)) {
+                addWrapper(context, path, wrapper, jspWildCard);
+            }
+        }
+    }
+
+
+    /**
+     * Add a wrapper to the context associated with this wrapper.
+     *
+     * @param path Wrapper mapping
+     * @param wrapper The Wrapper object
+     */
+    public void addWrapper(String path, Object wrapper) {
+        addWrapper(context, path, wrapper);
+    }
+
+
+    public void addWrapper(String path, Object wrapper, boolean jspWildCard) {
+        addWrapper(context, path, wrapper, jspWildCard);
+    }
+
+
+    protected void addWrapper(Context context, String path, Object wrapper) {
+        addWrapper(context, path, wrapper, false);
+    }
+
+
+    /**
+     * Adds a wrapper to the given context.
+     *
+     * @param context The context to which to add the wrapper
+     * @param path Wrapper mapping
+     * @param wrapper The Wrapper object
+     * @param jspWildCard true if the wrapper corresponds to the JspServlet
+     * and the mapping path contains a wildcard; false otherwise
+     */
+    protected void addWrapper(Context context, String path, Object wrapper,
+                              boolean jspWildCard) {
+
+        synchronized (context) {
+            Wrapper newWrapper = new Wrapper();
+            newWrapper.object = wrapper;
+            newWrapper.jspWildCard = jspWildCard;
+            if (path.endsWith("/*")) {
+                // Wildcard wrapper
+                newWrapper.name = path.substring(0, path.length() - 2);
+                Wrapper[] oldWrappers = context.wildcardWrappers;
+                Wrapper[] newWrappers =
+                    new Wrapper[oldWrappers.length + 1];
+                if (insertMap(oldWrappers, newWrappers, newWrapper)) {
+                    context.wildcardWrappers = newWrappers;
+                    int slashCount = slashCount(newWrapper.name);
+                    if (slashCount > context.nesting) {
+                        context.nesting = slashCount;
+                    }
+                }
+            } else if (path.startsWith("*.")) {
+                // Extension wrapper
+                newWrapper.name = path.substring(2);
+                Wrapper[] oldWrappers = context.extensionWrappers;
+                Wrapper[] newWrappers =
+                    new Wrapper[oldWrappers.length + 1];
+                if (insertMap(oldWrappers, newWrappers, newWrapper)) {
+                    context.extensionWrappers = newWrappers;
+                }
+            } else if (path.equals("/")) {
+                // Default wrapper
+                newWrapper.name = "";
+                context.defaultWrapper = newWrapper;
+            } else {
+                // Exact wrapper
+                newWrapper.name = path;
+                Wrapper[] oldWrappers = context.exactWrappers;
+                Wrapper[] newWrappers =
+                    new Wrapper[oldWrappers.length + 1];
+                if (insertMap(oldWrappers, newWrappers, newWrapper)) {
+                    context.exactWrappers = newWrappers;
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Remove a wrapper from the context associated with this wrapper.
+     *
+     * @param path Wrapper mapping
+     */
+    public void removeWrapper(String path) {
+        removeWrapper(context, path);
+    }
+
+
+    /**
+     * Remove a wrapper from an existing context.
+     *
+     * @param hostName Virtual host name this wrapper belongs to
+     * @param contextPath Context path this wrapper belongs to
+     * @param path Wrapper mapping
+     */
+    public void removeWrapper
+        (String hostName, String contextPath, String path) {
+        Host[] hosts = this.hosts;
+        int pos = find(hosts, hostName);
+        if (pos < 0) {
+            return;
+        }
+        Host host = hosts[pos];
+        if (host.name.equals(hostName)) {
+            Context[] contexts = host.contextList.contexts;
+            int pos2 = find(contexts, contextPath);
+            if (pos2 < 0) {
+                return;
+            }
+            Context context = contexts[pos2];
+            if (context.name.equals(contextPath)) {
+                removeWrapper(context, path);
+            }
+        }
+    }
+
+    protected void removeWrapper(Context context, String path) {
+        synchronized (context) {
+            if (path.endsWith("/*")) {
+                // Wildcard wrapper
+                String name = path.substring(0, path.length() - 2);
+                Wrapper[] oldWrappers = context.wildcardWrappers;
+                Wrapper[] newWrappers =
+                    new Wrapper[oldWrappers.length - 1];
+                if (removeMap(oldWrappers, newWrappers, name)) {
+                    // Recalculate nesting
+                    context.nesting = 0;
+                    for (int i = 0; i < newWrappers.length; i++) {
+                        int slashCount = slashCount(newWrappers[i].name);
+                        if (slashCount > context.nesting) {
+                            context.nesting = slashCount;
+                        }
+                    }
+                    context.wildcardWrappers = newWrappers;
+                }
+            } else if (path.startsWith("*.")) {
+                // Extension wrapper
+                String name = path.substring(2);
+                Wrapper[] oldWrappers = context.extensionWrappers;
+                Wrapper[] newWrappers =
+                    new Wrapper[oldWrappers.length - 1];
+                if (removeMap(oldWrappers, newWrappers, name)) {
+                    context.extensionWrappers = newWrappers;
+                }
+            } else if (path.equals("/")) {
+                // Default wrapper
+                context.defaultWrapper = null;
+            } else {
+                // Exact wrapper
+                String name = path;
+                Wrapper[] oldWrappers = context.exactWrappers;
+                Wrapper[] newWrappers =
+                    new Wrapper[oldWrappers.length - 1];
+                if (removeMap(oldWrappers, newWrappers, name)) {
+                    context.exactWrappers = newWrappers;
+                }
+            }
+        }
+    }
+
+    public String getWrappersString( String host, String context ) {
+        String names[]=getWrapperNames(host, context);
+        StringBuffer sb=new StringBuffer();
+        for( int i=0; i<names.length; i++ ) {
+            sb.append(names[i]).append(":");
+        }
+        return sb.toString();
+    }
+
+    public String[] getWrapperNames( String host, String context ) {
+        List list=new ArrayList();
+        if( host==null ) host="";
+        if( context==null ) context="";
+        for( int i=0; i<hosts.length; i++ ) {
+            if( ! host.equals( hosts[i].name ))
+                continue;
+            for( int j=0; j<hosts[i].contextList.contexts.length; j++ ) {
+                if( ! context.equals( hosts[i].contextList.contexts[j].name))
+                    continue;
+                // found the context
+                Context ctx=hosts[i].contextList.contexts[j];
+                list.add( ctx.defaultWrapper.path);
+                for( int k=0; k<ctx.exactWrappers.length; k++ ) {
+                    list.add( ctx.exactWrappers[k].path);
+                }
+                for( int k=0; k<ctx.wildcardWrappers.length; k++ ) {
+                    list.add( ctx.wildcardWrappers[k].path + "*");
+                }
+                for( int k=0; k<ctx.extensionWrappers.length; k++ ) {
+                    list.add( "*." + ctx.extensionWrappers[k].path);
+                }
+            }
+        }
+        String res[]=new String[list.size()];
+        return (String[])list.toArray(res);
+    }
+
+
+
+    /**
+     * Map the specified host name and URI, mutating the given mapping data.
+     *
+     * @param host Virtual host name
+     * @param uri URI
+     * @param mappingData This structure will contain the result of the mapping
+     *                    operation
+     */
+    public void map(MessageBytes host, MessageBytes uri,
+                    MappingData mappingData)
+        throws Exception {
+
+        host.toChars();
+        uri.toChars();
+        internalMap(host.getCharChunk(), uri.getCharChunk(), mappingData);
+
+    }
+
+
+    /**
+     * Map the specified URI relative to the context,
+     * mutating the given mapping data.
+     *
+     * @param uri URI
+     * @param mappingData This structure will contain the result of the mapping
+     *                    operation
+     */
+    public void map(MessageBytes uri, MappingData mappingData)
+        throws Exception {
+
+        uri.toChars();
+        CharChunk uricc = uri.getCharChunk();
+        uricc.setLimit(-1);
+        internalMapWrapper(context, uricc, mappingData);
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Map the specified URI.
+     */
+    private final void internalMap(CharChunk host, CharChunk uri,
+                                   MappingData mappingData)
+        throws Exception {
+
+        uri.setLimit(-1);
+
+        Context[] contexts = null;
+        Context context = null;
+        int nesting = 0;
+
+        // Virtual host mapping
+        if (mappingData.host == null) {
+            Host[] hosts = this.hosts;
+            int pos = findIgnoreCase(hosts, host);
+            if ((pos != -1) && (host.equalsIgnoreCase(hosts[pos].name))) {
+                mappingData.host = hosts[pos].object;
+                contexts = hosts[pos].contextList.contexts;
+                nesting = hosts[pos].contextList.nesting;
+            } else {
+                if (defaultHostName == null) {
+                    return;
+                }
+                pos = find(hosts, defaultHostName);
+                if ((pos != -1) && (defaultHostName.equals(hosts[pos].name))) {
+                    mappingData.host = hosts[pos].object;
+                    contexts = hosts[pos].contextList.contexts;
+                    nesting = hosts[pos].contextList.nesting;
+                } else {
+                    return;
+                }
+            }
+        }
+
+        // Context mapping
+        if (mappingData.context == null) {
+            int pos = find(contexts, uri);
+            if (pos == -1) {
+                return;
+            }
+
+            int lastSlash = -1;
+            int uriEnd = uri.getEnd();
+            int length = -1;
+            boolean found = false;
+            while (pos >= 0) {
+                if (uri.startsWith(contexts[pos].name)) {
+                    length = contexts[pos].name.length();
+                    if (uri.getLength() == length) {
+                        found = true;
+                        break;
+                    } else if (uri.startsWithIgnoreCase("/", length)) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (lastSlash == -1) {
+                    lastSlash = nthSlash(uri, nesting + 1);
+                } else {
+                    lastSlash = lastSlash(uri);
+                }
+                uri.setEnd(lastSlash);
+                pos = find(contexts, uri);
+            }
+            uri.setEnd(uriEnd);
+
+            if (!found) {
+                if (contexts[0].name.equals("")) {
+                    context = contexts[0];
+                }
+            } else {
+                context = contexts[pos];
+            }
+            if (context != null) {
+                mappingData.context = context.object;
+                mappingData.contextPath.setString(context.name);
+            }
+        }
+
+        // Wrapper mapping
+        if ((context != null) && (mappingData.wrapper == null)) {
+            internalMapWrapper(context, uri, mappingData);
+        }
+
+    }
+
+
+    /**
+     * Wrapper mapping.
+     */
+    private final void internalMapWrapper(Context context, CharChunk path,
+                                          MappingData mappingData)
+        throws Exception {
+
+        int pathOffset = path.getOffset();
+        int pathEnd = path.getEnd();
+        int servletPath = pathOffset;
+        boolean noServletPath = false;
+
+        int length = context.name.length();
+        if (length != (pathEnd - pathOffset)) {
+            servletPath = pathOffset + length;
+        } else {
+            noServletPath = true;
+            path.append('/');
+            pathOffset = path.getOffset();
+            pathEnd = path.getEnd();
+            servletPath = pathOffset+length;
+        }
+
+        path.setOffset(servletPath);
+
+        // Rule 1 -- Exact Match
+        Wrapper[] exactWrappers = context.exactWrappers;
+        internalMapExactWrapper(exactWrappers, path, mappingData);
+
+        // Rule 2 -- Prefix Match
+        boolean checkJspWelcomeFiles = false;
+        Wrapper[] wildcardWrappers = context.wildcardWrappers;
+        if (mappingData.wrapper == null) {
+            internalMapWildcardWrapper(wildcardWrappers, context.nesting, 
+                                       path, mappingData);
+            if (mappingData.wrapper != null && mappingData.jspWildCard) {
+                char[] buf = path.getBuffer();
+                if (buf[pathEnd - 1] == '/') {
+                    /*
+                     * Path ending in '/' was mapped to JSP servlet based on
+                     * wildcard match (e.g., as specified in url-pattern of a
+                     * jsp-property-group.
+                     * Force the context's welcome files, which are interpreted
+                     * as JSP files (since they match the url-pattern), to be
+                     * considered. See Bugzilla 27664.
+                     */ 
+                    mappingData.wrapper = null;
+                    checkJspWelcomeFiles = true;
+                } else {
+                    // See Bugzilla 27704
+                    mappingData.wrapperPath.setChars(buf, path.getStart(),
+                                                     path.getLength());
+                    mappingData.pathInfo.recycle();
+                }
+            }
+        }
+
+        if(mappingData.wrapper == null && noServletPath) {
+            // The path is empty, redirect to "/"
+            mappingData.redirectPath.setChars
+                (path.getBuffer(), pathOffset, pathEnd);
+            path.setEnd(pathEnd - 1);
+            return;
+        }
+
+        // Rule 3 -- Extension Match
+        Wrapper[] extensionWrappers = context.extensionWrappers;
+        if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
+            internalMapExtensionWrapper(extensionWrappers, path, mappingData);
+        }
+
+        // Rule 4 -- Welcome resources processing for servlets
+        if (mappingData.wrapper == null) {
+            boolean checkWelcomeFiles = checkJspWelcomeFiles;
+            if (!checkWelcomeFiles) {
+                char[] buf = path.getBuffer();
+                checkWelcomeFiles = (buf[pathEnd - 1] == '/');
+            }
+            if (checkWelcomeFiles) {
+                for (int i = 0; (i < context.welcomeResources.length)
+                         && (mappingData.wrapper == null); i++) {
+                    path.setOffset(pathOffset);
+                    path.setEnd(pathEnd);
+                    path.append(context.welcomeResources[i], 0,
+                                context.welcomeResources[i].length());
+                    path.setOffset(servletPath);
+
+                    // Rule 4a -- Welcome resources processing for exact macth
+                    internalMapExactWrapper(exactWrappers, path, mappingData);
+
+                    // Rule 4b -- Welcome resources processing for prefix match
+                    if (mappingData.wrapper == null) {
+                        internalMapWildcardWrapper
+                            (wildcardWrappers, context.nesting, 
+                             path, mappingData);
+                    }
+
+                    // Rule 4c -- Welcome resources processing
+                    //            for physical folder
+                    if (mappingData.wrapper == null
+                        && context.resources != null) {
+                        Object file = null;
+                        String pathStr = path.toString();
+                        try {
+                            file = context.resources.lookup(pathStr);
+                        } catch(NamingException nex) {
+                            // Swallow not found, since this is normal
+                        }
+                        if (file != null && !(file instanceof DirContext) ) {
+                            internalMapExtensionWrapper(extensionWrappers,
+                                                        path, mappingData);
+                            if (mappingData.wrapper == null
+                                && context.defaultWrapper != null) {
+                                mappingData.wrapper =
+                                    context.defaultWrapper.object;
+                                mappingData.requestPath.setChars
+                                    (path.getBuffer(), path.getStart(), 
+                                     path.getLength());
+                                mappingData.wrapperPath.setChars
+                                    (path.getBuffer(), path.getStart(), 
+                                     path.getLength());
+                                mappingData.requestPath.setString(pathStr);
+                                mappingData.wrapperPath.setString(pathStr);
+                            }
+                        }
+                    }
+                }
+
+                path.setOffset(servletPath);
+                path.setEnd(pathEnd);
+            }
+                                        
+        }
+
+
+        // Rule 7 -- Default servlet
+        if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
+            if (context.defaultWrapper != null) {
+                mappingData.wrapper = context.defaultWrapper.object;
+                mappingData.requestPath.setChars
+                    (path.getBuffer(), path.getStart(), path.getLength());
+                mappingData.wrapperPath.setChars
+                    (path.getBuffer(), path.getStart(), path.getLength());
+            }
+            // Redirection to a folder
+            char[] buf = path.getBuffer();
+            if (context.resources != null && buf[pathEnd -1 ] != '/') {
+                Object file = null;
+                String pathStr = path.toString();
+                try {
+                    file = context.resources.lookup(pathStr);
+                } catch(NamingException nex) {
+                    // Swallow, since someone else handles the 404
+                }
+                if (file != null && file instanceof DirContext) {
+                    // Note: this mutates the path: do not do any processing 
+                    // after this (since we set the redirectPath, there 
+                    // shouldn't be any)
+                    path.setOffset(pathOffset);
+                    path.append('/');
+                    mappingData.redirectPath.setChars
+                        (path.getBuffer(), path.getStart(), path.getLength());
+                } else {
+                    mappingData.requestPath.setString(pathStr);
+                    mappingData.wrapperPath.setString(pathStr);
+                }
+            }
+        }
+
+        path.setOffset(pathOffset);
+        path.setEnd(pathEnd);
+
+    }
+
+
+    /**
+     * Exact mapping.
+     */
+    private final void internalMapExactWrapper
+        (Wrapper[] wrappers, CharChunk path, MappingData mappingData) {
+        int pos = find(wrappers, path);
+        if ((pos != -1) && (path.equals(wrappers[pos].name))) {
+            mappingData.requestPath.setString(wrappers[pos].name);
+            mappingData.wrapperPath.setString(wrappers[pos].name);
+            mappingData.wrapper = wrappers[pos].object;
+        }
+    }
+
+
+    /**
+     * Wildcard mapping.
+     */
+    private final void internalMapWildcardWrapper
+        (Wrapper[] wrappers, int nesting, CharChunk path, 
+         MappingData mappingData) {
+
+        int pathEnd = path.getEnd();
+        int pathOffset = path.getOffset();
+
+        int lastSlash = -1;
+        int length = -1;
+        int pos = find(wrappers, path);
+        if (pos != -1) {
+            boolean found = false;
+            while (pos >= 0) {
+                if (path.startsWith(wrappers[pos].name)) {
+                    length = wrappers[pos].name.length();
+                    if (path.getLength() == length) {
+                        found = true;
+                        break;
+                    } else if (path.startsWithIgnoreCase("/", length)) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (lastSlash == -1) {
+                    lastSlash = nthSlash(path, nesting + 1);
+                } else {
+                    lastSlash = lastSlash(path);
+                }
+                path.setEnd(lastSlash);
+                pos = find(wrappers, path);
+            }
+            path.setEnd(pathEnd);
+            if (found) {
+                mappingData.wrapperPath.setString(wrappers[pos].name);
+                if (path.getLength() > length) {
+                    mappingData.pathInfo.setChars
+                        (path.getBuffer(),
+                         path.getOffset() + length,
+                         path.getLength() - length);
+                }
+                mappingData.requestPath.setChars
+                    (path.getBuffer(), path.getOffset(), path.getLength());
+                mappingData.wrapper = wrappers[pos].object;
+                mappingData.jspWildCard = wrappers[pos].jspWildCard;
+            }
+        }
+    }
+
+
+    /**
+     * Extension mappings.
+     */
+    private final void internalMapExtensionWrapper
+        (Wrapper[] wrappers, CharChunk path, MappingData mappingData) {
+        char[] buf = path.getBuffer();
+        int pathEnd = path.getEnd();
+        int servletPath = path.getOffset();
+        int slash = -1;
+        for (int i = pathEnd - 1; i >= servletPath; i--) {
+            if (buf[i] == '/') {
+                slash = i;
+                break;
+            }
+        }
+        if (slash >= 0) {
+            int period = -1;
+            for (int i = pathEnd - 1; i > slash; i--) {
+                if (buf[i] == '.') {
+                    period = i;
+                    break;
+                }
+            }
+            if (period >= 0) {
+                path.setOffset(period + 1);
+                path.setEnd(pathEnd);
+                int pos = find(wrappers, path);
+                if ((pos != -1)
+                    && (path.equals(wrappers[pos].name))) {
+                    mappingData.wrapperPath.setChars
+                        (buf, servletPath, pathEnd - servletPath);
+                    mappingData.requestPath.setChars
+                        (buf, servletPath, pathEnd - servletPath);
+                    mappingData.wrapper = wrappers[pos].object;
+                }
+                path.setOffset(servletPath);
+                path.setEnd(pathEnd);
+            }
+        }
+    }
+
+
+    /**
+     * Find a map elemnt given its name in a sorted array of map elements.
+     * This will return the index for the closest inferior or equal item in the
+     * given array.
+     */
+    private static final int find(MapElement[] map, CharChunk name) {
+        return find(map, name, name.getStart(), name.getEnd());
+    }
+
+
+    /**
+     * Find a map elemnt given its name in a sorted array of map elements.
+     * This will return the index for the closest inferior or equal item in the
+     * given array.
+     */
+    private static final int find(MapElement[] map, CharChunk name,
+                                  int start, int end) {
+
+        int a = 0;
+        int b = map.length - 1;
+
+        // Special cases: -1 and 0
+        if (b == -1) {
+            return -1;
+        }
+        
+        if (compare(name, start, end, map[0].name) < 0 ) {
+            return -1;
+        }         
+        if (b == 0) {
+            return 0;
+        }
+
+        int i = 0;
+        while (true) {
+            i = (b + a) / 2;
+            int result = compare(name, start, end, map[i].name);
+            if (result == 1) {
+                a = i;
+            } else if (result == 0) {
+                return i;
+            } else {
+                b = i;
+            }
+            if ((b - a) == 1) {
+                int result2 = compare(name, start, end, map[b].name);
+                if (result2 < 0) {
+                    return a;
+                } else {
+                    return b;
+                }
+            }
+        }
+
+    }
+
+    /**
+     * Find a map elemnt given its name in a sorted array of map elements.
+     * This will return the index for the closest inferior or equal item in the
+     * given array.
+     */
+    private static final int findIgnoreCase(MapElement[] map, CharChunk name) {
+        return findIgnoreCase(map, name, name.getStart(), name.getEnd());
+    }
+
+
+    /**
+     * Find a map elemnt given its name in a sorted array of map elements.
+     * This will return the index for the closest inferior or equal item in the
+     * given array.
+     */
+    private static final int findIgnoreCase(MapElement[] map, CharChunk name,
+                                  int start, int end) {
+
+        int a = 0;
+        int b = map.length - 1;
+
+        // Special cases: -1 and 0
+        if (b == -1) {
+            return -1;
+        }
+        if (compareIgnoreCase(name, start, end, map[0].name) < 0 ) {
+            return -1;
+        }         
+        if (b == 0) {
+            return 0;
+        }
+
+        int i = 0;
+        while (true) {
+            i = (b + a) / 2;
+            int result = compareIgnoreCase(name, start, end, map[i].name);
+            if (result == 1) {
+                a = i;
+            } else if (result == 0) {
+                return i;
+            } else {
+                b = i;
+            }
+            if ((b - a) == 1) {
+                int result2 = compareIgnoreCase(name, start, end, map[b].name);
+                if (result2 < 0) {
+                    return a;
+                } else {
+                    return b;
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Find a map elemnt given its name in a sorted array of map elements.
+     * This will return the index for the closest inferior or equal item in the
+     * given array.
+     */
+    private static final int find(MapElement[] map, String name) {
+
+        int a = 0;
+        int b = map.length - 1;
+
+        // Special cases: -1 and 0
+        if (b == -1) {
+            return -1;
+        }
+        
+        if (name.compareTo(map[0].name) < 0) {
+            return -1;
+        } 
+        if (b == 0) {
+            return 0;
+        }
+
+        int i = 0;
+        while (true) {
+            i = (b + a) / 2;
+            int result = name.compareTo(map[i].name);
+            if (result > 0) {
+                a = i;
+            } else if (result == 0) {
+                return i;
+            } else {
+                b = i;
+            }
+            if ((b - a) == 1) {
+                int result2 = name.compareTo(map[b].name);
+                if (result2 < 0) {
+                    return a;
+                } else {
+                    return b;
+                }
+            }
+        }
+
+    }
+
+
+    /**
+     * Compare given char chunk with String.
+     * Return -1, 0 or +1 if inferior, equal, or superior to the String.
+     */
+    private static final int compare(CharChunk name, int start, int end,
+                                     String compareTo) {
+        int result = 0;
+        char[] c = name.getBuffer();
+        int len = compareTo.length();
+        if ((end - start) < len) {
+            len = end - start;
+        }
+        for (int i = 0; (i < len) && (result == 0); i++) {
+            if (c[i + start] > compareTo.charAt(i)) {
+                result = 1;
+            } else if (c[i + start] < compareTo.charAt(i)) {
+                result = -1;
+            }
+        }
+        if (result == 0) {
+            if (compareTo.length() > (end - start)) {
+                result = -1;
+            } else if (compareTo.length() < (end - start)) {
+                result = 1;
+            }
+        }
+        return result;
+    }
+
+
+    /**
+     * Compare given char chunk with String ignoring case.
+     * Return -1, 0 or +1 if inferior, equal, or superior to the String.
+     */
+    private static final int compareIgnoreCase(CharChunk name, int start, int end,
+                                     String compareTo) {
+        int result = 0;
+        char[] c = name.getBuffer();
+        int len = compareTo.length();
+        if ((end - start) < len) {
+            len = end - start;
+        }
+        for (int i = 0; (i < len) && (result == 0); i++) {
+            if (Ascii.toLower(c[i + start]) > Ascii.toLower(compareTo.charAt(i))) {
+                result = 1;
+            } else if (Ascii.toLower(c[i + start]) < Ascii.toLower(compareTo.charAt(i))) {
+                result = -1;
+            }
+        }
+        if (result == 0) {
+            if (compareTo.length() > (end - start)) {
+                result = -1;
+            } else if (compareTo.length() < (end - start)) {
+                result = 1;
+            }
+        }
+        return result;
+    }
+
+
+    /**
+     * Find the position of the last slash in the given char chunk.
+     */
+    private static final int lastSlash(CharChunk name) {
+
+        char[] c = name.getBuffer();
+        int end = name.getEnd();
+        int start = name.getStart();
+        int pos = end;
+
+        while (pos > start) {
+            if (c[--pos] == '/') {
+                break;
+            }
+        }
+
+        return (pos);
+
+    }
+
+
+    /**
+     * Find the position of the nth slash, in the given char chunk.
+     */
+    private static final int nthSlash(CharChunk name, int n) {
+
+        char[] c = name.getBuffer();
+        int end = name.getEnd();
+        int start = name.getStart();
+        int pos = start;
+        int count = 0;
+
+        while (pos < end) {
+            if ((c[pos++] == '/') && ((++count) == n)) {
+                pos--;
+                break;
+            }
+        }
+
+        return (pos);
+
+    }
+
+
+    /**
+     * Return the slash count in a given string.
+     */
+    private static final int slashCount(String name) {
+        int pos = -1;
+        int count = 0;
+        while ((pos = name.indexOf('/', pos + 1)) != -1) {
+            count++;
+        }
+        return count;
+    }
+
+
+    /**
+     * Insert into the right place in a sorted MapElement array, and prevent
+     * duplicates.
+     */
+    private static final boolean insertMap
+        (MapElement[] oldMap, MapElement[] newMap, MapElement newElement) {
+        int pos = find(oldMap, newElement.name);
+        if ((pos != -1) && (newElement.name.equals(oldMap[pos].name))) {
+            return false;
+        }
+        System.arraycopy(oldMap, 0, newMap, 0, pos + 1);
+        newMap[pos + 1] = newElement;
+        System.arraycopy
+            (oldMap, pos + 1, newMap, pos + 2, oldMap.length - pos - 1);
+        return true;
+    }
+
+
+    /**
+     * Insert into the right place in a sorted MapElement array.
+     */
+    private static final boolean removeMap
+        (MapElement[] oldMap, MapElement[] newMap, String name) {
+        int pos = find(oldMap, name);
+        if ((pos != -1) && (name.equals(oldMap[pos].name))) {
+            System.arraycopy(oldMap, 0, newMap, 0, pos);
+            System.arraycopy(oldMap, pos + 1, newMap, pos,
+                             oldMap.length - pos - 1);
+            return true;
+        }
+        return false;
+    }
+
+
+    // ------------------------------------------------- MapElement Inner Class
+
+
+    protected static abstract class MapElement {
+
+        public String name = null;
+        public Object object = null;
+
+    }
+
+
+    // ------------------------------------------------------- Host Inner Class
+
+
+    protected static final class Host
+        extends MapElement {
+
+        public ContextList contextList = null;
+
+    }
+
+
+    // ------------------------------------------------ ContextList Inner Class
+
+
+    protected static final class ContextList {
+
+        public Context[] contexts = new Context[0];
+        public int nesting = 0;
+
+    }
+
+
+    // ---------------------------------------------------- Context Inner Class
+
+
+    protected static final class Context
+        extends MapElement {
+
+        public String path = null;
+        public String[] welcomeResources = new String[0];
+        public javax.naming.Context resources = null;
+        public Wrapper defaultWrapper = null;
+        public Wrapper[] exactWrappers = new Wrapper[0];
+        public Wrapper[] wildcardWrappers = new Wrapper[0];
+        public Wrapper[] extensionWrappers = new Wrapper[0];
+        public int nesting = 0;
+
+    }
+
+
+    // ---------------------------------------------------- Wrapper Inner Class
+
+
+    protected static class Wrapper
+        extends MapElement {
+
+        public String path = null;
+        public boolean jspWildCard = false;
+    }
+
+
+    // -------------------------------------------------------- Testing Methods
+
+    // FIXME: Externalize this
+    /*
+    public static void main(String args[]) {
+
+        try {
+
+        Mapper mapper = new Mapper();
+        System.out.println("Start");
+
+        mapper.addHost("sjbjdvwsbvhrb", new String[0], "blah1");
+        mapper.addHost("sjbjdvwsbvhr/", new String[0], "blah1");
+        mapper.addHost("wekhfewuifweuibf", new String[0], "blah2");
+        mapper.addHost("ylwrehirkuewh", new String[0], "blah3");
+        mapper.addHost("iohgeoihro", new String[0], "blah4");
+        mapper.addHost("fwehoihoihwfeo", new String[0], "blah5");
+        mapper.addHost("owefojiwefoi", new String[0], "blah6");
+        mapper.addHost("iowejoiejfoiew", new String[0], "blah7");
+        mapper.addHost("iowejoiejfoiew", new String[0], "blah17");
+        mapper.addHost("ohewoihfewoih", new String[0], "blah8");
+        mapper.addHost("fewohfoweoih", new String[0], "blah9");
+        mapper.addHost("ttthtiuhwoih", new String[0], "blah10");
+        mapper.addHost("lkwefjwojweffewoih", new String[0], "blah11");
+        mapper.addHost("zzzuyopjvewpovewjhfewoih", new String[0], "blah12");
+        mapper.addHost("xxxxgqwiwoih", new String[0], "blah13");
+        mapper.addHost("qwigqwiwoih", new String[0], "blah14");
+
+        System.out.println("Map:");
+        for (int i = 0; i < mapper.hosts.length; i++) {
+            System.out.println(mapper.hosts[i].name);
+        }
+
+        mapper.setDefaultHostName("ylwrehirkuewh");
+
+        String[] welcomes = new String[2];
+        welcomes[0] = "boo/baba";
+        welcomes[1] = "bobou";
+
+        mapper.addContext("iowejoiejfoiew", "", "context0", new String[0], null);
+        mapper.addContext("iowejoiejfoiew", "/foo", "context1", new String[0], null);
+        mapper.addContext("iowejoiejfoiew", "/foo/bar", "context2", welcomes, null);
+        mapper.addContext("iowejoiejfoiew", "/foo/bar/bla", "context3", new String[0], null);
+
+        mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/fo/*", "wrapper0");
+        mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/", "wrapper1");
+        mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/blh", "wrapper2");
+        mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "*.jsp", "wrapper3");
+        mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/blah/bou/*", "wrapper4");
+        mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/blah/bobou/*", "wrapper5");
+        mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "*.htm", "wrapper6");
+
+        MappingData mappingData = new MappingData();
+        MessageBytes host = MessageBytes.newInstance();
+        host.setString("iowejoiejfoiew");
+        MessageBytes uri = MessageBytes.newInstance();
+        uri.setString("/foo/bar/blah/bobou/foo");
+        uri.toChars();
+        uri.getCharChunk().setLimit(-1);
+
+        mapper.map(host, uri, mappingData);
+        System.out.println("MD Host:" + mappingData.host);
+        System.out.println("MD Context:" + mappingData.context);
+        System.out.println("MD Wrapper:" + mappingData.wrapper);
+
+        System.out.println("contextPath:" + mappingData.contextPath);
+        System.out.println("wrapperPath:" + mappingData.wrapperPath);
+        System.out.println("pathInfo:" + mappingData.pathInfo);
+        System.out.println("redirectPath:" + mappingData.redirectPath);
+
+        mappingData.recycle();
+        mapper.map(host, uri, mappingData);
+        System.out.println("MD Host:" + mappingData.host);
+        System.out.println("MD Context:" + mappingData.context);
+        System.out.println("MD Wrapper:" + mappingData.wrapper);
+
+        System.out.println("contextPath:" + mappingData.contextPath);
+        System.out.println("wrapperPath:" + mappingData.wrapperPath);
+        System.out.println("pathInfo:" + mappingData.pathInfo);
+        System.out.println("redirectPath:" + mappingData.redirectPath);
+
+        for (int i = 0; i < 1000000; i++) {
+            mappingData.recycle();
+            mapper.map(host, uri, mappingData);
+        }
+
+        long time = System.currentTimeMillis();
+        for (int i = 0; i < 1000000; i++) {
+            mappingData.recycle();
+            mapper.map(host, uri, mappingData);
+        }
+        System.out.println("Elapsed:" + (System.currentTimeMillis() - time));
+
+        System.out.println("MD Host:" + mappingData.host);
+        System.out.println("MD Context:" + mappingData.context);
+        System.out.println("MD Wrapper:" + mappingData.wrapper);
+
+        System.out.println("contextPath:" + mappingData.contextPath);
+        System.out.println("wrapperPath:" + mappingData.wrapperPath);
+        System.out.println("requestPath:" + mappingData.requestPath);
+        System.out.println("pathInfo:" + mappingData.pathInfo);
+        System.out.println("redirectPath:" + mappingData.redirectPath);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+    */
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/http/mapper/MappingData.java b/connectors/util/java/org/apache/tomcat/util/http/mapper/MappingData.java
new file mode 100644
index 0000000..13bf7a4
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/mapper/MappingData.java
@@ -0,0 +1,52 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.http.mapper;
+
+import org.apache.tomcat.util.buf.MessageBytes;
+
+/**
+ * Mapping data.
+ *
+ * @author Remy Maucherat
+ */
+public class MappingData {
+
+    public Object host = null;
+    public Object context = null;
+    public Object wrapper = null;
+    public boolean jspWildCard = false;
+
+    public MessageBytes contextPath = MessageBytes.newInstance();
+    public MessageBytes requestPath = MessageBytes.newInstance();
+    public MessageBytes wrapperPath = MessageBytes.newInstance();
+    public MessageBytes pathInfo = MessageBytes.newInstance();
+
+    public MessageBytes redirectPath = MessageBytes.newInstance();
+
+    public void recycle() {
+        host = null;
+        context = null;
+        wrapper = null;
+        pathInfo.recycle();
+        requestPath.recycle();
+        wrapperPath.recycle();
+        contextPath.recycle();
+        redirectPath.recycle();
+        jspWildCard = false;
+    }
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/http/package.html b/connectors/util/java/org/apache/tomcat/util/http/package.html
new file mode 100644
index 0000000..dd80960
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/package.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+<title>util.http</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body bgcolor="#FFFFFF">
+Special utils for handling HTTP-specific entities - headers, parameters,
+cookies, etc.
+
+The utils are not specific to tomcat, but use util.MessageBytes.
+
+</body>
+</html>
diff --git a/connectors/util/java/org/apache/tomcat/util/http/res/LocalStrings.properties b/connectors/util/java/org/apache/tomcat/util/http/res/LocalStrings.properties
new file mode 100644
index 0000000..2ec1364
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/res/LocalStrings.properties
@@ -0,0 +1,46 @@
+# HttpMessages
+sc.100=Continue
+sc.101=Switching Protocols
+sc.200=OK
+sc.201=Created
+sc.202=Accepted
+sc.203=Non-Authoritative Information
+sc.204=No Content
+sc.205=Reset Content
+sc.206=Partial Content
+sc.207=Multi-Status
+sc.300=Multiple Choices
+sc.301=Moved Permanently
+sc.302=Moved Temporarily
+sc.303=See Other
+sc.304=Not Modified
+sc.305=Use Proxy
+sc.307=Temporary Redirect
+sc.400=Bad Request
+sc.401=Unauthorized
+sc.402=Payment Required
+sc.403=Forbidden
+sc.404=Not Found
+sc.405=Method Not Allowed
+sc.406=Not Acceptable
+sc.407=Proxy Authentication Required
+sc.408=Request Timeout
+sc.409=Conflict
+sc.410=Gone
+sc.411=Length Required
+sc.412=Precondition Failed
+sc.413=Request Entity Too Large
+sc.414=Request-URI Too Long
+sc.415=Unsupported Media Type
+sc.416=Requested Range Not Satisfiable
+sc.417=Expectation Failed
+sc.422=Unprocessable Entity
+sc.423=Locked
+sc.424=Failed Dependency
+sc.500=Internal Server Error
+sc.501=Not Implemented
+sc.502=Bad Gateway
+sc.503=Service Unavailable
+sc.504=Gateway Timeout
+sc.505=HTTP Version Not Supported
+sc.507=Insufficient Storage
diff --git a/connectors/util/java/org/apache/tomcat/util/http/res/LocalStrings_es.properties b/connectors/util/java/org/apache/tomcat/util/http/res/LocalStrings_es.properties
new file mode 100644
index 0000000..03312fd
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/res/LocalStrings_es.properties
@@ -0,0 +1,47 @@
+# HttpMessages
+sc.100=Continuar
+sc.101=Cambiando Protocolos
+sc.200=OK
+sc.201=Creado
+sc.202=Aceptado
+sc.203=Información No-Autorizativa
+sc.204=Sin Contenido
+sc.205=Reset Contenido
+sc.206=Contenido Parcial
+sc.207=Multi-Estado
+sc.300=Múltiples Elecciones
+sc.301=Movido permanentemente
+sc.302=Movido temporálmente
+sc.303=Mirar Otro
+sc.304=No Modificado
+sc.305=Usar Proxy
+sc.307=Redirección Temporal
+sc.400=Petición incorrecta
+sc.401=No Autorizado
+sc.402=Pago requerido
+sc.403=Prohibido
+sc.404=No Encontrado
+sc.405=Método No Permitido
+sc.406=No Aceptable
+sc.407=Autentificación Proxy Requerida
+sc.408=Request Caducada
+sc.409=Conflicto
+sc.410=Ido
+sc.411=Longitud Requerida
+sc.412=Precondición Fallada
+sc.413=Entidad de Request Demasiado Grande
+sc.414=Request-URI Demasiado Larga
+sc.415=Tipo de Medio No Soportado
+sc.416=El Rango Pedido No Ser Satisfecho
+sc.417=Expectativa Fallada
+sc.422=Entidad Improcesable
+sc.423=Bloqueado
+sc.424=Dependencia Fallida
+sc.500=Error Interno del Servidor
+sc.501=No Implementado
+sc.502=Pasarela Incorrecta
+sc.503=Servicio no Disponible
+sc.504=Pasarela Caducada
+sc.505=Versión de HTTP No Soportada
+sc.507=Almacenaje Insuficiente
+
diff --git a/connectors/util/java/org/apache/tomcat/util/http/res/LocalStrings_fr.properties b/connectors/util/java/org/apache/tomcat/util/http/res/LocalStrings_fr.properties
new file mode 100644
index 0000000..61399c9
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/http/res/LocalStrings_fr.properties
@@ -0,0 +1,44 @@
+sc.100=Continuer
+sc.101=Changement de Protocols
+sc.200=OK
+sc.201=Crée
+sc.202=Accepté
+sc.203=Information Sans-Autorité
+sc.204=Pas de Contenu
+sc.205=Remise à Zéro de Contenu
+sc.206=Contenu Partiel
+sc.207=Etat Multiple
+sc.300=Choix Multiples
+sc.301=Déplacé de façon Permanente
+sc.302=Déplacé Temporairement
+sc.303=Voir Autre
+sc.304=Non Modifié
+sc.305=Utilisation de Relais
+sc.307=Redirection Temporaire
+sc.400=Mauvaise Requête
+sc.401=Non-Autorisé
+sc.402=Paiement Nécessaire
+sc.403=Interdit
+sc.404=Introuvable
+sc.405=Méthode Non Autorisée
+sc.406=Inacceptable
+sc.407=Authentification de Relais Nécessaire
+sc.408=Dépassement de Délais pour la Requête
+sc.409=Conflit
+sc.410=Parti
+sc.411=Taille Demandée
+sc.412=Echec de Pré-condition
+sc.413=Entité de Requête Trop Grande
+sc.414=URI de Requête Trop Grande
+sc.415=Type de Support Non Supporté
+sc.416=Etendue de Requête Irréalisable
+sc.417=Echec d'Attente
+sc.422=Entité Ingérable
+sc.424=Echec de Dépendance
+sc.500=Erreur Interne de Servlet
+sc.501=Non Implémenté
+sc.502=Mauvaise Passerelle
+sc.503=Service Indisponible
+sc.504=Dépassement de Délais pour la Passerelle
+sc.505=Version HTTP Non Supportée
+sc.507=Stockage Insuffisant
diff --git a/connectors/util/java/org/apache/tomcat/util/log/CaptureLog.java b/connectors/util/java/org/apache/tomcat/util/log/CaptureLog.java
new file mode 100644
index 0000000..51dfee7
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/log/CaptureLog.java
@@ -0,0 +1,49 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+/**
+ * Per Thread System.err and System.out log capture data.
+ *
+ * @author Glenn L. Nielsen
+ */
+
+class CaptureLog {
+
+    protected CaptureLog() {
+        baos = new ByteArrayOutputStream();
+        ps = new PrintStream(baos);
+    }
+
+    private ByteArrayOutputStream baos;
+    private PrintStream ps;
+
+    protected PrintStream getStream() {
+        return ps;
+    }
+
+    protected void reset() {
+        baos.reset();
+    }
+
+    protected String getCapture() {
+        return baos.toString();
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/log/SystemLogHandler.java b/connectors/util/java/org/apache/tomcat/util/log/SystemLogHandler.java
new file mode 100644
index 0000000..79b9fea
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/log/SystemLogHandler.java
@@ -0,0 +1,243 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.log;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Hashtable;
+import java.util.Stack;
+
+/**
+ * This helper class may be used to do sophisticated redirection of 
+ * System.out and System.err on a per Thread basis.
+ * 
+ * A stack is implemented per Thread so that nested startCapture
+ * and stopCapture can be used.
+ *
+ * @author Remy Maucherat
+ * @author Glenn L. Nielsen
+ */
+public class SystemLogHandler extends PrintStream {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Construct the handler to capture the output of the given steam.
+     */
+    public SystemLogHandler(PrintStream wrapped) {
+        super(wrapped);
+        out = wrapped;
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Wrapped PrintStream.
+     */
+    protected PrintStream out = null;
+
+
+    /**
+     * Thread <-> CaptureLog associations.
+     */
+    protected static Hashtable logs = new Hashtable();
+
+
+    /**
+     * Spare CaptureLog ready for reuse.
+     */
+    protected static Stack reuse = new Stack();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Start capturing thread's output.
+     */
+    public static void startCapture() {
+        CaptureLog log = null;
+
+        // Synchronized for Bugzilla 31018
+        synchronized(reuse) {
+            log = reuse.isEmpty() ? new CaptureLog() : (CaptureLog)reuse.pop();
+        }
+
+        Thread thread = Thread.currentThread();
+        Stack stack = (Stack)logs.get(thread);
+        if (stack == null) {
+            stack = new Stack();
+            logs.put(thread, stack);
+        }
+
+        stack.push(log);
+    }
+
+
+    /**
+     * Stop capturing thread's output and return captured data as a String.
+     */
+    public static String stopCapture() {
+        Stack stack = (Stack)logs.get(Thread.currentThread());
+        if (stack == null || stack.isEmpty()) {
+            return null;
+        }
+        CaptureLog log = (CaptureLog)stack.pop();
+        if (log == null) {
+            return null;
+        }
+        String capture = log.getCapture();
+        log.reset();
+        reuse.push(log);
+        return capture;
+    }
+
+
+    // ------------------------------------------------------ Protected Methods
+
+
+    /**
+     * Find PrintStream to which the output must be written to.
+     */
+    protected PrintStream findStream() {
+        Stack stack = (Stack)logs.get(Thread.currentThread());
+        if (stack != null && !stack.isEmpty()) {
+            CaptureLog log = (CaptureLog)stack.peek();
+            if (log != null) {
+                PrintStream ps = log.getStream();
+                if (ps != null) {
+                    return ps;
+                }
+            }
+        }
+        return out;
+    }
+
+
+    // ---------------------------------------------------- PrintStream Methods
+
+
+    public void flush() {
+        findStream().flush();
+    }
+
+    public void close() {
+        findStream().close();
+    }
+
+    public boolean checkError() {
+        return findStream().checkError();
+    }
+
+    protected void setError() {
+        //findStream().setError();
+    }
+
+    public void write(int b) {
+        findStream().write(b);
+    }
+
+    public void write(byte[] b)
+        throws IOException {
+        findStream().write(b);
+    }
+
+    public void write(byte[] buf, int off, int len) {
+        findStream().write(buf, off, len);
+    }
+
+    public void print(boolean b) {
+        findStream().print(b);
+    }
+
+    public void print(char c) {
+        findStream().print(c);
+    }
+
+    public void print(int i) {
+        findStream().print(i);
+    }
+
+    public void print(long l) {
+        findStream().print(l);
+    }
+
+    public void print(float f) {
+        findStream().print(f);
+    }
+
+    public void print(double d) {
+        findStream().print(d);
+    }
+
+    public void print(char[] s) {
+        findStream().print(s);
+    }
+
+    public void print(String s) {
+        findStream().print(s);
+    }
+
+    public void print(Object obj) {
+        findStream().print(obj);
+    }
+
+    public void println() {
+        findStream().println();
+    }
+
+    public void println(boolean x) {
+        findStream().println(x);
+    }
+
+    public void println(char x) {
+        findStream().println(x);
+    }
+
+    public void println(int x) {
+        findStream().println(x);
+    }
+
+    public void println(long x) {
+        findStream().println(x);
+    }
+
+    public void println(float x) {
+        findStream().println(x);
+    }
+
+    public void println(double x) {
+        findStream().println(x);
+    }
+
+    public void println(char[] x) {
+        findStream().println(x);
+    }
+
+    public void println(String x) {
+        findStream().println(x);
+    }
+
+    public void println(Object x) {
+        findStream().println(x);
+    }
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/DefaultServerSocketFactory.java b/connectors/util/java/org/apache/tomcat/util/net/DefaultServerSocketFactory.java
new file mode 100644
index 0000000..d6c56c1
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/DefaultServerSocketFactory.java
@@ -0,0 +1,69 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net;
+
+import java.io.*;
+import java.net.*;
+
+/**
+ * Default server socket factory. Doesn't do much except give us
+ * plain ol' server sockets.
+ *
+ * @author db@eng.sun.com
+ * @author Harish Prabandham
+ */
+
+// Default implementation of server sockets.
+
+//
+// WARNING: Some of the APIs in this class are used by J2EE. 
+// Please talk to harishp@eng.sun.com before making any changes.
+//
+class DefaultServerSocketFactory extends ServerSocketFactory {
+
+    DefaultServerSocketFactory () {
+        /* NOTHING */
+    }
+
+    public ServerSocket createSocket (int port)
+    throws IOException {
+        return  new ServerSocket (port);
+    }
+
+    public ServerSocket createSocket (int port, int backlog)
+    throws IOException {
+        return new ServerSocket (port, backlog);
+    }
+
+    public ServerSocket createSocket (int port, int backlog,
+        InetAddress ifAddress)
+    throws IOException {
+        return new ServerSocket (port, backlog, ifAddress);
+    }
+ 
+    public Socket acceptSocket(ServerSocket socket)
+ 	throws IOException {
+ 	return socket.accept();
+    }
+ 
+    public void handshake(Socket sock)
+ 	throws IOException {
+ 	; // NOOP
+    }
+ 	    
+        
+ }
diff --git a/connectors/util/java/org/apache/tomcat/util/net/PoolTcpEndpoint.java b/connectors/util/java/org/apache/tomcat/util/net/PoolTcpEndpoint.java
new file mode 100644
index 0000000..407921f
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/PoolTcpEndpoint.java
@@ -0,0 +1,612 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.BindException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.security.AccessControlException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
+import org.apache.tomcat.util.threads.ThreadPool;
+import org.apache.tomcat.util.threads.ThreadPoolRunnable;
+
+/* Similar with MPM module in Apache2.0. Handles all the details related with
+   "tcp server" functionality - thread management, accept policy, etc.
+   It should do nothing more - as soon as it get a socket ( and all socket options
+   are set, etc), it just handle the stream to ConnectionHandler.processConnection. (costin)
+*/
+
+
+
+/**
+ * Handle incoming TCP connections.
+ *
+ * This class implement a simple server model: one listener thread accepts on a socket and
+ * creates a new worker thread for each incoming connection.
+ *
+ * More advanced Endpoints will reuse the threads, use queues, etc.
+ *
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author Jason Hunter [jch@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Costin@eng.sun.com
+ * @author Gal Shachor [shachor@il.ibm.com]
+ * @author Yoav Shapira <yoavs@apache.org>
+ */
+public class PoolTcpEndpoint { // implements Endpoint {
+
+    private StringManager sm = 
+        StringManager.getManager("org.apache.tomcat.util.net.res");
+
+    private static final int BACKLOG = 100;
+    private static final int TIMEOUT = 1000;
+
+    private final Object threadSync = new Object();
+
+    private boolean isPool = true;
+
+    private int backlog = BACKLOG;
+    private int serverTimeout = TIMEOUT;
+
+    TcpConnectionHandler handler;
+
+    private InetAddress inet;
+    private int port;
+
+    private ServerSocketFactory factory;
+    private ServerSocket serverSocket;
+
+    ThreadPoolRunnable listener;
+    private volatile boolean running = false;
+    private volatile boolean paused = false;
+    private boolean initialized = false;
+    private boolean reinitializing = false;
+    static final int debug=0;
+
+    ThreadPool tp;
+
+    static Log log=LogFactory.getLog(PoolTcpEndpoint.class );
+
+    protected boolean tcpNoDelay=false;
+    protected int linger=100;
+    protected int socketTimeout=-1;
+    
+    public PoolTcpEndpoint() {
+	tp = new ThreadPool();
+    }
+
+    public PoolTcpEndpoint( ThreadPool tp ) {
+        this.tp=tp;
+    }
+
+    // -------------------- Configuration --------------------
+
+    public void setPoolOn(boolean isPool) {
+        this.isPool = isPool;
+    }
+
+    public boolean isPoolOn() {
+        return isPool;
+    }
+
+    public void setMaxThreads(int maxThreads) {
+	if( maxThreads > 0)
+	    tp.setMaxThreads(maxThreads);
+    }
+
+    public int getMaxThreads() {
+        return tp.getMaxThreads();
+    }
+
+    public void setMaxSpareThreads(int maxThreads) {
+	if(maxThreads > 0) 
+	    tp.setMaxSpareThreads(maxThreads);
+    }
+
+    public int getMaxSpareThreads() {
+        return tp.getMaxSpareThreads();
+    }
+
+    public void setMinSpareThreads(int minThreads) {
+	if(minThreads > 0) 
+	    tp.setMinSpareThreads(minThreads);
+    }
+
+    public int getMinSpareThreads() {
+        return tp.getMinSpareThreads();
+    }
+
+    public void setThreadPriority(int threadPriority) {
+      tp.setThreadPriority(threadPriority);
+    }
+
+    public int getThreadPriority() {
+      return tp.getThreadPriority();
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port ) {
+        this.port=port;
+    }
+
+    public InetAddress getAddress() {
+	    return inet;
+    }
+
+    public void setAddress(InetAddress inet) {
+	    this.inet=inet;
+    }
+
+    public void setServerSocket(ServerSocket ss) {
+	    serverSocket = ss;
+    }
+
+    public void setServerSocketFactory(  ServerSocketFactory factory ) {
+	    this.factory=factory;
+    }
+
+   ServerSocketFactory getServerSocketFactory() {
+ 	    return factory;
+   }
+
+    public void setConnectionHandler( TcpConnectionHandler handler ) {
+    	this.handler=handler;
+    }
+
+    public TcpConnectionHandler getConnectionHandler() {
+	    return handler;
+    }
+
+    public boolean isRunning() {
+	return running;
+    }
+    
+    public boolean isPaused() {
+	return paused;
+    }
+    
+    /**
+     * Allows the server developer to specify the backlog that
+     * should be used for server sockets. By default, this value
+     * is 100.
+     */
+    public void setBacklog(int backlog) {
+	if( backlog>0)
+	    this.backlog = backlog;
+    }
+
+    public int getBacklog() {
+        return backlog;
+    }
+
+    /**
+     * Sets the timeout in ms of the server sockets created by this
+     * server. This method allows the developer to make servers
+     * more or less responsive to having their server sockets
+     * shut down.
+     *
+     * <p>By default this value is 1000ms.
+     */
+    public void setServerTimeout(int timeout) {
+	this.serverTimeout = timeout;
+    }
+
+    public boolean getTcpNoDelay() {
+        return tcpNoDelay;
+    }
+    
+    public void setTcpNoDelay( boolean b ) {
+	tcpNoDelay=b;
+    }
+
+    public int getSoLinger() {
+        return linger;
+    }
+    
+    public void setSoLinger( int i ) {
+	linger=i;
+    }
+
+    public int getSoTimeout() {
+        return socketTimeout;
+    }
+    
+    public void setSoTimeout( int i ) {
+	socketTimeout=i;
+    }
+    
+    public int getServerSoTimeout() {
+        return serverTimeout;
+    }  
+    
+    public void setServerSoTimeout( int i ) {
+	serverTimeout=i;
+    }
+
+    // -------------------- Public methods --------------------
+
+    public void initEndpoint() throws IOException, InstantiationException {
+	try {
+	    if(factory==null)
+		factory=ServerSocketFactory.getDefault();
+	    if(serverSocket==null) {
+                try {
+                    if (inet == null) {
+                        serverSocket = factory.createSocket(port, backlog);
+                    } else {
+                        serverSocket = factory.createSocket(port, backlog, inet);
+                    }
+                } catch ( BindException be ) {
+                    throw new BindException(be.getMessage() + ":" + port);
+                }
+	    }
+            if( serverTimeout >= 0 )
+		serverSocket.setSoTimeout( serverTimeout );
+	} catch( IOException ex ) {
+	    //	    log("couldn't start endpoint", ex, Logger.DEBUG);
+            throw ex;
+	} catch( InstantiationException ex1 ) {
+	    //	    log("couldn't start endpoint", ex1, Logger.DEBUG);
+            throw ex1;
+	}
+        initialized = true;
+    }
+
+    public void startEndpoint() throws IOException, InstantiationException {
+        if (!initialized) {
+            initEndpoint();
+        }
+	if(isPool) {
+	    tp.start();
+	}
+	running = true;
+        paused = false;
+        if(isPool) {
+    	    listener = new TcpWorkerThread(this);
+            tp.runIt(listener);
+        } else {
+	    log.error("XXX Error - need pool !");
+	}
+    }
+
+    public void pauseEndpoint() {
+        if (running && !paused) {
+            paused = true;
+            unlockAccept();
+        }
+    }
+
+    public void resumeEndpoint() {
+        if (running) {
+            paused = false;
+        }
+    }
+
+    public void stopEndpoint() {
+	if (running) {
+	    tp.shutdown();
+	    running = false;
+            if (serverSocket != null) {
+                closeServerSocket();
+            }
+	}
+    }
+
+    protected void closeServerSocket() {
+        if (!paused)
+            unlockAccept();
+        try {
+            if( serverSocket!=null)
+                serverSocket.close();
+        } catch(Exception e) {
+            log.error("Caught exception trying to close socket.", e);
+        }
+        serverSocket = null;
+    }
+
+    protected void unlockAccept() {
+        Socket s = null;
+        try {
+            // Need to create a connection to unlock the accept();
+            if (inet == null) {
+                s=new Socket("127.0.0.1", port );
+            }else{
+                s=new Socket(inet, port );
+                    // setting soLinger to a small value will help shutdown the
+                    // connection quicker
+                s.setSoLinger(true, 0);
+            }
+        } catch(Exception e) {
+            log.debug("Caught exception trying to unlock accept on " + port
+                      + " " + e.toString());
+        } finally {
+            if (s != null) {
+                try {
+                    s.close();
+                } catch (Exception e) {
+                    // Ignore
+                }
+            }
+        }
+    }
+
+    // -------------------- Private methods
+
+    Socket acceptSocket() {
+        if( !running || serverSocket==null ) return null;
+
+        Socket accepted = null;
+
+    	try {
+            if(factory==null) {
+                accepted = serverSocket.accept();
+            } else {
+                accepted = factory.acceptSocket(serverSocket);
+            }
+            if (null == accepted) {
+                log.warn("Null socket returned by accept");
+            } else {
+                if (!running) {
+                    accepted.close();  // rude, but unlikely!
+                    accepted = null;
+                } else if (factory != null) {
+                    factory.initSocket( accepted );
+                }
+            }
+        }
+        catch(InterruptedIOException iioe) {
+            // normal part -- should happen regularly so
+            // that the endpoint can release if the server
+            // is shutdown.
+        }
+        catch (AccessControlException ace) {
+            // When using the Java SecurityManager this exception
+            // can be thrown if you are restricting access to the
+            // socket with SocketPermission's.
+            // Log the unauthorized access and continue
+            String msg = sm.getString("endpoint.warn.security",
+                                      serverSocket,ace);
+            log.warn(msg);
+        }
+        catch (IOException e) {
+
+            String msg = null;
+
+            if (running) {
+                msg = sm.getString("endpoint.err.nonfatal",
+                        serverSocket, e);
+                log.error(msg, e);
+            }
+
+            if (accepted != null) {
+                try {
+                    accepted.close();
+                } catch(Throwable ex) {
+                    msg = sm.getString("endpoint.err.nonfatal",
+                                       accepted, ex);
+                    log.warn(msg, ex);
+                }
+                accepted = null;
+            }
+
+            if( ! running ) return null;
+            reinitializing = true;
+            // Restart endpoint when getting an IOException during accept
+            synchronized (threadSync) {
+                if (reinitializing) {
+                    reinitializing = false;
+                    // 1) Attempt to close server socket
+                    closeServerSocket();
+                    initialized = false;
+                    // 2) Reinit endpoint (recreate server socket)
+                    try {
+                        msg = sm.getString("endpoint.warn.reinit");
+                        log.warn(msg);
+                        initEndpoint();
+                    } catch (Throwable t) {
+                        msg = sm.getString("endpoint.err.nonfatal",
+                                           serverSocket, t);
+                        log.error(msg, t);
+                    }
+                    // 3) If failed, attempt to restart endpoint
+                    if (!initialized) {
+                        msg = sm.getString("endpoint.warn.restart");
+                        log.warn(msg);
+                        try {
+                            stopEndpoint();
+                            initEndpoint();
+                            startEndpoint();
+                        } catch (Throwable t) {
+                            msg = sm.getString("endpoint.err.fatal",
+                                               serverSocket, t);
+                            log.error(msg, t);
+                        }
+                        // Current thread is now invalid: kill it
+                        throw new ThreadDeath();
+                    }
+                }
+            }
+
+        }
+
+        return accepted;
+    }
+
+    /** @deprecated
+     */
+    public void log(String msg)
+    {
+	log.info(msg);
+    }
+
+    /** @deprecated
+     */
+    public void log(String msg, Throwable t)
+    {
+	log.error( msg, t );
+    }
+
+    /** @deprecated
+     */
+    public void log(String msg, int level)
+    {
+	log.info( msg );
+    }
+
+    /** @deprecated
+     */
+    public void log(String msg, Throwable t, int level) {
+    	log.error( msg, t );
+    }
+
+    void setSocketOptions(Socket socket)
+        throws SocketException {
+        if(linger >= 0 ) 
+            socket.setSoLinger( true, linger);
+        if( tcpNoDelay )
+            socket.setTcpNoDelay(tcpNoDelay);
+        if( socketTimeout > 0 )
+            socket.setSoTimeout( socketTimeout );
+    }
+
+}
+
+// -------------------- Threads --------------------
+
+/*
+ * I switched the threading model here.
+ *
+ * We used to have a "listener" thread and a "connection"
+ * thread, this results in code simplicity but also a needless
+ * thread switch.
+ *
+ * Instead I am now using a pool of threads, all the threads are
+ * simmetric in their execution and no thread switch is needed.
+ */
+class TcpWorkerThread implements ThreadPoolRunnable {
+    /* This is not a normal Runnable - it gets attached to an existing
+       thread, runs and when run() ends - the thread keeps running.
+
+       It's better to keep the name ThreadPoolRunnable - avoid confusion.
+       We also want to use per/thread data and avoid sync wherever possible.
+    */
+    PoolTcpEndpoint endpoint;
+    
+    public TcpWorkerThread(PoolTcpEndpoint endpoint) {
+        this.endpoint = endpoint;
+    }
+
+    public Object[] getInitData() {
+        // no synchronization overhead, but 2 array access 
+        Object obj[]=new Object[2];
+        obj[1]= endpoint.getConnectionHandler().init();
+        obj[0]=new TcpConnection();
+        return obj;
+    }
+    
+    public void runIt(Object perThrData[]) {
+
+        // Create per-thread cache
+        if (endpoint.isRunning()) {
+
+            // Loop if endpoint is paused
+            while (endpoint.isPaused()) {
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    // Ignore
+                }
+            }
+
+            // Accept a new connection
+            Socket s = null;
+            try {
+                s = endpoint.acceptSocket();
+            } finally {
+                // Continue accepting on another thread...
+                if (endpoint.isRunning()) {
+                    endpoint.tp.runIt(this);
+                }
+            }
+
+            // Process the connection
+            if (null != s) {
+                TcpConnection con = null;
+                int step = 1;
+                try {
+
+                    // 1: Set socket options: timeout, linger, etc
+                    endpoint.setSocketOptions(s);
+
+                    // 2: SSL handshake
+                    step = 2;
+                    if (endpoint.getServerSocketFactory() != null) {
+                        endpoint.getServerSocketFactory().handshake(s);
+                    }
+
+                    // 3: Process the connection
+                    step = 3;
+                    con = (TcpConnection) perThrData[0];
+                    con.setEndpoint(endpoint);
+                    con.setSocket(s);
+                    endpoint.getConnectionHandler().processConnection(
+                        con,
+                        (Object[]) perThrData[1]);
+
+                } catch (SocketException se) {
+                    PoolTcpEndpoint.log.error(
+                        "Remote Host "
+                            + s.getInetAddress()
+                            + " SocketException: "
+                            + se.getMessage());
+                    // Try to close the socket
+                    try {
+                        s.close();
+                    } catch (IOException e) {
+                    }
+                } catch (Throwable t) {
+                    if (step == 2) {
+                        PoolTcpEndpoint.log.debug("Handshake failed", t);
+                    } else {
+                        PoolTcpEndpoint.log.error("Unexpected error", t);
+                    }
+                    // Try to close the socket
+                    try {
+                        s.close();
+                    } catch (IOException e) {
+                    }
+                } finally {
+                    if (con != null) {
+                        con.recycle();
+                    }
+                }
+            }
+
+        }
+    }
+    
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/SSLImplementation.java b/connectors/util/java/org/apache/tomcat/util/net/SSLImplementation.java
new file mode 100644
index 0000000..859cbd9
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/SSLImplementation.java
@@ -0,0 +1,86 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net;
+
+import java.net.Socket;
+
+/* SSLImplementation:
+
+   Abstract factory and base class for all SSL implementations.
+
+   @author EKR
+*/
+abstract public class SSLImplementation {
+    private static org.apache.commons.logging.Log logger =
+        org.apache.commons.logging.LogFactory.getLog(SSLImplementation.class);
+
+    // The default implementations in our search path
+    private static final String PureTLSImplementationClass=
+	"org.apache.tomcat.util.net.puretls.PureTLSImplementation";
+    private static final String JSSEImplementationClass=
+	"org.apache.tomcat.util.net.jsse.JSSEImplementation";
+    
+    private static final String[] implementations=
+    {
+        PureTLSImplementationClass,
+        JSSEImplementationClass
+    };
+
+    public static SSLImplementation getInstance() throws ClassNotFoundException
+    {
+	for(int i=0;i<implementations.length;i++){
+	    try {
+               SSLImplementation impl=
+		    getInstance(implementations[i]);
+		return impl;
+	    } catch (Exception e) {
+		if(logger.isTraceEnabled()) 
+		    logger.trace("Error creating " + implementations[i],e);
+	    }
+	}
+
+	// If we can't instantiate any of these
+	throw new ClassNotFoundException("Can't find any SSL implementation");
+    }
+
+    public static SSLImplementation getInstance(String className)
+	throws ClassNotFoundException
+    {
+	if(className==null) return getInstance();
+
+	try {
+	    // Workaround for the J2SE 1.4.x classloading problem (under Solaris).
+	    // Class.forName(..) fails without creating class using new.
+	    // This is an ugly workaround. 
+	    if( JSSEImplementationClass.equals(className) ) {
+		return new org.apache.tomcat.util.net.jsse.JSSEImplementation();
+	    }
+	    Class clazz=Class.forName(className);
+	    return (SSLImplementation)clazz.newInstance();
+	} catch (Exception e){
+	    if(logger.isDebugEnabled())
+		logger.debug("Error loading SSL Implementation "
+			     +className, e);
+	    throw new ClassNotFoundException("Error loading SSL Implementation "
+				      +className+ " :" +e.toString());
+	}
+    }
+
+    abstract public String getImplementationName();
+    abstract public ServerSocketFactory getServerSocketFactory();
+    abstract public SSLSupport getSSLSupport(Socket sock);
+}    
diff --git a/connectors/util/java/org/apache/tomcat/util/net/SSLSupport.java b/connectors/util/java/org/apache/tomcat/util/net/SSLSupport.java
new file mode 100644
index 0000000..3bfded1
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/SSLSupport.java
@@ -0,0 +1,127 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net;
+
+import java.io.IOException;
+
+/* SSLSupport
+
+   Interface for SSL-specific functions
+
+   @author EKR
+*/
+
+public interface SSLSupport {
+    /**
+     * The Request attribute key for the cipher suite.
+     */
+    public static final String CIPHER_SUITE_KEY = "javax.servlet.request.cipher_suite";
+
+    /**
+     * The Request attribute key for the key size.
+     */
+    public static final String KEY_SIZE_KEY = "javax.servlet.request.key_size";
+
+    /**
+     * The Request attribute key for the client certificate chain.
+     */
+    public static final String CERTIFICATE_KEY = "javax.servlet.request.X509Certificate";
+
+    /**
+     * The Request attribute key for the session id.
+     * This one is a Tomcat extension to the Servlet spec.
+     */
+    public static final String SESSION_ID_KEY = "javax.servlet.request.ssl_session";
+
+    /**
+     * A mapping table to determine the number of effective bits in the key
+     * when using a cipher suite containing the specified cipher name.  The
+     * underlying data came from the TLS Specification (RFC 2246), Appendix C.
+     */
+     static final CipherData ciphers[] = {
+        new CipherData("_WITH_NULL_", 0),
+        new CipherData("_WITH_IDEA_CBC_", 128),
+        new CipherData("_WITH_RC2_CBC_40_", 40),
+        new CipherData("_WITH_RC4_40_", 40),
+        new CipherData("_WITH_RC4_128_", 128),
+        new CipherData("_WITH_DES40_CBC_", 40),
+        new CipherData("_WITH_DES_CBC_", 56),
+        new CipherData("_WITH_3DES_EDE_CBC_", 168)
+    };
+
+    /**
+     * The cipher suite being used on this connection.
+     */
+    public String getCipherSuite() throws IOException;
+
+    /**
+     * The client certificate chain (if any).
+     */
+    public Object[] getPeerCertificateChain()
+        throws IOException;
+
+    /**
+     * The client certificate chain (if any).
+     * @param force If <code>true</code>, then re-negotiate the 
+     *              connection if necessary.
+     */
+    public Object[] getPeerCertificateChain(boolean force)
+        throws IOException;
+
+    /**
+     * Get the keysize.
+     *
+     * What we're supposed to put here is ill-defined by the
+     * Servlet spec (S 4.7 again). There are at least 4 potential
+     * values that might go here:
+     *
+     * (a) The size of the encryption key
+     * (b) The size of the MAC key
+     * (c) The size of the key-exchange key
+     * (d) The size of the signature key used by the server
+     *
+     * Unfortunately, all of these values are nonsensical.
+     **/
+    public Integer getKeySize()
+        throws IOException;
+
+    /**
+     * The current session Id.
+     */
+    public String getSessionId()
+        throws IOException;
+    /**
+     * Simple data class that represents the cipher being used, along with the
+     * corresponding effective key size.  The specified phrase must appear in the
+     * name of the cipher suite to be recognized.
+     */
+    
+    final class CipherData {
+    
+        public String phrase = null;
+    
+        public int keySize = 0;
+    
+        public CipherData(String phrase, int keySize) {
+            this.phrase = phrase;
+            this.keySize = keySize;
+        }
+    
+    }
+    
+}
+
diff --git a/connectors/util/java/org/apache/tomcat/util/net/ServerSocketFactory.java b/connectors/util/java/org/apache/tomcat/util/net/ServerSocketFactory.java
new file mode 100644
index 0000000..bfb5d80
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/ServerSocketFactory.java
@@ -0,0 +1,172 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Hashtable;
+
+/**
+ * This class creates server sockets.  It may be subclassed by other
+ * factories, which create particular types of server sockets.  This
+ * provides a general framework for the addition of public socket-level
+ * functionality.  It it is the server side analogue of a socket factory,
+ * and similarly provides a way to capture a variety of policies related
+ * to the sockets being constructed.
+ *
+ * <P> Like socket factories, Server Socket factory instances have two
+ * categories of methods.  First are methods used to create sockets.
+ * Second are methods which set properties used in the production of
+ * sockets, such as networking options.  There is also an environment
+ * specific default server socket factory; frameworks will often use
+ * their own customized factory.
+ * 
+ * <P><hr><em> It may be desirable to move this interface into the
+ * <b>java.net</b> package, so that is not an extension but the preferred
+ * interface.  Should this be serializable, making it a JavaBean which can
+ * be saved along with its networking configuration?
+ * </em>   
+ *
+ * @author db@eng.sun.com
+ * @author Harish Prabandham
+ */
+public abstract class ServerSocketFactory implements Cloneable {
+
+    //
+    // NOTE:  JDK 1.1 bug in class GC, this can get collected
+    // even though it's always accessible via getDefault().
+    //
+
+    private static ServerSocketFactory theFactory;
+    protected Hashtable attributes=new Hashtable();
+
+    /**
+     * Constructor is used only by subclasses.
+     */
+
+    protected ServerSocketFactory () {
+        /* NOTHING */
+    }
+
+    /** General mechanism to pass attributes from the
+     *  ServerConnector to the socket factory.
+     *
+     *  Note that the "prefered" mechanism is to
+     *  use bean setters and explicit methods, but
+     *  this allows easy configuration via server.xml
+     *  or simple Properties
+     */
+    public void setAttribute( String name, Object value ) {
+	if( name!=null && value !=null)
+	    attributes.put( name, value );
+    }
+    
+    /**
+     * Returns a copy of the environment's default socket factory.
+     */
+    public static synchronized ServerSocketFactory getDefault () {
+        //
+        // optimize typical case:  no synch needed
+        //
+
+        if (theFactory == null) {
+            //
+            // Different implementations of this method could
+            // work rather differently.  For example, driving
+            // this from a system property, or using a different
+            // implementation than JavaSoft's.
+            //
+
+            theFactory = new DefaultServerSocketFactory ();
+        }
+
+        try {
+            return (ServerSocketFactory) theFactory.clone ();
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeException (e.getMessage ());
+        }
+    }
+
+    /**
+     * Returns a server socket which uses all network interfaces on
+     * the host, and is bound to a the specified port.  The socket is
+     * configured with the socket options (such as accept timeout)
+     * given to this factory.
+     *
+     * @param port the port to listen to
+     * @exception IOException for networking errors
+     * @exception InstantiationException for construction errors
+     */
+    public abstract ServerSocket createSocket (int port)
+    throws IOException, InstantiationException;
+
+    /**
+     * Returns a server socket which uses all network interfaces on
+     * the host, is bound to a the specified port, and uses the 
+     * specified connection backlog.  The socket is configured with
+     * the socket options (such as accept timeout) given to this factory.
+     *
+     * @param port the port to listen to
+     * @param backlog how many connections are queued
+     * @exception IOException for networking errors
+     * @exception InstantiationException for construction errors
+     */
+
+    public abstract ServerSocket createSocket (int port, int backlog)
+    throws IOException, InstantiationException;
+
+    /**
+     * Returns a server socket which uses only the specified network
+     * interface on the local host, is bound to a the specified port,
+     * and uses the specified connection backlog.  The socket is configured
+     * with the socket options (such as accept timeout) given to this factory.
+     *
+     * @param port the port to listen to
+     * @param backlog how many connections are queued
+     * @param ifAddress the network interface address to use
+     * @exception IOException for networking errors
+     * @exception InstantiationException for construction errors
+     */
+
+    public abstract ServerSocket createSocket (int port,
+        int backlog, InetAddress ifAddress)
+    throws IOException, InstantiationException;
+
+    public void initSocket( Socket s ) {
+    }
+ 
+     /**
+       Wrapper function for accept(). This allows us to trap and
+       translate exceptions if necessary
+ 
+       @exception IOException;
+     */ 
+     public abstract Socket acceptSocket(ServerSocket socket)
+ 	throws IOException;
+ 
+     /**
+       Extra function to initiate the handshake. Sometimes necessary
+       for SSL
+ 
+       @exception IOException;
+     */ 
+     public abstract void handshake(Socket sock)
+ 	throws IOException;
+}
+
diff --git a/connectors/util/java/org/apache/tomcat/util/net/TcpConnection.java b/connectors/util/java/org/apache/tomcat/util/net/TcpConnection.java
new file mode 100644
index 0000000..027e28f
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/TcpConnection.java
@@ -0,0 +1,109 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Socket;
+
+/**
+ *
+ */
+public class TcpConnection  { // implements Endpoint {
+    /**
+     * Maxium number of times to clear the socket input buffer.
+     */
+    static  int MAX_SHUTDOWN_TRIES=20;
+
+    public TcpConnection() {
+    }
+
+    // -------------------- Properties --------------------
+
+    PoolTcpEndpoint endpoint;
+    Socket socket;
+
+    public static void setMaxShutdownTries(int mst) {
+	MAX_SHUTDOWN_TRIES = mst;
+    }
+    public void setEndpoint(PoolTcpEndpoint endpoint) {
+	this.endpoint = endpoint;
+    }
+
+    public PoolTcpEndpoint getEndpoint() {
+	return endpoint;
+    }
+
+    public void setSocket(Socket socket) {
+	this.socket=socket;
+    }
+
+    public Socket getSocket() {
+	return socket;
+    }
+
+    public void recycle() {
+        endpoint = null;
+        socket = null;
+    }
+
+    // Another frequent repetition
+    public static int readLine(InputStream in, byte[] b, int off, int len)
+	throws IOException
+    {
+	if (len <= 0) {
+	    return 0;
+	}
+	int count = 0, c;
+
+	while ((c = in.read()) != -1) {
+	    b[off++] = (byte)c;
+	    count++;
+	    if (c == '\n' || count == len) {
+		break;
+	    }
+	}
+	return count > 0 ? count : -1;
+    }
+
+    
+    // Usefull stuff - avoid having it replicated everywhere
+    public static void shutdownInput(Socket socket)
+	throws IOException
+    {
+	try {
+	    InputStream is = socket.getInputStream();
+	    int available = is.available ();
+	    int count=0;
+	    
+	    // XXX on JDK 1.3 just socket.shutdownInput () which
+	    // was added just to deal with such issues.
+	    
+	    // skip any unread (bogus) bytes
+	    while (available > 0 && count++ < MAX_SHUTDOWN_TRIES) {
+		is.skip (available);
+		available = is.available();
+	    }
+	}catch(NullPointerException npe) {
+	    // do nothing - we are just cleaning up, this is
+	    // a workaround for Netscape \n\r in POST - it is supposed
+	    // to be ignored
+	}
+    }
+}
+
+
diff --git a/connectors/util/java/org/apache/tomcat/util/net/TcpConnectionHandler.java b/connectors/util/java/org/apache/tomcat/util/net/TcpConnectionHandler.java
new file mode 100644
index 0000000..f2e6008
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/TcpConnectionHandler.java
@@ -0,0 +1,65 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net;
+
+
+/**
+ * This interface will be implemented by any object that
+ * uses TcpConnections. It is supported by the pool tcp
+ * connection manager and should be supported by future
+ * managers.
+ * The goal is to decouple the connection handler from
+ * the thread, socket and pooling complexity.
+ */
+public interface TcpConnectionHandler {
+    
+    /** Add informations about the a "controler" object
+     *  specific to the server. In tomcat it will be a
+     *  ContextManager.
+     *  @deprecated This has nothing to do with TcpHandling,
+     *  was used as a workaround
+     */
+    public void setServer(Object manager);
+
+    
+    /** Used to pass config informations to the handler.
+     *
+     *  @deprecated This has nothing to do with Tcp,
+     *    was used as a workaround.
+     */
+    public void setAttribute(String name, Object value );
+    
+    /** Called before the call to processConnection.
+     *  If the thread is reused, init() should be called once per thread.
+     *
+     *  It may look strange, but it's a _very_ good way to avoid synchronized
+     *  methods and keep per thread data.
+     *
+     *  Assert: the object returned from init() will be passed to
+     *  all processConnection() methods happening in the same thread.
+     * 
+     */
+    public Object[] init( );
+
+    /**
+     *  Assert: connection!=null
+     *  Assert: connection.getSocket() != null
+     *  Assert: thData != null and is the result of calling init()
+     *  Assert: thData is preserved per Thread.
+     */
+    public void processConnection(TcpConnection connection, Object thData[]);    
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/URL.java b/connectors/util/java/org/apache/tomcat/util/net/URL.java
new file mode 100644
index 0000000..e8050c5
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/URL.java
@@ -0,0 +1,731 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net;
+
+
+import java.io.Serializable;
+import java.net.MalformedURLException;
+
+
+/**
+ * <p><strong>URL</strong> is designed to provide public APIs for parsing
+ * and synthesizing Uniform Resource Locators as similar as possible to the
+ * APIs of <code>java.net.URL</code>, but without the ability to open a
+ * stream or connection.  One of the consequences of this is that you can
+ * construct URLs for protocols for which a URLStreamHandler is not
+ * available (such as an "https" URL when JSSE is not installed).</p>
+ *
+ * <p><strong>WARNING</strong> - This class assumes that the string
+ * representation of a URL conforms to the <code>spec</code> argument
+ * as described in RFC 2396 "Uniform Resource Identifiers: Generic Syntax":
+ * <pre>
+ *   &lt;scheme&gt;//&lt;authority&gt;&lt;path&gt;?&lt;query&gt;#&lt;fragment&gt;
+ * </pre></p>
+ *
+ * <p><strong>FIXME</strong> - This class really ought to end up in a Commons
+ * package someplace.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision$ $Date$
+ */
+
+public final class URL implements Serializable {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create a URL object from the specified String representation.
+     *
+     * @param spec String representation of the URL
+     *
+     * @exception MalformedURLException if the string representation
+     *  cannot be parsed successfully
+     */
+    public URL(String spec) throws MalformedURLException {
+
+        this(null, spec);
+
+    }
+
+
+    /**
+     * Create a URL object by parsing a string representation relative
+     * to a specified context.  Based on logic from JDK 1.3.1's
+     * <code>java.net.URL</code>.
+     *
+     * @param context URL against which the relative representation
+     *  is resolved
+     * @param spec String representation of the URL (usually relative)
+     *
+     * @exception MalformedURLException if the string representation
+     *  cannot be parsed successfully
+     */
+    public URL(URL context, String spec) throws MalformedURLException {
+
+        String original = spec;
+        int i, limit, c;
+        int start = 0;
+        String newProtocol = null;
+        boolean aRef = false;
+
+        try {
+
+            // Eliminate leading and trailing whitespace
+            limit = spec.length();
+            while ((limit > 0) && (spec.charAt(limit - 1) <= ' ')) {
+                limit--;
+            }
+            while ((start < limit) && (spec.charAt(start) <= ' ')) {
+                start++;
+            }
+
+            // If the string representation starts with "url:", skip it
+            if (spec.regionMatches(true, start, "url:", 0, 4)) {
+                start += 4;
+            }
+
+            // Is this a ref relative to the context URL?
+            if ((start < spec.length()) && (spec.charAt(start) == '#')) {
+                aRef = true;
+            }
+
+            // Parse out the new protocol
+            for (i = start; !aRef && (i < limit) ; i++) { 
+                c = spec.charAt(i);
+                if (c == ':') {
+                    String s = spec.substring(start, i).toLowerCase();
+                    // Assume all protocols are valid
+                    newProtocol = s;
+                    start = i + 1;
+                    break;
+                } else if( c == '#' ) {
+                    aRef = true;
+                } else if( !isSchemeChar((char)c) ) {
+                    break;
+                }
+            }
+
+            // Only use our context if the protocols match
+            protocol = newProtocol;
+            if ((context != null) && ((newProtocol == null) ||
+                 newProtocol.equalsIgnoreCase(context.getProtocol()))) {
+                // If the context is a hierarchical URL scheme and the spec
+                // contains a matching scheme then maintain backwards
+                // compatibility and treat it as if the spec didn't contain
+                // the scheme; see 5.2.3 of RFC2396
+                if ((context.getPath() != null) &&
+                    (context.getPath().startsWith("/")))
+                    newProtocol = null;
+                if (newProtocol == null) {
+                    protocol = context.getProtocol();
+                    authority = context.getAuthority();
+                    userInfo = context.getUserInfo();
+                    host = context.getHost();
+                    port = context.getPort();
+                    file = context.getFile();
+                    int question = file.lastIndexOf("?");
+                    if (question < 0)
+                        path = file;
+                    else
+                        path = file.substring(0, question);
+                }
+            }
+
+            if (protocol == null)
+                throw new MalformedURLException("no protocol: " + original);
+
+            // Parse out any ref portion of the spec
+            i = spec.indexOf('#', start);
+            if (i >= 0) {
+                ref = spec.substring(i + 1, limit);
+                limit = i;
+            }
+
+            // Parse the remainder of the spec in a protocol-specific fashion
+            parse(spec, start, limit);
+            if (context != null)
+                normalize();
+
+
+        } catch (MalformedURLException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new MalformedURLException(e.toString());
+        }
+
+    }
+
+
+
+
+
+    /**
+     * Create a URL object from the specified components.  The default port
+     * number for the specified protocol will be used.
+     *
+     * @param protocol Name of the protocol to use
+     * @param host Name of the host addressed by this protocol
+     * @param file Filename on the specified host
+     *
+     * @exception MalformedURLException is never thrown, but present for
+     *  compatible APIs
+     */
+    public URL(String protocol, String host, String file)
+        throws MalformedURLException {
+
+        this(protocol, host, -1, file);
+
+    }
+
+
+    /**
+     * Create a URL object from the specified components.  Specifying a port
+     * number of -1 indicates that the URL should use the default port for
+     * that protocol.  Based on logic from JDK 1.3.1's
+     * <code>java.net.URL</code>.
+     *
+     * @param protocol Name of the protocol to use
+     * @param host Name of the host addressed by this protocol
+     * @param port Port number, or -1 for the default port for this protocol
+     * @param file Filename on the specified host
+     *
+     * @exception MalformedURLException is never thrown, but present for
+     *  compatible APIs
+     */
+    public URL(String protocol, String host, int port, String file)
+        throws MalformedURLException {
+
+        this.protocol = protocol;
+        this.host = host;
+        this.port = port;
+
+        int hash = file.indexOf('#');
+        this.file = hash < 0 ? file : file.substring(0, hash);
+        this.ref = hash < 0 ? null : file.substring(hash + 1);
+        int question = file.lastIndexOf('?');
+        if (question >= 0) {
+            query = file.substring(question + 1);
+            path = file.substring(0, question);
+        } else
+            path = file;
+
+        if ((host != null) && (host.length() > 0))
+            authority = (port == -1) ? host : host + ":" + port;
+
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * The authority part of the URL.
+     */
+    private String authority = null;
+
+
+    /**
+     * The filename part of the URL.
+     */
+    private String file = null;
+
+
+    /**
+     * The host name part of the URL.
+     */
+    private String host = null;
+
+
+    /**
+     * The path part of the URL.
+     */
+    private String path = null;
+
+
+    /**
+     * The port number part of the URL.
+     */
+    private int port = -1;
+
+
+    /**
+     * The protocol name part of the URL.
+     */
+    private String protocol = null;
+
+
+    /**
+     * The query part of the URL.
+     */
+    private String query = null;
+
+
+    /**
+     * The reference part of the URL.
+     */
+    private String ref = null;
+
+
+    /**
+     * The user info part of the URL.
+     */
+    private String userInfo = null;
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Compare two URLs for equality.  The result is <code>true</code> if and
+     * only if the argument is not null, and is a <code>URL</code> object
+     * that represents the same <code>URL</code> as this object.  Two
+     * <code>URLs</code> are equal if they have the same protocol and
+     * reference the same host, the same port number on the host,
+     * and the same file and anchor on the host.
+     *
+     * @param obj The URL to compare against
+     */
+    public boolean equals(Object obj) {
+
+        if (obj == null)
+            return (false);
+        if (!(obj instanceof URL))
+            return (false);
+        URL other = (URL) obj;
+        if (!sameFile(other))
+            return (false);
+        return (compare(ref, other.getRef()));
+
+    }
+
+
+    /**
+     * Return the authority part of the URL.
+     */
+    public String getAuthority() {
+
+        return (this.authority);
+
+    }
+
+
+    /**
+     * Return the filename part of the URL.  <strong>NOTE</strong> - For
+     * compatibility with <code>java.net.URL</code>, this value includes
+     * the query string if there was one.  For just the path portion,
+     * call <code>getPath()</code> instead.
+     */
+    public String getFile() {
+
+        if (file == null)
+            return ("");
+        return (this.file);
+
+    }
+
+
+    /**
+     * Return the host name part of the URL.
+     */
+    public String getHost() {
+
+        return (this.host);
+
+    }
+
+
+    /**
+     * Return the path part of the URL.
+     */
+    public String getPath() {
+
+        if (this.path == null)
+            return ("");
+        return (this.path);
+
+    }
+
+
+    /**
+     * Return the port number part of the URL.
+     */
+    public int getPort() {
+
+        return (this.port);
+
+    }
+
+
+    /**
+     * Return the protocol name part of the URL.
+     */
+    public String getProtocol() {
+
+        return (this.protocol);
+
+    }
+
+
+    /**
+     * Return the query part of the URL.
+     */
+    public String getQuery() {
+
+        return (this.query);
+
+    }
+
+
+    /**
+     * Return the reference part of the URL.
+     */
+    public String getRef() {
+
+        return (this.ref);
+
+    }
+
+
+    /**
+     * Return the user info part of the URL.
+     */
+    public String getUserInfo() {
+
+        return (this.userInfo);
+
+    }
+
+
+    /**
+     * Normalize the <code>path</code> (and therefore <code>file</code>)
+     * portions of this URL.
+     * <p>
+     * <strong>NOTE</strong> - This method is not part of the public API
+     * of <code>java.net.URL</code>, but is provided as a value added
+     * service of this implementation.
+     *
+     * @exception MalformedURLException if a normalization error occurs,
+     *  such as trying to move about the hierarchical root
+     */
+    public void normalize() throws MalformedURLException {
+
+        // Special case for null path
+        if (path == null) {
+            if (query != null)
+                file = "?" + query;
+            else
+                file = "";
+            return;
+        }
+
+        // Create a place for the normalized path
+        String normalized = path;
+        if (normalized.equals("/.")) {
+            path = "/";
+            if (query != null)
+                file = path + "?" + query;
+            else
+                file = path;
+            return;
+        }
+
+        // Normalize the slashes and add leading slash if necessary
+        if (normalized.indexOf('\\') >= 0)
+            normalized = normalized.replace('\\', '/');
+        if (!normalized.startsWith("/"))
+            normalized = "/" + normalized;
+
+        // Resolve occurrences of "//" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("//");
+            if (index < 0)
+                break;
+            normalized = normalized.substring(0, index) +
+                normalized.substring(index + 1);
+        }
+
+        // Resolve occurrences of "/./" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("/./");
+            if (index < 0)
+                break;
+            normalized = normalized.substring(0, index) +
+                normalized.substring(index + 2);
+        }
+
+        // Resolve occurrences of "/../" in the normalized path
+        while (true) {
+            int index = normalized.indexOf("/../");
+            if (index < 0)
+                break;
+            if (index == 0)
+                throw new MalformedURLException
+                    ("Invalid relative URL reference");
+            int index2 = normalized.lastIndexOf('/', index - 1);
+            normalized = normalized.substring(0, index2) +
+                normalized.substring(index + 3);
+        }
+
+        // Resolve occurrences of "/." at the end of the normalized path
+        if (normalized.endsWith("/."))
+            normalized = normalized.substring(0, normalized.length() - 1);
+
+        // Resolve occurrences of "/.." at the end of the normalized path
+        if (normalized.endsWith("/..")) {
+            int index = normalized.length() - 3;
+            int index2 = normalized.lastIndexOf('/', index - 1);
+            if (index2 < 0)
+                throw new MalformedURLException
+                    ("Invalid relative URL reference");
+            normalized = normalized.substring(0, index2 + 1);
+        }
+
+        // Return the normalized path that we have completed
+        path = normalized;
+        if (query != null)
+            file = path + "?" + query;
+        else
+            file = path;
+
+    }
+
+
+    /**
+     * Compare two URLs, excluding the "ref" fields.  Returns <code>true</code>
+     * if this <code>URL</code> and the <code>other</code> argument both refer
+     * to the same resource.  The two <code>URLs</code> might not both contain
+     * the same anchor.
+     */
+    public boolean sameFile(URL other) {
+
+        if (!compare(protocol, other.getProtocol()))
+            return (false);
+        if (!compare(host, other.getHost()))
+            return (false);
+        if (port != other.getPort())
+            return (false);
+        if (!compare(file, other.getFile()))
+            return (false);
+        return (true);
+
+    }
+
+
+    /**
+     * Return a string representation of this URL.  This follow the rules in
+     * RFC 2396, Section 5.2, Step 7.
+     */
+    public String toExternalForm() {
+
+        StringBuffer sb = new StringBuffer();
+        if (protocol != null) {
+            sb.append(protocol);
+            sb.append(":");
+        }
+        if (authority != null) {
+            sb.append("//");
+            sb.append(authority);
+        }
+        if (path != null)
+            sb.append(path);
+        if (query != null) {
+            sb.append('?');
+            sb.append(query);
+        }
+        if (ref != null) {
+            sb.append('#');
+            sb.append(ref);
+        }
+        return (sb.toString());
+
+    }
+
+
+    /**
+     * Return a string representation of this object.
+     */
+    public String toString() {
+
+        StringBuffer sb = new StringBuffer("URL[");
+        sb.append("authority=");
+        sb.append(authority);
+        sb.append(", file=");
+        sb.append(file);
+        sb.append(", host=");
+        sb.append(host);
+        sb.append(", port=");
+        sb.append(port);
+        sb.append(", protocol=");
+        sb.append(protocol);
+        sb.append(", query=");
+        sb.append(query);
+        sb.append(", ref=");
+        sb.append(ref);
+        sb.append(", userInfo=");
+        sb.append(userInfo);
+        sb.append("]");
+        return (sb.toString());
+
+        //        return (toExternalForm());
+
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+
+    /**
+     * Compare to String values for equality, taking appropriate care if one
+     * or both of the values are <code>null</code>.
+     *
+     * @param first First string
+     * @param second Second string
+     */
+    private boolean compare(String first, String second) {
+
+        if (first == null) {
+            if (second == null)
+                return (true);
+            else
+                return (false);
+        } else {
+            if (second == null)
+                return (false);
+            else
+                return (first.equals(second));
+        }
+
+    }
+
+
+    /**
+     * Parse the specified portion of the string representation of a URL,
+     * assuming that it has a format similar to that for <code>http</code>.
+     *
+     * <p><strong>FIXME</strong> - This algorithm can undoubtedly be optimized
+     * for performance.  However, that needs to wait until after sufficient
+     * unit tests are implemented to guarantee correct behavior with no
+     * regressions.</p>
+     *
+     * @param spec String representation being parsed
+     * @param start Starting offset, which will be just after the ':' (if
+     *  there is one) that determined the protocol name
+     * @param limit Ending position, which will be the position of the '#'
+     *  (if there is one) that delimited the anchor
+     *
+     * @exception MalformedURLException if a parsing error occurs
+     */
+    private void parse(String spec, int start, int limit)
+        throws MalformedURLException {
+
+        // Trim the query string (if any) off the tail end
+        int question = spec.lastIndexOf('?', limit - 1);
+        if ((question >= 0) && (question < limit)) {
+            query = spec.substring(question + 1, limit);
+            limit = question;
+        } else {
+            query = null;
+        }
+
+        // Parse the authority section
+        if (spec.indexOf("//", start) == start) {
+            int pathStart = spec.indexOf("/", start + 2);
+            if ((pathStart >= 0) && (pathStart < limit)) {
+                authority = spec.substring(start + 2, pathStart);
+                start = pathStart;
+            } else {
+                authority = spec.substring(start + 2, limit);
+                start = limit;
+            }
+            if (authority.length() > 0) {
+                int at = authority.indexOf('@');
+                if( at >= 0 ) {
+                    userInfo = authority.substring(0,at);
+                }
+                int ipv6 = authority.indexOf('[',at+1);
+                int hStart = at+1;
+                if( ipv6 >= 0 ) {
+                    hStart = ipv6;
+                    ipv6 = authority.indexOf(']', ipv6);
+                    if( ipv6 < 0 ) {
+                        throw new MalformedURLException(
+                                                        "Closing ']' not found in IPV6 address: " + authority);
+                    } else {
+                        at = ipv6-1;
+                    }
+                }
+                                                        
+                int colon = authority.indexOf(':', at+1);
+                if (colon >= 0) {
+                    try {
+                        port =
+                            Integer.parseInt(authority.substring(colon + 1));
+                    } catch (NumberFormatException e) {
+                        throw new MalformedURLException(e.toString());
+                    }
+                    host = authority.substring(hStart, colon);
+                } else {
+                    host = authority.substring(hStart);
+                    port = -1;
+                }
+            }
+        }
+
+        // Parse the path section
+        if (spec.indexOf("/", start) == start) {     // Absolute path
+            path = spec.substring(start, limit);
+            if (query != null)
+                file = path + "?" + query;
+            else
+                file = path;
+            return;
+        }
+
+        // Resolve relative path against our context's file
+        if (path == null) {
+            if (query != null)
+                file = "?" + query;
+            else
+                file = null;
+            return;
+        }
+        if (!path.startsWith("/"))
+            throw new MalformedURLException
+                ("Base path does not start with '/'");
+        if (!path.endsWith("/"))
+            path += "/../";
+        path += spec.substring(start, limit);
+        if (query != null)
+            file = path + "?" + query;
+        else
+            file = path;
+        return;
+
+    }
+
+    /**
+     * Determine if the character is allowed in the scheme of a URI.
+     * See RFC 2396, Section 3.1
+     */
+    public static boolean isSchemeChar(char c) {
+        return Character.isLetterOrDigit(c) ||
+            c == '+' || c == '-' || c == '.';
+    }
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE13Factory.java b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE13Factory.java
new file mode 100644
index 0000000..b4be466
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE13Factory.java
@@ -0,0 +1,43 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net.jsse;
+
+import java.net.Socket;
+import javax.net.ssl.SSLSocket;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.ServerSocketFactory;
+
+/**
+ * Implementation class for JSSEFactory for JSSE 1.0.x (that is an extension
+ * to the 1.3 JVM).
+ *
+ * @author Bill Barker
+ */
+
+class JSSE13Factory implements JSSEFactory {
+
+    JSSE13Factory() {
+    }
+
+    public ServerSocketFactory getSocketFactory() {
+        return new JSSE13SocketFactory();
+    }
+
+    public SSLSupport getSSLSupport(Socket socket) {
+        return new JSSESupport((SSLSocket)socket);
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE13SocketFactory.java b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE13SocketFactory.java
new file mode 100644
index 0000000..e511ee8
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE13SocketFactory.java
@@ -0,0 +1,150 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net.jsse;
+
+import java.io.IOException;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.security.Security;
+
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLSocket;
+
+/*
+  1. Make the JSSE's jars available, either as an installed
+     extension (copy them into jre/lib/ext) or by adding
+     them to the Tomcat classpath.
+  2. keytool -genkey -alias tomcat -keyalg RSA
+     Use "changeit" as password ( this is the default we use )
+ */
+
+/**
+ * SSL server socket factory. It _requires_ a valid RSA key and
+ * JSSE. 
+ *
+ * @author Harish Prabandham
+ * @author Costin Manolache
+ * @author Stefan Freyr Stefansson
+ * @author EKR -- renamed to JSSESocketFactory
+ * @author Bill Barker
+ */
+public class JSSE13SocketFactory extends JSSESocketFactory
+{
+    /**
+     * Flag for client authentication
+     */
+    protected boolean clientAuth = false;
+
+    public JSSE13SocketFactory () {
+        super();
+    }
+
+    /**
+     * Reads the keystore and initializes the SSL socket factory.
+     *
+     * NOTE: This method is identical in functionality to the method of the
+     * same name in JSSE14SocketFactory, except that this method is used with
+     * JSSE 1.0.x (which is an extension to the 1.3 JVM), whereas the other is
+     * used with JSSE 1.1.x (which ships with the 1.4 JVM). Therefore, this
+     * method uses classes in com.sun.net.ssl, which have since moved to
+     * javax.net.ssl, and explicitly registers the required security providers,
+     * which come standard in a 1.4 JVM.
+     */
+     void init() throws IOException {
+        try {
+            Security.addProvider (new sun.security.provider.Sun());
+            Security.addProvider (new com.sun.net.ssl.internal.ssl.Provider());
+
+            String clientAuthStr = (String)attributes.get("clientauth");
+            if("true".equalsIgnoreCase(clientAuthStr) || 
+               "yes".equalsIgnoreCase(clientAuthStr)  ||
+               "want".equalsIgnoreCase(clientAuthStr)) {
+                clientAuth = true;
+            }
+            
+            // SSL protocol variant (e.g., TLS, SSL v3, etc.)
+            String protocol = (String)attributes.get("protocol");
+            if (protocol == null) protocol = defaultProtocol;
+            
+            // Certificate encoding algorithm (e.g., SunX509)
+            String algorithm = (String)attributes.get("algorithm");
+            if (algorithm == null) algorithm = defaultAlgorithm;
+
+            // Set up KeyManager, which will extract server key
+            com.sun.net.ssl.KeyManagerFactory kmf = 
+                com.sun.net.ssl.KeyManagerFactory.getInstance(algorithm);
+            String keystoreType = (String)attributes.get("keystoreType");
+            if (keystoreType == null) {
+                keystoreType = defaultKeystoreType;
+            }
+            String keystorePass = getKeystorePassword();
+            kmf.init(getKeystore(keystoreType, keystorePass),
+                     keystorePass.toCharArray());
+
+            // Set up TrustManager
+            com.sun.net.ssl.TrustManager[] tm = null;
+            String truststoreType = (String)attributes.get("truststoreType");
+            if(truststoreType == null) {
+                truststoreType = keystoreType;
+            }
+            KeyStore trustStore = getTrustStore(truststoreType);
+            if (trustStore != null) {
+                com.sun.net.ssl.TrustManagerFactory tmf =
+                    com.sun.net.ssl.TrustManagerFactory.getInstance("SunX509");
+                tmf.init(trustStore);
+                tm = tmf.getTrustManagers();
+            }
+
+            // Create and init SSLContext
+            com.sun.net.ssl.SSLContext context = 
+                com.sun.net.ssl.SSLContext.getInstance(protocol); 
+            context.init(kmf.getKeyManagers(), tm, new SecureRandom());
+
+            // Create proxy
+            sslProxy = context.getServerSocketFactory();
+
+            // Determine which cipher suites to enable
+            String requestedCiphers = (String)attributes.get("ciphers");
+            enabledCiphers = getEnabledCiphers(requestedCiphers,
+                     sslProxy.getSupportedCipherSuites());
+
+        } catch(Exception e) {
+            if( e instanceof IOException )
+                throw (IOException)e;
+            throw new IOException(e.getMessage());
+        }
+    }
+    protected String[] getEnabledProtocols(SSLServerSocket socket,
+                                           String requestedProtocols){
+        return null;
+    }
+    protected void setEnabledProtocols(SSLServerSocket socket, 
+                                             String [] protocols){
+    }
+
+    protected void configureClientAuth(SSLServerSocket socket){
+        socket.setNeedClientAuth(clientAuth);
+    }
+
+    protected void configureClientAuth(SSLSocket socket){
+        // In JSSE 1.0.2 docs it does not explicitly
+        // state whether SSLSockets returned from 
+        // SSLServerSocket.accept() inherit this setting.
+        socket.setNeedClientAuth(clientAuth);
+    }
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE14Factory.java b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE14Factory.java
new file mode 100644
index 0000000..e253c72
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE14Factory.java
@@ -0,0 +1,43 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net.jsse;
+
+import java.net.Socket;
+import javax.net.ssl.SSLSocket;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.ServerSocketFactory;
+
+/**
+ * Implementation class for JSSEFactory for JSSE 1.1.x (that ships with the
+ * 1.4 JVM).
+ *
+ * @author Bill Barker
+ */
+
+class JSSE14Factory implements JSSEFactory {
+
+    JSSE14Factory() {
+    }
+
+    public ServerSocketFactory getSocketFactory() {
+	return new JSSE14SocketFactory();
+    }
+
+    public SSLSupport getSSLSupport(Socket socket) {
+	return new JSSE14Support((SSLSocket)socket);
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE14SocketFactory.java b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE14SocketFactory.java
new file mode 100644
index 0000000..6cc1acd
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE14SocketFactory.java
@@ -0,0 +1,267 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net.jsse;
+
+import java.io.IOException;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.util.Vector;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509KeyManager;
+
+import org.apache.tomcat.util.res.StringManager;
+
+/*
+  1. Make the JSSE's jars available, either as an installed
+     extension (copy them into jre/lib/ext) or by adding
+     them to the Tomcat classpath.
+  2. keytool -genkey -alias tomcat -keyalg RSA
+     Use "changeit" as password ( this is the default we use )
+ */
+
+/**
+ * SSL server socket factory. It _requires_ a valid RSA key and
+ * JSSE. 
+ *
+ * @author Harish Prabandham
+ * @author Costin Manolache
+ * @author Stefan Freyr Stefansson
+ * @author EKR -- renamed to JSSESocketFactory
+ * @author Jan Luehe
+ */
+public class JSSE14SocketFactory  extends JSSESocketFactory {
+
+    private static StringManager sm =
+        StringManager.getManager("org.apache.tomcat.util.net.jsse.res");
+
+    /**
+     * Flag to state that we require client authentication.
+     */
+    protected boolean requireClientAuth = false;
+
+    /**
+     * Flag to state that we would like client authentication.
+     */
+    protected boolean wantClientAuth    = false;
+
+    public JSSE14SocketFactory () {
+        super();
+    }
+
+    /**
+     * Reads the keystore and initializes the SSL socket factory.
+     */
+    void init() throws IOException {
+        try {
+
+            String clientAuthStr = (String) attributes.get("clientauth");
+            if("true".equalsIgnoreCase(clientAuthStr) ||
+               "yes".equalsIgnoreCase(clientAuthStr)) {
+                requireClientAuth = true;
+            } else if("want".equalsIgnoreCase(clientAuthStr)) {
+                wantClientAuth = true;
+            }
+
+            // SSL protocol variant (e.g., TLS, SSL v3, etc.)
+            String protocol = (String) attributes.get("protocol");
+            if (protocol == null) {
+                protocol = defaultProtocol;
+            }
+
+            // Certificate encoding algorithm (e.g., SunX509)
+            String algorithm = (String) attributes.get("algorithm");
+            if (algorithm == null) {
+                algorithm = defaultAlgorithm;
+            }
+
+            String keystoreType = (String) attributes.get("keystoreType");
+            if (keystoreType == null) {
+                keystoreType = defaultKeystoreType;
+            }
+
+	    String trustAlgorithm = (String)attributes.get("truststoreAlgorithm");
+	    if( trustAlgorithm == null ) {
+		trustAlgorithm = algorithm;
+	    }
+            // Create and init SSLContext
+            SSLContext context = SSLContext.getInstance(protocol); 
+            context.init(getKeyManagers(keystoreType, algorithm,
+                                        (String) attributes.get("keyAlias")),
+                         getTrustManagers(keystoreType, trustAlgorithm),
+                         new SecureRandom());
+
+            // create proxy
+            sslProxy = context.getServerSocketFactory();
+
+            // Determine which cipher suites to enable
+            String requestedCiphers = (String)attributes.get("ciphers");
+            enabledCiphers = getEnabledCiphers(requestedCiphers,
+                                               sslProxy.getSupportedCipherSuites());
+
+        } catch(Exception e) {
+            if( e instanceof IOException )
+                throw (IOException)e;
+            throw new IOException(e.getMessage());
+        }
+    }
+
+    /**
+     * Gets the initialized key managers.
+     */
+    protected KeyManager[] getKeyManagers(String keystoreType,
+                                          String algorithm,
+                                          String keyAlias)
+                throws Exception {
+
+        KeyManager[] kms = null;
+
+        String keystorePass = getKeystorePassword();
+
+        KeyStore ks = getKeystore(keystoreType, keystorePass);
+        if (keyAlias != null && !ks.isKeyEntry(keyAlias)) {
+            throw new IOException(sm.getString("jsse.alias_no_key_entry", keyAlias));
+        }
+
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
+        kmf.init(ks, keystorePass.toCharArray());
+
+        kms = kmf.getKeyManagers();
+        if (keyAlias != null) {
+            if (JSSESocketFactory.defaultKeystoreType.equals(keystoreType)) {
+                keyAlias = keyAlias.toLowerCase();
+            }
+            for(int i=0; i<kms.length; i++) {
+                kms[i] = new JSSEKeyManager((X509KeyManager)kms[i], keyAlias);
+            }
+        }
+
+        return kms;
+    }
+
+    /**
+     * Gets the intialized trust managers.
+     */
+    protected TrustManager[] getTrustManagers(String keystoreType, String algorithm)
+                throws Exception {
+
+        TrustManager[] tms = null;
+
+        String truststoreType = (String)attributes.get("truststoreType");
+        if(truststoreType == null) {
+            truststoreType = keystoreType;
+        }
+        KeyStore trustStore = getTrustStore(truststoreType);
+        if (trustStore != null) {
+            TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
+            tmf.init(trustStore);
+            tms = tmf.getTrustManagers();
+        }
+
+        return tms;
+    }
+    protected void setEnabledProtocols(SSLServerSocket socket, String []protocols){
+        if (protocols != null) {
+            socket.setEnabledProtocols(protocols);
+        }
+    }
+
+    protected String[] getEnabledProtocols(SSLServerSocket socket,
+                                           String requestedProtocols){
+        String[] supportedProtocols = socket.getSupportedProtocols();
+
+        String[] enabledProtocols = null;
+
+        if (requestedProtocols != null) {
+            Vector vec = null;
+            String protocol = requestedProtocols;
+            int index = requestedProtocols.indexOf(',');
+            if (index != -1) {
+                int fromIndex = 0;
+                while (index != -1) {
+                    protocol = requestedProtocols.substring(fromIndex, index).trim();
+                    if (protocol.length() > 0) {
+                        /*
+                         * Check to see if the requested protocol is among the
+                         * supported protocols, i.e., may be enabled
+                         */
+                        for (int i=0; supportedProtocols != null
+                                     && i<supportedProtocols.length; i++) {
+                            if (supportedProtocols[i].equals(protocol)) {
+                                if (vec == null) {
+                                    vec = new Vector();
+                                }
+                                vec.addElement(protocol);
+                                break;
+                            }
+                        }
+                    }
+                    fromIndex = index+1;
+                    index = requestedProtocols.indexOf(',', fromIndex);
+                } // while
+                protocol = requestedProtocols.substring(fromIndex);
+            }
+
+            if (protocol != null) {
+                protocol = protocol.trim();
+                if (protocol.length() > 0) {
+                    /*
+                     * Check to see if the requested protocol is among the
+                     * supported protocols, i.e., may be enabled
+                     */
+                    for (int i=0; supportedProtocols != null
+                                 && i<supportedProtocols.length; i++) {
+                        if (supportedProtocols[i].equals(protocol)) {
+                            if (vec == null) {
+                                vec = new Vector();
+                            }
+                            vec.addElement(protocol);
+                            break;
+                        }
+                    }
+                }
+            }           
+
+            if (vec != null) {
+                enabledProtocols = new String[vec.size()];
+                vec.copyInto(enabledProtocols);
+            }
+        }
+
+        return enabledProtocols;
+    }
+
+    protected void configureClientAuth(SSLServerSocket socket){
+        if (wantClientAuth){
+            socket.setWantClientAuth(wantClientAuth);
+        } else {
+            socket.setNeedClientAuth(requireClientAuth);
+        }
+    }
+
+    protected void configureClientAuth(SSLSocket socket){
+        // Per JavaDocs: SSLSockets returned from 
+        // SSLServerSocket.accept() inherit this setting.
+    }
+    
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE14Support.java b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE14Support.java
new file mode 100644
index 0000000..dee4ad5
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSE14Support.java
@@ -0,0 +1,158 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net.jsse;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.SocketException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+
+/* JSSESupport
+
+   Concrete implementation class for JSSE
+   Support classes.
+
+   This will only work with JDK 1.2 and up since it
+   depends on JDK 1.2's certificate support
+
+   @author EKR
+   @author Craig R. McClanahan
+   Parts cribbed from JSSECertCompat       
+   Parts cribbed from CertificatesValve
+*/
+
+class JSSE14Support extends JSSESupport {
+
+    private static org.apache.commons.logging.Log logger =
+        org.apache.commons.logging.LogFactory.getLog(JSSE14Support.class);
+
+    Listener listener = new Listener();
+
+    public JSSE14Support(SSLSocket sock){
+        super(sock);
+        sock.addHandshakeCompletedListener(listener);
+    }
+
+    protected void handShake() throws IOException {
+        if( ssl.getWantClientAuth() ) {
+            logger.debug("No client cert sent for want");
+        } else {
+            ssl.setNeedClientAuth(true);
+        }
+        synchronousHandshake(ssl);
+    }
+
+    /**
+     * JSSE in JDK 1.4 has an issue/feature that requires us to do a
+     * read() to get the client-cert.  As suggested by Andreas
+     * Sterbenz
+     */
+    private  void synchronousHandshake(SSLSocket socket) 
+        throws IOException {
+        InputStream in = socket.getInputStream();
+        int oldTimeout = socket.getSoTimeout();
+        socket.setSoTimeout(1000);
+        byte[] b = new byte[0];
+        listener.reset();
+        socket.startHandshake();
+        int maxTries = 60; // 60 * 1000 = example 1 minute time out
+        for (int i = 0; i < maxTries; i++) {
+	    if(logger.isTraceEnabled())
+		logger.trace("Reading for try #" +i);
+            try {
+                int x = in.read(b);
+            } catch(SSLException sslex) {
+                logger.info("SSL Error getting client Certs",sslex);
+                throw sslex;
+            } catch (IOException e) {
+                // ignore - presumably the timeout
+            }
+            if (listener.completed) {
+                break;
+            }
+        }
+        socket.setSoTimeout(oldTimeout);
+        if (listener.completed == false) {
+            throw new SocketException("SSL Cert handshake timeout");
+        }
+    }
+
+    /** Return the X509certificates or null if we can't get them.
+     *  XXX We should allow unverified certificates 
+     */ 
+    protected X509Certificate [] getX509Certificates(SSLSession session) 
+	throws IOException 
+    {
+        Certificate [] certs=null;
+        try {
+	    certs = session.getPeerCertificates();
+        } catch( Throwable t ) {
+            logger.debug("Error getting client certs",t);
+            return null;
+        }
+        if( certs==null ) return null;
+        
+        X509Certificate [] x509Certs = new X509Certificate[certs.length];
+	for(int i=0; i < certs.length; i++) {
+	    if( certs[i] instanceof X509Certificate ) {
+		// always currently true with the JSSE 1.1.x
+		x509Certs[i] = (X509Certificate)certs[i];
+	    } else {
+		try {
+		    byte [] buffer = certs[i].getEncoded();
+		    CertificateFactory cf =
+			CertificateFactory.getInstance("X.509");
+		    ByteArrayInputStream stream =
+			new ByteArrayInputStream(buffer);
+		    x509Certs[i] = (X509Certificate)
+			cf.generateCertificate(stream);
+		} catch(Exception ex) { 
+		    logger.info("Error translating cert " + certs[i], ex);
+		    return null;
+		}
+	    }
+	    if(logger.isTraceEnabled())
+		logger.trace("Cert #" + i + " = " + x509Certs[i]);
+	}
+	if(x509Certs.length < 1)
+	    return null;
+	return x509Certs;
+    }
+
+
+    private static class Listener implements HandshakeCompletedListener {
+        volatile boolean completed = false;
+        public void handshakeCompleted(HandshakeCompletedEvent event) {
+            completed = true;
+        }
+        void reset() {
+            completed = false;
+        }
+    }
+
+}
+
diff --git a/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSEFactory.java b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSEFactory.java
new file mode 100644
index 0000000..a72067b
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSEFactory.java
@@ -0,0 +1,42 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net.jsse;
+
+import java.net.Socket;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.ServerSocketFactory;
+
+/** 
+ * Factory interface to construct components based on the JSSE version
+ * in use.
+ *
+ * @author Bill Barker
+ */
+
+interface JSSEFactory {
+
+    /**
+     * Returns the ServerSocketFactory to use.
+     */
+    public ServerSocketFactory getSocketFactory();
+
+    /**
+     * returns the SSLSupport attached to this socket.
+     */
+    public SSLSupport getSSLSupport(Socket socket);
+
+};
diff --git a/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java
new file mode 100644
index 0000000..56b6526
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java
@@ -0,0 +1,78 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net.jsse;
+
+import java.net.Socket;
+
+import org.apache.tomcat.util.compat.JdkCompat;
+import org.apache.tomcat.util.net.SSLImplementation;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.ServerSocketFactory;
+
+/* JSSEImplementation:
+
+   Concrete implementation class for JSSE
+
+   @author EKR
+*/
+        
+public class JSSEImplementation extends SSLImplementation
+{
+    static final String JSSE14Factory = 
+        "org.apache.tomcat.util.net.jsse.JSSE14Factory";
+    static final String JSSE13Factory = 
+        "org.apache.tomcat.util.net.jsse.JSSE13Support";
+    static final String SSLSocketClass = "javax.net.ssl.SSLSocket";
+
+    static org.apache.commons.logging.Log logger = 
+        org.apache.commons.logging.LogFactory.getLog(JSSEImplementation.class);
+
+    private JSSEFactory factory;
+
+    public JSSEImplementation() throws ClassNotFoundException {
+        // Check to see if JSSE is floating around somewhere
+        Class.forName(SSLSocketClass);
+	if( JdkCompat.isJava14() ) {
+	    try {
+		Class factcl = Class.forName(JSSE14Factory);
+		factory = (JSSEFactory)factcl.newInstance();
+	    } catch(Exception ex) {
+		factory = new JSSE13Factory();
+		if(logger.isDebugEnabled()) {
+		    logger.debug("Error getting factory: " + JSSE14Factory, ex);
+		}
+	    }
+	} else {
+	    factory = new JSSE13Factory();
+	}
+    }
+
+
+    public String getImplementationName(){
+      return "JSSE";
+    }
+      
+    public ServerSocketFactory getServerSocketFactory()  {
+        ServerSocketFactory ssf = factory.getSocketFactory();
+        return ssf;
+    } 
+
+    public SSLSupport getSSLSupport(Socket s) {
+        SSLSupport ssls = factory.getSSLSupport(s);
+        return ssls;
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java
new file mode 100644
index 0000000..5034f59
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java
@@ -0,0 +1,143 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net.jsse;
+
+import java.net.Socket;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.X509KeyManager;
+
+/**
+ * X509KeyManager which allows selection of a specific keypair and certificate
+ * chain (identified by their keystore alias name) to be used by the server to
+ * authenticate itself to SSL clients.
+ *
+ * @author Jan Luehe
+ */
+public final class JSSEKeyManager implements X509KeyManager {
+
+    private X509KeyManager delegate;
+    private String serverKeyAlias;
+
+    /**
+     * Constructor.
+     *
+     * @param mgr The X509KeyManager used as a delegate
+     * @param serverKeyAlias The alias name of the server's keypair and
+     * supporting certificate chain
+     */
+    public JSSEKeyManager(X509KeyManager mgr, String serverKeyAlias) {
+        this.delegate = mgr;
+        this.serverKeyAlias = serverKeyAlias;
+    }
+
+    /**
+     * Choose an alias to authenticate the client side of a secure socket,
+     * given the public key type and the list of certificate issuer authorities
+     * recognized by the peer (if any).
+     *
+     * @param keyType The key algorithm type name(s), ordered with the
+     * most-preferred key type first
+     * @param issuers The list of acceptable CA issuer subject names, or null
+     * if it does not matter which issuers are used
+     * @param socket The socket to be used for this connection. This parameter
+     * can be null, in which case this method will return the most generic
+     * alias to use
+     *
+     * @return The alias name for the desired key, or null if there are no
+     * matches
+     */
+    public String chooseClientAlias(String[] keyType, Principal[] issuers,
+                                    Socket socket) {
+        return delegate.chooseClientAlias(keyType, issuers, socket);
+    }
+
+    /**
+     * Returns this key manager's server key alias that was provided in the
+     * constructor.
+     *
+     * @param keyType The key algorithm type name (ignored)
+     * @param issuers The list of acceptable CA issuer subject names, or null
+     * if it does not matter which issuers are used (ignored)
+     * @param socket The socket to be used for this connection. This parameter
+     * can be null, in which case this method will return the most generic
+     * alias to use (ignored)
+     *
+     * @return Alias name for the desired key
+     */
+    public String chooseServerAlias(String keyType, Principal[] issuers,
+                                    Socket socket) {
+        return serverKeyAlias;
+    }
+
+    /**
+     * Returns the certificate chain associated with the given alias.
+     *
+     * @param alias The alias name
+     *
+     * @return Certificate chain (ordered with the user's certificate first
+     * and the root certificate authority last), or null if the alias can't be
+     * found
+     */
+    public X509Certificate[] getCertificateChain(String alias) {
+        return delegate.getCertificateChain(alias); 
+    }
+
+    /**
+     * Get the matching aliases for authenticating the client side of a secure
+     * socket, given the public key type and the list of certificate issuer
+     * authorities recognized by the peer (if any).
+     *
+     * @param keyType The key algorithm type name
+     * @param issuers The list of acceptable CA issuer subject names, or null
+     * if it does not matter which issuers are used
+     *
+     * @return Array of the matching alias names, or null if there were no
+     * matches
+     */
+    public String[] getClientAliases(String keyType, Principal[] issuers) {
+        return delegate.getClientAliases(keyType, issuers);
+    }
+
+    /**
+     * Get the matching aliases for authenticating the server side of a secure
+     * socket, given the public key type and the list of certificate issuer
+     * authorities recognized by the peer (if any).
+     *
+     * @param keyType The key algorithm type name
+     * @param issuers The list of acceptable CA issuer subject names, or null
+     * if it does not matter which issuers are used
+     *
+     * @return Array of the matching alias names, or null if there were no
+     * matches
+     */
+    public String[] getServerAliases(String keyType, Principal[] issuers) {
+        return delegate.getServerAliases(keyType, issuers);
+    }
+
+    /**
+     * Returns the key associated with the given alias.
+     *
+     * @param alias The alias name
+     *
+     * @return The requested key, or null if the alias can't be found
+     */
+    public PrivateKey getPrivateKey(String alias) {
+        return delegate.getPrivateKey(alias);
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java
new file mode 100644
index 0000000..3d9d05c
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java
@@ -0,0 +1,369 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net.jsse;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.security.KeyStore;
+import java.util.Vector;
+
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+
+/*
+  1. Make the JSSE's jars available, either as an installed
+     extension (copy them into jre/lib/ext) or by adding
+     them to the Tomcat classpath.
+  2. keytool -genkey -alias tomcat -keyalg RSA
+     Use "changeit" as password ( this is the default we use )
+ */
+
+/**
+ * SSL server socket factory. It _requires_ a valid RSA key and
+ * JSSE. 
+ *
+ * @author Harish Prabandham
+ * @author Costin Manolache
+ * @author Stefan Freyr Stefansson
+ * @author EKR -- renamed to JSSESocketFactory
+ */
+public abstract class JSSESocketFactory
+    extends org.apache.tomcat.util.net.ServerSocketFactory
+{
+    // defaults
+    static String defaultProtocol = "TLS";
+    static String defaultAlgorithm = "SunX509";
+    static boolean defaultClientAuth = false;
+    static String defaultKeystoreType = "JKS";
+    private static final String defaultKeystoreFile
+        = System.getProperty("user.home") + "/.keystore";
+    private static final String defaultKeyPass = "changeit";
+    static org.apache.commons.logging.Log log =
+        org.apache.commons.logging.LogFactory.getLog(JSSESocketFactory.class);
+
+    protected boolean initialized;
+    protected String clientAuth = "false";
+    protected SSLServerSocketFactory sslProxy = null;
+    protected String[] enabledCiphers;
+   
+
+    public JSSESocketFactory () {
+    }
+
+    public ServerSocket createSocket (int port)
+        throws IOException
+    {
+        if (!initialized) init();
+        ServerSocket socket = sslProxy.createServerSocket(port);
+        initServerSocket(socket);
+        return socket;
+    }
+    
+    public ServerSocket createSocket (int port, int backlog)
+        throws IOException
+    {
+        if (!initialized) init();
+        ServerSocket socket = sslProxy.createServerSocket(port, backlog);
+        initServerSocket(socket);
+        return socket;
+    }
+    
+    public ServerSocket createSocket (int port, int backlog,
+                                      InetAddress ifAddress)
+        throws IOException
+    {   
+        if (!initialized) init();
+        ServerSocket socket = sslProxy.createServerSocket(port, backlog,
+                                                          ifAddress);
+        initServerSocket(socket);
+        return socket;
+    }
+    
+    public Socket acceptSocket(ServerSocket socket)
+        throws IOException
+    {
+        SSLSocket asock = null;
+        try {
+             asock = (SSLSocket)socket.accept();
+             configureClientAuth(asock);
+        } catch (SSLException e){
+          throw new SocketException("SSL handshake error" + e.toString());
+        }
+        return asock;
+    }
+
+    public void handshake(Socket sock) throws IOException {
+        ((SSLSocket)sock).startHandshake();
+    }
+
+    /*
+     * Determines the SSL cipher suites to be enabled.
+     *
+     * @param requestedCiphers Comma-separated list of requested ciphers
+     * @param supportedCiphers Array of supported ciphers
+     *
+     * @return Array of SSL cipher suites to be enabled, or null if none of the
+     * requested ciphers are supported
+     */
+    protected String[] getEnabledCiphers(String requestedCiphers,
+                                         String[] supportedCiphers) {
+
+        String[] enabledCiphers = null;
+
+        if (requestedCiphers != null) {
+            Vector vec = null;
+            String cipher = requestedCiphers;
+            int index = requestedCiphers.indexOf(',');
+            if (index != -1) {
+                int fromIndex = 0;
+                while (index != -1) {
+                    cipher = requestedCiphers.substring(fromIndex, index).trim();
+                    if (cipher.length() > 0) {
+                        /*
+                         * Check to see if the requested cipher is among the
+                         * supported ciphers, i.e., may be enabled
+                         */
+                        for (int i=0; supportedCiphers != null
+                                     && i<supportedCiphers.length; i++) {
+                            if (supportedCiphers[i].equals(cipher)) {
+                                if (vec == null) {
+                                    vec = new Vector();
+                                }
+                                vec.addElement(cipher);
+                                break;
+                            }
+                        }
+                    }
+                    fromIndex = index+1;
+                    index = requestedCiphers.indexOf(',', fromIndex);
+                } // while
+                cipher = requestedCiphers.substring(fromIndex);
+            }
+
+            if (cipher != null) {
+                cipher = cipher.trim();
+                if (cipher.length() > 0) {
+                    /*
+                     * Check to see if the requested cipher is among the
+                     * supported ciphers, i.e., may be enabled
+                     */
+                    for (int i=0; supportedCiphers != null
+                                 && i<supportedCiphers.length; i++) {
+                        if (supportedCiphers[i].equals(cipher)) {
+                            if (vec == null) {
+                                vec = new Vector();
+                            }
+                            vec.addElement(cipher);
+                            break;
+                        }
+                    }
+                }
+            }           
+
+            if (vec != null) {
+                enabledCiphers = new String[vec.size()];
+                vec.copyInto(enabledCiphers);
+            }
+        } else {
+            enabledCiphers = supportedCiphers;
+        }
+
+        return enabledCiphers;
+    }
+     
+    /*
+     * Gets the SSL server's keystore password.
+     */
+    protected String getKeystorePassword() {
+        String keyPass = (String)attributes.get("keypass");
+        if (keyPass == null) {
+            keyPass = defaultKeyPass;
+        }
+        String keystorePass = (String)attributes.get("keystorePass");
+        if (keystorePass == null) {
+            keystorePass = keyPass;
+        }
+        return keystorePass;
+    }
+
+    /*
+     * Gets the SSL server's keystore.
+     */
+    protected KeyStore getKeystore(String type, String pass)
+            throws IOException {
+
+        String keystoreFile = (String)attributes.get("keystore");
+        if (keystoreFile == null)
+            keystoreFile = defaultKeystoreFile;
+
+        return getStore(type, keystoreFile, pass);
+    }
+
+    /*
+     * Gets the SSL server's truststore.
+     */
+    protected KeyStore getTrustStore(String keystoreType) throws IOException {
+        KeyStore trustStore = null;
+
+        String trustStoreFile = (String)attributes.get("truststoreFile");
+        if(trustStoreFile == null) {
+            trustStoreFile = System.getProperty("javax.net.ssl.trustStore");
+        }
+        if(log.isDebugEnabled()) {
+            log.debug("Truststore = " + trustStoreFile);
+        }
+        String trustStorePassword = (String)attributes.get("truststorePass");
+        if( trustStorePassword == null) {
+            trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
+        }
+        if( trustStorePassword == null ) {
+            trustStorePassword = getKeystorePassword();
+        }
+        if(log.isDebugEnabled()) {
+            log.debug("TrustPass = " + trustStorePassword);
+        }
+        String truststoreType = (String)attributes.get("truststoreType");
+        if(truststoreType == null) {
+            truststoreType = keystoreType;
+        }
+        if(log.isDebugEnabled()) {
+            log.debug("trustType = " + truststoreType);
+        }
+        if (trustStoreFile != null && trustStorePassword != null){
+            trustStore = getStore(truststoreType, trustStoreFile,
+                                  trustStorePassword);
+        }
+
+        return trustStore;
+    }
+
+    /*
+     * Gets the key- or truststore with the specified type, path, and password.
+     */
+    private KeyStore getStore(String type, String path, String pass)
+            throws IOException {
+
+        KeyStore ks = null;
+        InputStream istream = null;
+        try {
+            ks = KeyStore.getInstance(type);
+            File keyStoreFile = new File(path);
+            if (!keyStoreFile.isAbsolute()) {
+                keyStoreFile = new File(System.getProperty("catalina.base"),
+                                        path);
+            }
+            istream = new FileInputStream(keyStoreFile);
+
+            ks.load(istream, pass.toCharArray());
+            istream.close();
+            istream = null;
+        } catch (FileNotFoundException fnfe) {
+            throw fnfe;
+        } catch (IOException ioe) {
+            throw ioe;      
+        } catch(Exception ex) {
+            ex.printStackTrace();
+            throw new IOException("Exception trying to load keystore " +
+                                  path + ": " + ex.getMessage() );
+        } finally {
+            if (istream != null) {
+                try {
+                    istream.close();
+                } catch (IOException ioe) {
+                    // Do nothing
+                }
+            }
+        }
+
+        return ks;
+    }
+
+    /**
+     * Reads the keystore and initializes the SSL socket factory.
+     *
+     * Place holder method to initialize the KeyStore, etc.
+     */
+    abstract void init() throws IOException ;
+
+    /*
+     * Determines the SSL protocol variants to be enabled.
+     *
+     * @param socket The socket to get supported list from.
+     * @param requestedProtocols Comma-separated list of requested SSL
+     * protocol variants
+     *
+     * @return Array of SSL protocol variants to be enabled, or null if none of
+     * the requested protocol variants are supported
+     */
+    abstract protected String[] getEnabledProtocols(SSLServerSocket socket,
+                                                    String requestedProtocols);
+
+    /**
+     * Set the SSL protocol variants to be enabled.
+     * @param socket the SSLServerSocket.
+     * @param protocols the protocols to use.
+     */
+    abstract protected void setEnabledProtocols(SSLServerSocket socket, 
+                                            String [] protocols);
+
+    /**
+     * Configure Client authentication for this version of JSSE.  The
+     * JSSE included in Java 1.4 supports the 'want' value.  Prior
+     * versions of JSSE will treat 'want' as 'false'.
+     * @param socket the SSLServerSocket
+     */
+    abstract protected void configureClientAuth(SSLServerSocket socket);
+
+    /**
+     * Configure Client authentication for this version of JSSE.  The
+     * JSSE included in Java 1.4 supports the 'want' value.  Prior
+     * versions of JSSE will treat 'want' as 'false'.
+     * @param socket the SSLSocket
+     */
+    abstract protected void configureClientAuth(SSLSocket socket);
+    
+    /**
+     * Configures the given SSL server socket with the requested cipher suites,
+     * protocol versions, and need for client authentication
+     */
+    private void initServerSocket(ServerSocket ssocket) {
+
+        SSLServerSocket socket = (SSLServerSocket) ssocket;
+
+        if (enabledCiphers != null) {
+            socket.setEnabledCipherSuites(enabledCiphers);
+        }
+
+        String requestedProtocols = (String) attributes.get("protocols");
+        setEnabledProtocols(socket, getEnabledProtocols(socket, 
+                                                         requestedProtocols));
+
+        // we don't know if client auth is needed -
+        // after parsing the request we may re-handshake
+        configureClientAuth(socket);
+    }
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSESupport.java b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
new file mode 100644
index 0000000..a6af743
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
@@ -0,0 +1,178 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net.jsse;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.cert.CertificateFactory;
+
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.security.cert.X509Certificate;
+
+import org.apache.tomcat.util.net.SSLSupport;
+
+/* JSSESupport
+
+   Concrete implementation class for JSSE
+   Support classes.
+
+   This will only work with JDK 1.2 and up since it
+   depends on JDK 1.2's certificate support
+
+   @author EKR
+   @author Craig R. McClanahan
+   Parts cribbed from JSSECertCompat       
+   Parts cribbed from CertificatesValve
+*/
+
+class JSSESupport implements SSLSupport {
+    private static org.apache.commons.logging.Log log =
+	org.apache.commons.logging.LogFactory.getLog(JSSESupport.class);
+
+    protected SSLSocket ssl;
+
+
+    JSSESupport(SSLSocket sock){
+        ssl=sock;
+    }
+
+    public String getCipherSuite() throws IOException {
+        // Look up the current SSLSession
+        SSLSession session = ssl.getSession();
+        if (session == null)
+            return null;
+        return session.getCipherSuite();
+    }
+
+    public Object[] getPeerCertificateChain() 
+        throws IOException {
+        return getPeerCertificateChain(false);
+    }
+
+    protected java.security.cert.X509Certificate [] 
+	getX509Certificates(SSLSession session) throws IOException {
+        X509Certificate jsseCerts[] = null;
+    try{
+	    jsseCerts = session.getPeerCertificateChain();
+    } catch (Throwable ex){
+       // Get rid of the warning in the logs when no Client-Cert is
+       // available
+    }
+
+	if(jsseCerts == null)
+	    jsseCerts = new X509Certificate[0];
+	java.security.cert.X509Certificate [] x509Certs =
+	    new java.security.cert.X509Certificate[jsseCerts.length];
+	for (int i = 0; i < x509Certs.length; i++) {
+	    try {
+		byte buffer[] = jsseCerts[i].getEncoded();
+		CertificateFactory cf =
+		    CertificateFactory.getInstance("X.509");
+		ByteArrayInputStream stream =
+		    new ByteArrayInputStream(buffer);
+		x509Certs[i] = (java.security.cert.X509Certificate)
+		    cf.generateCertificate(stream);
+		if(log.isTraceEnabled())
+		    log.trace("Cert #" + i + " = " + x509Certs[i]);
+	    } catch(Exception ex) {
+		log.info("Error translating " + jsseCerts[i], ex);
+		return null;
+	    }
+	}
+	
+	if ( x509Certs.length < 1 )
+	    return null;
+	return x509Certs;
+    }
+    public Object[] getPeerCertificateChain(boolean force)
+        throws IOException {
+        // Look up the current SSLSession
+	SSLSession session = ssl.getSession();
+        if (session == null)
+            return null;
+
+        // Convert JSSE's certificate format to the ones we need
+	X509Certificate [] jsseCerts = null;
+	try {
+	    jsseCerts = session.getPeerCertificateChain();
+	} catch(Exception bex) {
+	    // ignore.
+	}
+	if (jsseCerts == null)
+	    jsseCerts = new X509Certificate[0];
+	if(jsseCerts.length <= 0 && force) {
+	    session.invalidate();
+	    handShake();
+	    session = ssl.getSession();
+	}
+        return getX509Certificates(session);
+    }
+
+    protected void handShake() throws IOException {
+        ssl.setNeedClientAuth(true);
+        ssl.startHandshake();
+    }
+    /**
+     * Copied from <code>org.apache.catalina.valves.CertificateValve</code>
+     */
+    public Integer getKeySize() 
+        throws IOException {
+        // Look up the current SSLSession
+        SSLSession session = ssl.getSession();
+        SSLSupport.CipherData c_aux[]=ciphers;
+        if (session == null)
+            return null;
+        Integer keySize = (Integer) session.getValue(KEY_SIZE_KEY);
+        if (keySize == null) {
+            int size = 0;
+            String cipherSuite = session.getCipherSuite();
+            for (int i = 0; i < c_aux.length; i++) {
+                if (cipherSuite.indexOf(c_aux[i].phrase) >= 0) {
+                    size = c_aux[i].keySize;
+                    break;
+                }
+            }
+            keySize = new Integer(size);
+            session.putValue(KEY_SIZE_KEY, keySize);
+        }
+        return keySize;
+    }
+
+    public String getSessionId()
+        throws IOException {
+        // Look up the current SSLSession
+        SSLSession session = ssl.getSession();
+        if (session == null)
+            return null;
+        // Expose ssl_session (getId)
+        byte [] ssl_session = session.getId();
+        if ( ssl_session == null) 
+            return null;
+        StringBuffer buf=new StringBuffer("");
+        for(int x=0; x<ssl_session.length; x++) {
+            String digit=Integer.toHexString((int)ssl_session[x]);
+            if (digit.length()<2) buf.append('0');
+            if (digit.length()>2) digit=digit.substring(digit.length()-2);
+            buf.append(digit);
+        }
+        return buf.toString();
+    }
+
+
+}
+
diff --git a/connectors/util/java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties b/connectors/util/java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties
new file mode 100644
index 0000000..fbd5c4e
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties
@@ -0,0 +1 @@
+jsse.alias_no_key_entry=Alias name {0} does not identify a key entry
diff --git a/connectors/util/java/org/apache/tomcat/util/net/jsse/res/LocalStrings_es.properties b/connectors/util/java/org/apache/tomcat/util/net/jsse/res/LocalStrings_es.properties
new file mode 100644
index 0000000..75e5e0d
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/res/LocalStrings_es.properties
@@ -0,0 +1 @@
+jsse.alias_no_key_entry=El nombre de Alias {0} no identifica una entrada de clave
diff --git a/connectors/util/java/org/apache/tomcat/util/net/jsse/res/LocalStrings_fr.properties b/connectors/util/java/org/apache/tomcat/util/net/jsse/res/LocalStrings_fr.properties
new file mode 100644
index 0000000..04aa2df
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/res/LocalStrings_fr.properties
@@ -0,0 +1 @@
+jsse.alias_no_key_entry=Le nom alias {0} n''identifie pas une entrée de clef
diff --git a/connectors/util/java/org/apache/tomcat/util/net/jsse/res/LocalStrings_ja.properties b/connectors/util/java/org/apache/tomcat/util/net/jsse/res/LocalStrings_ja.properties
new file mode 100644
index 0000000..cfb080b
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/jsse/res/LocalStrings_ja.properties
@@ -0,0 +1,2 @@
+jsse.alias_no_key_entry=\u5225\u540d {0} \u306f\u30ad\u30fc\u30a8\u30f3\u30c8\u30ea\u3092\u767a\u898b\u3067\u304d\u307e\u305b\u3093
+
diff --git a/connectors/util/java/org/apache/tomcat/util/net/puretls/PureTLSImplementation.java b/connectors/util/java/org/apache/tomcat/util/net/puretls/PureTLSImplementation.java
new file mode 100644
index 0000000..2d88a2a
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/puretls/PureTLSImplementation.java
@@ -0,0 +1,57 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net.puretls;
+
+import java.net.Socket;
+
+import org.apache.tomcat.util.net.SSLImplementation;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.ServerSocketFactory;
+
+import COM.claymoresystems.ptls.SSLSocket;
+
+/* PureTLSImplementation:
+
+   Concrete implementation class for PureTLS
+
+   @author EKR
+*/
+
+public class PureTLSImplementation extends SSLImplementation
+{
+    public PureTLSImplementation() throws ClassNotFoundException {
+	// Check to see if PureTLS is floating around somewhere
+	Class.forName("COM.claymoresystems.ptls.SSLContext");
+    }
+
+    public String getImplementationName(){
+      return "PureTLS";
+    }
+      
+    public ServerSocketFactory getServerSocketFactory()
+    {
+	return new PureTLSSocketFactory();
+    } 
+
+    public SSLSupport getSSLSupport(Socket s)
+    {
+	return new PureTLSSupport((SSLSocket)s);
+    }
+
+
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/puretls/PureTLSSocket.java b/connectors/util/java/org/apache/tomcat/util/net/puretls/PureTLSSocket.java
new file mode 100644
index 0000000..bb5d6b9
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/puretls/PureTLSSocket.java
@@ -0,0 +1,42 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net.puretls;
+
+import java.io.IOException;
+
+/*
+ * PureTLSSocket.java
+ *
+ * Wraps COM.claymoresystems.ptls.SSLSocket
+ *
+ * This class translates PureTLS's interfaces into those
+ * expected by Tomcat
+ *
+ * @author Eric Rescorla
+ *
+ */
+
+public class PureTLSSocket extends COM.claymoresystems.ptls.SSLSocket
+{
+    // The only constructor we need here is the no-arg
+    // constructor since this class is only used with
+    // implAccept
+    public PureTLSSocket() throws IOException {
+	super();
+    }
+}
+ 
diff --git a/connectors/util/java/org/apache/tomcat/util/net/puretls/PureTLSSocketFactory.java b/connectors/util/java/org/apache/tomcat/util/net/puretls/PureTLSSocketFactory.java
new file mode 100644
index 0000000..ca50348
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/puretls/PureTLSSocketFactory.java
@@ -0,0 +1,229 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net.puretls;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.util.Vector;
+
+import COM.claymoresystems.ptls.SSLContext;
+import COM.claymoresystems.ptls.SSLException;
+import COM.claymoresystems.ptls.SSLServerSocket;
+import COM.claymoresystems.ptls.SSLSocket;
+import COM.claymoresystems.sslg.SSLPolicyInt;
+
+/**
+ * SSL server socket factory--wraps PureTLS
+ *
+ * @author Eric Rescorla
+ *
+ * some sections of this file cribbed from SSLSocketFactory
+ * (the JSSE socket factory)
+ *
+ */
+ 
+public class PureTLSSocketFactory
+    extends org.apache.tomcat.util.net.ServerSocketFactory
+{
+    static org.apache.commons.logging.Log logger =
+	org.apache.commons.logging.LogFactory.getLog(PureTLSSocketFactory.class);
+    static String defaultProtocol = "TLS";
+    static boolean defaultClientAuth = false;
+    static String defaultKeyStoreFile = "server.pem";
+    static String defaultKeyPass = "password";    
+    static String defaultRootFile = "root.pem";
+    static String defaultRandomFile = "random.pem";
+    
+    private COM.claymoresystems.ptls.SSLContext context=null;
+    
+    public PureTLSSocketFactory() {
+    }
+
+    public ServerSocket createSocket(int port)
+	throws IOException
+    {
+	init();
+	return new SSLServerSocket(context,port);
+    }
+
+    public ServerSocket createSocket(int port, int backlog)
+	throws IOException
+    {
+	init();
+	ServerSocket tmp;
+	
+	try {
+	    tmp=new SSLServerSocket(context,port,backlog);
+	}
+	catch (IOException e){
+	    throw e;
+	}
+	return tmp;
+    }
+
+    public ServerSocket createSocket(int port, int backlog,
+				     InetAddress ifAddress)
+	throws IOException
+    {
+	init();
+	return new SSLServerSocket(context,port,backlog,ifAddress);
+    }
+
+    private void init()
+	throws IOException
+    {
+	if(context!=null)
+	    return;
+	
+	boolean clientAuth=defaultClientAuth;
+
+	try {
+	    String keyStoreFile=(String)attributes.get("keystore");
+	    if(keyStoreFile==null) keyStoreFile=defaultKeyStoreFile;
+	    
+	    String keyPass=(String)attributes.get("keypass");
+	    if(keyPass==null) keyPass=defaultKeyPass;
+	    
+	    String rootFile=(String)attributes.get("rootfile");
+	    if(rootFile==null) rootFile=defaultRootFile;
+
+	    String randomFile=(String)attributes.get("randomfile");
+	    if(randomFile==null) randomFile=defaultRandomFile;
+	    
+	    String protocol=(String)attributes.get("protocol");
+	    if(protocol==null) protocol=defaultProtocol;
+
+	    String clientAuthStr=(String)attributes.get("clientauth");
+	    if(clientAuthStr != null){
+		if(clientAuthStr.equals("true")){
+		    clientAuth=true;
+		} else if(clientAuthStr.equals("false")) {
+		    clientAuth=false;
+		} else {
+		    throw new IOException("Invalid value '" +
+					  clientAuthStr + 
+					  "' for 'clientauth' parameter:");
+		}
+	    }
+
+            SSLContext tmpContext=new SSLContext();
+            try {
+                tmpContext.loadRootCertificates(rootFile);
+            } catch(IOException iex) {
+                if(logger.isDebugEnabled())
+                    logger.debug("Error loading Client Root Store: " + 
+                                 rootFile,iex);
+            }
+            tmpContext.loadEAYKeyFile(keyStoreFile,keyPass);
+	    tmpContext.useRandomnessFile(randomFile,keyPass);
+	    
+	    SSLPolicyInt policy=new SSLPolicyInt();
+	    policy.requireClientAuth(clientAuth);
+            policy.handshakeOnConnect(false);
+            policy.waitOnClose(false);
+            short [] enabledCiphers = getEnabledCiphers(policy.getCipherSuites());
+            if( enabledCiphers != null ) {
+                policy.setCipherSuites(enabledCiphers);
+            }
+            tmpContext.setPolicy(policy);
+	    context=tmpContext;
+	} catch (Exception e){
+	    logger.info("Error initializing SocketFactory",e);
+	    throw new IOException(e.getMessage());
+	}
+    }
+
+    /*
+     * Determines the SSL cipher suites to be enabled.
+     *
+     * @return Array of SSL cipher suites to be enabled, or null if the
+     * cipherSuites property was not specified (meaning that all supported
+     * cipher suites are to be enabled)
+     */
+    private short [] getEnabledCiphers(short [] supportedCiphers) {
+
+        short [] enabledCiphers = null;
+
+        String attrValue = (String)attributes.get("ciphers");
+        if (attrValue != null) {
+            Vector vec = null;
+            int fromIndex = 0;
+            int index = attrValue.indexOf(',', fromIndex);
+            while (index != -1) {
+                String cipher = attrValue.substring(fromIndex, index).trim();
+                int cipherValue = SSLPolicyInt.getCipherSuiteNumber(cipher);                
+                /*
+                 * Check to see if the requested cipher is among the supported
+                 * ciphers, i.e., may be enabled
+                 */
+                if( cipherValue >= 0) {
+                    for (int i=0; supportedCiphers != null
+                             && i<supportedCiphers.length; i++) {
+
+                        if (cipherValue == supportedCiphers[i]) {
+                            if (vec == null) {
+                                vec = new Vector();
+                            }
+                            vec.addElement(new Integer(cipherValue));
+                            break;
+                        }
+                    }
+                }
+                fromIndex = index+1;
+                index = attrValue.indexOf(',', fromIndex);
+            }
+
+            if (vec != null) {
+                int nCipher = vec.size();
+                enabledCiphers = new short[nCipher];
+                for(int i=0; i < nCipher; i++) {
+                    Integer value = (Integer)vec.elementAt(i);
+                    enabledCiphers[i] = value.shortValue();
+                }
+            }
+        }
+
+        return enabledCiphers;
+
+    }
+
+    public Socket acceptSocket(ServerSocket socket)
+	throws IOException
+    {
+	try {
+	    Socket sock=socket.accept();
+	    return sock;
+	} catch (SSLException e){
+            logger.debug("SSL handshake error",e);
+            throw new SocketException("SSL handshake error" + e.toString());
+	}
+    }
+
+    public void handshake(Socket sock)
+	 throws IOException
+    {
+	((SSLSocket)sock).handshake();
+    }
+}
+
+    
+    
+
+
diff --git a/connectors/util/java/org/apache/tomcat/util/net/puretls/PureTLSSupport.java b/connectors/util/java/org/apache/tomcat/util/net/puretls/PureTLSSupport.java
new file mode 100644
index 0000000..3c29e7b
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/puretls/PureTLSSupport.java
@@ -0,0 +1,143 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.net.puretls;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Vector;
+
+import org.apache.tomcat.util.buf.HexUtils;
+import org.apache.tomcat.util.net.SSLSupport;
+
+import COM.claymoresystems.cert.X509Cert;
+import COM.claymoresystems.ptls.SSLSocket;
+import COM.claymoresystems.sslg.SSLPolicyInt;
+
+
+/* PureTLSSupport
+
+   Concrete implementation class for PureTLS
+   Support classes.
+
+   This will only work with JDK 1.2 and up since it
+   depends on JDK 1.2's certificate support
+
+   @author EKR
+*/
+
+class PureTLSSupport implements SSLSupport {
+    static org.apache.commons.logging.Log logger =
+	org.apache.commons.logging.LogFactory.getLog(PureTLSSupport.class);
+
+    private COM.claymoresystems.ptls.SSLSocket ssl;
+
+    PureTLSSupport(SSLSocket sock){
+        ssl=sock;
+    }
+
+    public String getCipherSuite() throws IOException {
+        int cs=ssl.getCipherSuite();
+        return SSLPolicyInt.getCipherSuiteName(cs);
+    }
+
+    public Object[] getPeerCertificateChain()
+        throws IOException {
+	return getPeerCertificateChain(false);
+    }
+
+    public Object[] getPeerCertificateChain(boolean force)
+        throws IOException {
+        Vector v=ssl.getCertificateChain();
+
+	if(v == null && force) {
+	    SSLPolicyInt policy=new SSLPolicyInt();
+	    policy.requireClientAuth(true);
+	    policy.handshakeOnConnect(false);
+	    policy.waitOnClose(false);
+	    ssl.renegotiate(policy);
+	    v = ssl.getCertificateChain();
+	}
+
+        if(v==null)
+            return null;
+        
+        java.security.cert.X509Certificate[] chain=
+            new java.security.cert.X509Certificate[v.size()];
+
+        try {
+          for(int i=1;i<=v.size();i++){
+            // PureTLS provides cert chains with the peer
+            // cert last but the Servlet 2.3 spec (S 4.7) requires
+            // the opposite order so we reverse the chain as we go
+            byte buffer[]=((X509Cert)v.elementAt(
+                 v.size()-i)).getDER();
+            
+            CertificateFactory cf =
+              CertificateFactory.getInstance("X.509");
+            ByteArrayInputStream stream =
+              new ByteArrayInputStream(buffer);
+
+            X509Certificate xCert = (X509Certificate)cf.generateCertificate(stream);
+            chain[i-1]= xCert;
+            if(logger.isTraceEnabled()) {
+		logger.trace("Cert # " + i + " = " + xCert);
+	    }
+          }
+        } catch (java.security.cert.CertificateException e) {
+	    logger.info("JDK's broken cert handling can't parse this certificate (which PureTLS likes)",e);
+            throw new IOException("JDK's broken cert handling can't parse this certificate (which PureTLS likes)");
+        }
+        return chain;
+    }
+
+    /**
+     * Lookup the symmetric key size.
+     */
+    public Integer getKeySize() 
+        throws IOException {
+
+        int cs=ssl.getCipherSuite();
+        String cipherSuite = SSLPolicyInt.getCipherSuiteName(cs);
+        int size = 0;
+        for (int i = 0; i < ciphers.length; i++) {
+            if (cipherSuite.indexOf(ciphers[i].phrase) >= 0) {
+                size = ciphers[i].keySize;
+                break;
+            }
+        }
+        Integer keySize = new Integer(size);
+        return keySize;
+    }
+
+    public String getSessionId()
+        throws IOException {
+        byte [] ssl_session = ssl.getSessionID();
+        if(ssl_session == null)
+            return null;
+        return HexUtils.convert(ssl_session);
+    }
+
+}
+
+
+
+
+
+
+
diff --git a/connectors/util/java/org/apache/tomcat/util/net/res/LocalStrings.properties b/connectors/util/java/org/apache/tomcat/util/net/res/LocalStrings.properties
new file mode 100644
index 0000000..bc2f6b6
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/res/LocalStrings.properties
@@ -0,0 +1,6 @@
+# net resources
+endpoint.err.fatal=Endpoint {0} shutdown due to exception: {1}
+endpoint.err.nonfatal=Endpoint {0} ignored exception: {1}
+endpoint.warn.reinit=Reinitializing ServerSocket
+endpoint.warn.restart=Restarting endpoint
+endpoint.warn.security=Endpoint {0} security exception: {1}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/res/LocalStrings_es.properties b/connectors/util/java/org/apache/tomcat/util/net/res/LocalStrings_es.properties
new file mode 100644
index 0000000..188fe99
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/res/LocalStrings_es.properties
@@ -0,0 +1,6 @@
+# net resources
+endpoint.err.fatal=Punto Final (Endpoint) {0} parado debido a excepción: {1}
+endpoint.err.nonfatal=El Punto Final (Endpoint) {0} ignoró excepción: {1}
+endpoint.warn.reinit=Reinicializando ServerSocket
+endpoint.warn.restart=Rearrancando punto final (endpoint)
+endpoint.warn.security=Punto Final (Endpoint) {0} con excepción de seguridad: {1}
diff --git a/connectors/util/java/org/apache/tomcat/util/net/res/LocalStrings_fr.properties b/connectors/util/java/org/apache/tomcat/util/net/res/LocalStrings_fr.properties
new file mode 100644
index 0000000..35d0b27
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/res/LocalStrings_fr.properties
@@ -0,0 +1,6 @@
+# net resources
+endpoint.err.fatal=Le point de contact {0} a été éteint suite à l''exception{1}
+endpoint.err.nonfatal=Le point de contact {0} a ignoré l''exception: {1}
+endpoint.warn.reinit=Réinitialisation du ServerSocket
+endpoint.warn.restart=Redémarrage du point de contact
+
diff --git a/connectors/util/java/org/apache/tomcat/util/net/res/LocalStrings_ja.properties b/connectors/util/java/org/apache/tomcat/util/net/res/LocalStrings_ja.properties
new file mode 100644
index 0000000..15611e3
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/net/res/LocalStrings_ja.properties
@@ -0,0 +1,6 @@
+# net resources
+endpoint.err.fatal=\u4f8b\u5916\u306e\u305f\u3081\u306b\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8 {0} \u306f\u30b7\u30e3\u30c3\u30c8\u30c0\u30a6\u30f3\u3057\u307e\u3059: {1}
+endpoint.err.nonfatal=\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8 {0} \u306f\u4f8b\u5916\u3092\u7121\u8996\u3057\u307e\u3057\u305f: {1}
+endpoint.warn.reinit=ServerSocket\u3092\u518d\u521d\u671f\u5316\u3057\u307e\u3059
+endpoint.warn.restart=\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u3092\u518d\u8d77\u52d5\u3057\u307e\u3059
+endpoint.warn.security=\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8 {0} \u306e\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u4f8b\u5916\u3067\u3059: {1}
diff --git a/connectors/util/java/org/apache/tomcat/util/res/StringManager.java b/connectors/util/java/org/apache/tomcat/util/res/StringManager.java
new file mode 100644
index 0000000..237e3da
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/res/StringManager.java
@@ -0,0 +1,285 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.res;
+
+import java.text.MessageFormat;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * An internationalization / localization helper class which reduces
+ * the bother of handling ResourceBundles and takes care of the
+ * common cases of message formating which otherwise require the
+ * creation of Object arrays and such.
+ *
+ * <p>The StringManager operates on a package basis. One StringManager
+ * per package can be created and accessed via the getManager method
+ * call.
+ *
+ * <p>The StringManager will look for a ResourceBundle named by
+ * the package name given plus the suffix of "LocalStrings". In
+ * practice, this means that the localized information will be contained
+ * in a LocalStrings.properties file located in the package
+ * directory of the classpath.
+ *
+ * <p>Please see the documentation for java.util.ResourceBundle for
+ * more information.
+ *
+ * @version $Revision$ $Date$
+ *
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Mel Martinez [mmartinez@g1440.com]
+ * @see java.util.ResourceBundle
+ */
+
+public class StringManager {
+
+    /**
+     * The ResourceBundle for this StringManager.
+     */
+
+    private ResourceBundle bundle;
+    private Locale locale;
+
+    /**
+     * Creates a new StringManager for a given package. This is a
+     * private method and all access to it is arbitrated by the
+     * static getManager method call so that only one StringManager
+     * per package will be created.
+     *
+     * @param packageName Name of package to create StringManager for.
+     */
+
+    private StringManager(String packageName) {
+	this( packageName, Locale.getDefault() );
+    }
+
+    private StringManager(String packageName, Locale loc) {
+        String bundleName = packageName + ".LocalStrings";
+        bundle = ResourceBundle.getBundle(bundleName, loc);
+        // Get the actual locale, which may be different from the requested one
+        locale = bundle.getLocale();
+    }
+
+    private StringManager(ResourceBundle bundle )
+    {
+	this.bundle=bundle;
+        locale = bundle.getLocale();
+    }
+
+    /**
+        Get a string from the underlying resource bundle or return
+        null if the String is not found.
+     
+        @param key to desired resource String
+        @return resource String matching <i>key</i> from underlying
+                bundle or null if not found.
+        @throws IllegalArgumentException if <i>key</i> is null.        
+     */
+
+    public String getString(String key) {
+        if(key == null){
+            String msg = "key may not have a null value";
+
+            throw new IllegalArgumentException(msg);
+        }
+
+        String str = null;
+
+        try{
+	        str = bundle.getString(key);
+        }catch(MissingResourceException mre){
+            //bad: shouldn't mask an exception the following way:
+            //   str = "[cannot find message associated with key '" + key + "' due to " + mre + "]";
+	        //     because it hides the fact that the String was missing
+	        //     from the calling code.
+	        //good: could just throw the exception (or wrap it in another)
+	        //      but that would probably cause much havoc on existing
+	        //      code.
+	        //better: consistent with container pattern to
+	        //      simply return null.  Calling code can then do
+	        //      a null check.
+	        str = null;
+        }
+
+        return str;
+    }
+
+    /**
+     * Get a string from the underlying resource bundle and format
+     * it with the given set of arguments.
+     *
+     * @param key
+     * @param args
+     */
+
+    public String getString(String key, Object[] args) {
+        String iString = null;
+        String value = getString(key);
+
+        // this check for the runtime exception is some pre 1.1.6
+        // VM's don't do an automatic toString() on the passed in
+        // objects and barf out
+
+        try {
+            // ensure the arguments are not null so pre 1.2 VM's don't barf
+            if(args==null){
+                args = new Object[1];
+            }
+            
+            Object[] nonNullArgs = args;
+            for (int i=0; i<args.length; i++) {
+                if (args[i] == null) {
+                    if (nonNullArgs==args){
+                        nonNullArgs=(Object[])args.clone();
+                    }
+                    nonNullArgs[i] = "null";
+                }
+            }
+            if( value==null ) value=key;
+	    MessageFormat mf = new MessageFormat(value);
+            mf.setLocale(locale);
+            iString = mf.format(nonNullArgs, new StringBuffer(), null).toString();
+        } catch (IllegalArgumentException iae) {
+            StringBuffer buf = new StringBuffer();
+            buf.append(value);
+            for (int i = 0; i < args.length; i++) {
+                buf.append(" arg[" + i + "]=" + args[i]);
+            }
+            iString = buf.toString();
+        }
+        return iString;
+    }
+
+    /**
+     * Get a string from the underlying resource bundle and format it
+     * with the given object argument. This argument can of course be
+     * a String object.
+     *
+     * @param key
+     * @param arg
+     */
+
+    public String getString(String key, Object arg) {
+	Object[] args = new Object[] {arg};
+	return getString(key, args);
+    }
+
+    /**
+     * Get a string from the underlying resource bundle and format it
+     * with the given object arguments. These arguments can of course
+     * be String objects.
+     *
+     * @param key
+     * @param arg1
+     * @param arg2
+     */
+
+    public String getString(String key, Object arg1, Object arg2) {
+	Object[] args = new Object[] {arg1, arg2};
+	return getString(key, args);
+    }
+    
+    /**
+     * Get a string from the underlying resource bundle and format it
+     * with the given object arguments. These arguments can of course
+     * be String objects.
+     *
+     * @param key
+     * @param arg1
+     * @param arg2
+     * @param arg3
+     */
+
+    public String getString(String key, Object arg1, Object arg2,
+			    Object arg3) {
+	Object[] args = new Object[] {arg1, arg2, arg3};
+	return getString(key, args);
+    }
+
+    /**
+     * Get a string from the underlying resource bundle and format it
+     * with the given object arguments. These arguments can of course
+     * be String objects.
+     *
+     * @param key
+     * @param arg1
+     * @param arg2
+     * @param arg3
+     * @param arg4
+     */
+
+    public String getString(String key, Object arg1, Object arg2,
+			    Object arg3, Object arg4) {
+	Object[] args = new Object[] {arg1, arg2, arg3, arg4};
+	return getString(key, args);
+    }
+    // --------------------------------------------------------------
+    // STATIC SUPPORT METHODS
+    // --------------------------------------------------------------
+
+    private static Hashtable managers = new Hashtable();
+
+    /**
+     * Get the StringManager for a particular package. If a manager for
+     * a package already exists, it will be reused, else a new
+     * StringManager will be created and returned.
+     *
+     * @param packageName The package name
+     */
+    public synchronized static StringManager getManager(String packageName) {
+      StringManager mgr = (StringManager)managers.get(packageName);
+      if (mgr == null) {
+          mgr = new StringManager(packageName);
+          managers.put(packageName, mgr);
+      }
+      return mgr;
+    }
+
+    /**
+     * Get the StringManager for a particular package. If a manager for
+     * a package already exists, it will be reused, else a new
+     * StringManager will be created and returned.
+     *
+     * @param bundle The resource bundle
+     */
+    public synchronized static StringManager getManager(ResourceBundle bundle) {
+      return new StringManager( bundle );
+    }
+
+    /**
+     * Get the StringManager for a particular package and Locale. If a manager for
+     * a package already exists, it will be reused, else a new
+     * StringManager will be created for that Locale and returned.
+     *
+     * @param packageName The package name
+     * @param loc The locale
+     */
+
+   public synchronized static StringManager getManager(String packageName,Locale loc) {
+      StringManager mgr = (StringManager)managers.get(packageName+"_"+loc.toString());
+      if (mgr == null) {
+          mgr = new StringManager(packageName,loc);
+          managers.put(packageName+"_"+loc.toString(), mgr);
+      }
+      return mgr;
+    }
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/threads/Expirer.java b/connectors/util/java/org/apache/tomcat/util/threads/Expirer.java
new file mode 100644
index 0000000..081c627
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/threads/Expirer.java
@@ -0,0 +1,154 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.threads;
+
+import org.apache.tomcat.util.buf.TimeStamp;
+
+/**
+ * Expire unused objects. 
+ * 
+ */
+public final class Expirer  implements ThreadPoolRunnable
+{
+    // We can use Event/Listener, but this is probably simpler
+    // and more efficient
+    public static interface ExpireCallback {
+	public void expired( TimeStamp o );
+    }
+    
+    private int checkInterval = 60;
+    private Reaper reaper;
+    ExpireCallback expireCallback;
+
+    public Expirer() {
+    }
+
+    // ------------------------------------------------------------- Properties
+    public int getCheckInterval() {
+	return checkInterval;
+    }
+
+    public void setCheckInterval(int checkInterval) {
+	this.checkInterval = checkInterval;
+    }
+
+    public void setExpireCallback( ExpireCallback cb ) {
+	expireCallback=cb;
+    }
+    
+    // -------------------- Managed objects --------------------
+    static final int INITIAL_SIZE=8;
+    TimeStamp managedObjs[]=new TimeStamp[INITIAL_SIZE];
+    TimeStamp checkedObjs[]=new TimeStamp[INITIAL_SIZE];
+    int managedLen=managedObjs.length;
+    int managedCount=0;
+    
+    public void addManagedObject( TimeStamp ts ) {
+	synchronized( managedObjs ) {
+	    if( managedCount >= managedLen ) {
+		// What happens if expire is on the way ? Nothing,
+		// expire will do it's job on the old array ( GC magic )
+		// and the expired object will be marked as such
+		// Same thing would happen ( and did ) with Hashtable
+		TimeStamp newA[]=new TimeStamp[ 2 * managedLen ];
+		System.arraycopy( managedObjs, 0, newA, 0, managedLen);
+		managedObjs = newA;
+		managedLen = 2 * managedLen;
+	    }
+	    managedObjs[managedCount]=ts;
+	    managedCount++;
+	}
+    }
+
+    public void removeManagedObject( TimeStamp ts ) {
+	for( int i=0; i< managedCount; i++ ) {
+	    if( ts == managedObjs[i] ) {
+		synchronized( managedObjs ) {
+		    managedObjs[ i ] = managedObjs[managedCount-1];
+		    managedCount--;
+		}
+		return;
+	    }
+	}
+    }
+    
+    // --------------------------------------------------------- Public Methods
+
+    public void start() {
+	// Start the background reaper thread
+	if( reaper==null) {
+	    reaper=new Reaper("Expirer");
+	    reaper.addCallback( this, checkInterval * 1000 );
+	}
+	
+	reaper.startReaper();
+    }
+
+    public void stop() {
+	reaper.stopReaper();
+    }
+
+
+    // -------------------------------------------------------- Private Methods
+
+    // ThreadPoolRunnable impl
+
+    public Object[] getInitData() {
+	return null;
+    }
+
+    public void runIt( Object td[] ) {
+	long timeNow = System.currentTimeMillis();
+	if( dL > 2 ) debug( "Checking " + timeNow );
+	int checkedCount;
+	synchronized( managedObjs ) {
+	    checkedCount=managedCount;
+	    if(checkedObjs.length < checkedCount)
+		checkedObjs = new TimeStamp[managedLen];
+	    System.arraycopy( managedObjs, 0, checkedObjs, 0, checkedCount);
+	}
+	for( int i=0; i< checkedCount; i++ ) {
+	    TimeStamp ts=checkedObjs[i];
+	    checkedObjs[i] = null;
+	    
+	    if (ts==null || !ts.isValid())
+		continue;
+	    
+	    long maxInactiveInterval = ts.getMaxInactiveInterval();
+	    if( dL > 3 ) debug( "TS: " + maxInactiveInterval + " " +
+				ts.getLastAccessedTime());
+	    if (maxInactiveInterval < 0)
+		continue;
+	    
+	    long timeIdle = timeNow - ts.getThisAccessedTime();
+	    
+	    if (timeIdle >= maxInactiveInterval) {
+		if( expireCallback != null ) {
+		    if( dL > 0 )
+			debug( ts + " " + timeIdle + " " +
+			       maxInactiveInterval );
+		    expireCallback.expired( ts );
+		}
+	    }
+	}
+    }
+
+    private static final int dL=0;
+    private void debug( String s ) {
+	System.out.println("Expirer: " + s );
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/threads/Reaper.java b/connectors/util/java/org/apache/tomcat/util/threads/Reaper.java
new file mode 100644
index 0000000..e21a652
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/threads/Reaper.java
@@ -0,0 +1,119 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.threads;
+
+
+/**
+ * The reaper is a background thread with which ticks every minute
+ * and calls registered objects to allow reaping of old session
+ * data.
+ * 
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author Costin Manolache
+ */
+public class Reaper extends Thread {
+    private boolean daemon = false;
+
+    public Reaper() {
+        if (daemon)
+            this.setDaemon(true);
+        this.setName("TomcatReaper");
+    }
+
+    public Reaper(String name) {
+        if (daemon)
+            this.setDaemon(true);
+        this.setName(name);
+    }
+
+    private long interval = 1000 * 60; //ms
+
+    // XXX TODO Allow per/callback interval, find next, etc
+    // Right now the "interval" is used for all callbacks
+    // and it represent a sleep between runs.
+
+    ThreadPoolRunnable cbacks[] = new ThreadPoolRunnable[30]; // XXX max
+    Object tdata[][] = new Object[30][]; // XXX max
+    int count = 0;
+
+    /** Adding and removing callbacks is synchronized
+     */
+    Object lock = new Object();
+    static boolean running = true;
+
+    // XXX Should be called 'interval' not defaultInterval
+
+    public void setDefaultInterval(long t) {
+        interval = t;
+    }
+
+    public long getDefaultIntervale() {
+        return interval;
+    }
+
+    public int addCallback(ThreadPoolRunnable c, int interval) {
+        synchronized (lock) {
+            cbacks[count] = c;
+            count++;
+            return count - 1;
+        }
+    }
+
+    public void removeCallback(int idx) {
+        synchronized (lock) {
+            count--;
+            cbacks[idx] = cbacks[count];
+            cbacks[count] = null;
+        }
+    }
+
+    public void startReaper() {
+        running = true;
+        this.start();
+    }
+
+    public synchronized void stopReaper() {
+        running = false;
+        System.out.println("Stop reaper ");
+        this.interrupt(); // notify() doesn't stop sleep
+    }
+
+    public void run() {
+        while (running) {
+            if (!running)
+                break;
+            try {
+                Thread.sleep(interval);
+            } catch (InterruptedException ie) {
+                // sometimes will happen
+            }
+
+            if (!running)
+                break;
+            for (int i = 0; i < count; i++) {
+                ThreadPoolRunnable callB = cbacks[i];
+                // it may be null if a callback is removed.
+                //  I think the code is correct
+                if (callB != null) {
+                    callB.runIt(tdata[i]);
+                }
+                if (!running)
+                    break;
+            }
+        }
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/threads/ThreadPool.java b/connectors/util/java/org/apache/tomcat/util/threads/ThreadPool.java
new file mode 100644
index 0000000..fde47df
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/threads/ThreadPool.java
@@ -0,0 +1,838 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.threads;
+
+import java.util.*;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * A thread pool that is trying to copy the apache process management.
+ *
+ * Should we remove this in favor of Doug Lea's thread package?
+ *
+ * @author Gal Shachor
+ * @author Yoav Shapira <yoavs@apache.org>
+ */
+public class ThreadPool  {
+
+    private static Log log = LogFactory.getLog(ThreadPool.class);
+
+    private static StringManager sm =
+        StringManager.getManager("org.apache.tomcat.util.threads.res");
+
+    private static boolean logfull=true;
+
+    /*
+     * Default values ...
+     */
+    public static final int MAX_THREADS = 200;
+    public static final int MAX_THREADS_MIN = 10;
+    public static final int MAX_SPARE_THREADS = 50;
+    public static final int MIN_SPARE_THREADS = 4;
+    public static final int WORK_WAIT_TIMEOUT = 60*1000;
+
+    /*
+     * Where the threads are held.
+     */
+    protected ControlRunnable[] pool = null;
+
+    /*
+     * A monitor thread that monitors the pool for idel threads.
+     */
+    protected MonitorRunnable monitor;
+
+
+    /*
+     * Max number of threads that you can open in the pool.
+     */
+    protected int maxThreads;
+
+    /*
+     * Min number of idel threads that you can leave in the pool.
+     */
+    protected int minSpareThreads;
+
+    /*
+     * Max number of idel threads that you can leave in the pool.
+     */
+    protected int maxSpareThreads;
+
+    /*
+     * Number of threads in the pool.
+     */
+    protected int currentThreadCount;
+
+    /*
+     * Number of busy threads in the pool.
+     */
+    protected int currentThreadsBusy;
+
+    /*
+     * Flag that the pool should terminate all the threads and stop.
+     */
+    protected boolean stopThePool;
+
+    /* Flag to control if the main thread is 'daemon' */
+    protected boolean isDaemon=true;
+
+    /** The threads that are part of the pool.
+     * Key is Thread, value is the ControlRunnable
+     */
+    protected Hashtable threads=new Hashtable();
+
+    protected Vector listeners=new Vector();
+
+    /** Name of the threadpool
+     */
+    protected String name = "TP";
+
+    /**
+     * Sequence.
+     */
+    protected int sequence = 1;
+
+    /**
+     * Thread priority.
+     */
+    protected int threadPriority = Thread.NORM_PRIORITY;
+
+
+    /**
+     * Constructor.
+     */    
+    public ThreadPool() {
+        maxThreads = MAX_THREADS;
+        maxSpareThreads = MAX_SPARE_THREADS;
+        minSpareThreads = MIN_SPARE_THREADS;
+        currentThreadCount = 0;
+        currentThreadsBusy = 0;
+        stopThePool = false;
+    }
+
+
+    /** Create a ThreadPool instance.
+     *
+     * @param jmx UNUSED 
+     * @return ThreadPool instance. If JMX support is requested, you need to
+     *   call register() in order to set a name.
+     */
+    public static ThreadPool createThreadPool(boolean jmx) {
+        return new ThreadPool();
+    }
+
+    public synchronized void start() {
+	stopThePool=false;
+        currentThreadCount  = 0;
+        currentThreadsBusy  = 0;
+
+        adjustLimits();
+
+        pool = new ControlRunnable[maxThreads];
+
+        openThreads(minSpareThreads);
+        if (maxSpareThreads < maxThreads) {
+            monitor = new MonitorRunnable(this);
+        }
+    }
+
+    public MonitorRunnable getMonitor() {
+        return monitor;
+    }
+  
+    /**
+     * Sets the thread priority for current
+     * and future threads in this pool.
+     *
+     * @param threadPriority The new priority
+     * @throws IllegalArgumentException If the specified
+     *  priority is less than Thread.MIN_PRIORITY or
+     *  more than Thread.MAX_PRIORITY 
+     */
+    public synchronized void setThreadPriority(int threadPriority) {
+        if(log.isDebugEnabled())
+            log.debug(getClass().getName() +
+                      ": setPriority(" + threadPriority + "): here.");
+
+      if (threadPriority < Thread.MIN_PRIORITY) {
+        throw new IllegalArgumentException("new priority < MIN_PRIORITY");
+      } else if (threadPriority > Thread.MAX_PRIORITY) {
+        throw new IllegalArgumentException("new priority > MIN_PRIORITY");
+      }
+
+      // Set for future threads
+      this.threadPriority = threadPriority;
+
+      Enumeration currentThreads = getThreads();
+      Thread t = null;
+      while(currentThreads.hasMoreElements()) {
+        t = (Thread) currentThreads.nextElement();
+        t.setPriority(threadPriority);
+      } 
+    }
+
+    /**
+     * Returns the priority level of current and
+     * future threads in this pool.
+     *
+     * @return The priority
+     */
+    public int getThreadPriority() {
+      return threadPriority;
+    }   
+     
+
+    public void setMaxThreads(int maxThreads) {
+        this.maxThreads = maxThreads;
+    }
+
+    public int getMaxThreads() {
+        return maxThreads;
+    }
+
+    public void setMinSpareThreads(int minSpareThreads) {
+        this.minSpareThreads = minSpareThreads;
+    }
+
+    public int getMinSpareThreads() {
+        return minSpareThreads;
+    }
+
+    public void setMaxSpareThreads(int maxSpareThreads) {
+        this.maxSpareThreads = maxSpareThreads;
+    }
+
+    public int getMaxSpareThreads() {
+        return maxSpareThreads;
+    }
+
+    public int getCurrentThreadCount() {
+        return currentThreadCount;
+    }
+
+    public int getCurrentThreadsBusy() {
+        return currentThreadsBusy;
+    }
+
+    public boolean isDaemon() {
+        return isDaemon;
+    }
+
+    public static int getDebug() {
+        return 0;
+    }
+
+    /** The default is true - the created threads will be
+     *  in daemon mode. If set to false, the control thread
+     *  will not be daemon - and will keep the process alive.
+     */
+    public void setDaemon( boolean b ) {
+        isDaemon=b;
+    }
+    
+    public boolean getDaemon() {
+        return isDaemon;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getSequence() {
+        return sequence++;
+    }
+
+    public void addThread( Thread t, ControlRunnable cr ) {
+        threads.put( t, cr );
+        for( int i=0; i<listeners.size(); i++ ) {
+            ThreadPoolListener tpl=(ThreadPoolListener)listeners.elementAt(i);
+            tpl.threadStart(this, t);
+        }
+    }
+
+    public void removeThread( Thread t ) {
+        threads.remove(t);
+        for( int i=0; i<listeners.size(); i++ ) {
+            ThreadPoolListener tpl=(ThreadPoolListener)listeners.elementAt(i);
+            tpl.threadEnd(this, t);
+        }
+    }
+
+    public void addThreadPoolListener( ThreadPoolListener tpl ) {
+        listeners.addElement( tpl );
+    }
+
+    public Enumeration getThreads(){
+        return threads.keys();
+    }
+
+    public void run(Runnable r) {
+        ControlRunnable c = findControlRunnable();
+        c.runIt(r);
+    }    
+    
+    //
+    // You may wonder what you see here ... basically I am trying
+    // to maintain a stack of threads. This way locality in time
+    // is kept and there is a better chance to find residues of the
+    // thread in memory next time it runs.
+    //
+
+    /**
+     * Executes a given Runnable on a thread in the pool, block if needed.
+     */
+    public void runIt(ThreadPoolRunnable r) {
+        if(null == r) {
+            throw new NullPointerException();
+        }
+
+        ControlRunnable c = findControlRunnable();
+        c.runIt(r);
+    }
+
+    private ControlRunnable findControlRunnable() {
+        ControlRunnable c=null;
+
+        if ( stopThePool ) {
+            throw new IllegalStateException();
+        }
+
+        // Obtain a free thread from the pool.
+        synchronized(this) {
+
+            while (currentThreadsBusy == currentThreadCount) {
+                 // All threads are busy
+                if (currentThreadCount < maxThreads) {
+                    // Not all threads were open,
+                    // Open new threads up to the max number of idel threads
+                    int toOpen = currentThreadCount + minSpareThreads;
+                    openThreads(toOpen);
+                } else {
+                    logFull(log, currentThreadCount, maxThreads);
+                    // Wait for a thread to become idel.
+                    try {
+                        this.wait();
+                    }
+                    // was just catch Throwable -- but no other
+                    // exceptions can be thrown by wait, right?
+                    // So we catch and ignore this one, since
+                    // it'll never actually happen, since nowhere
+                    // do we say pool.interrupt().
+                    catch(InterruptedException e) {
+                        log.error("Unexpected exception", e);
+                    }
+		    if( log.isDebugEnabled() ) {
+			log.debug("Finished waiting: CTC="+currentThreadCount +
+				  ", CTB=" + currentThreadsBusy);
+                    }
+                    // Pool was stopped. Get away of the pool.
+                    if( stopThePool) {
+                        break;
+                    }
+                }
+            }
+            // Pool was stopped. Get away of the pool.
+            if(0 == currentThreadCount || stopThePool) {
+                throw new IllegalStateException();
+            }
+                    
+            // If we are here it means that there is a free thread. Take it.
+            int pos = currentThreadCount - currentThreadsBusy - 1;
+            c = pool[pos];
+            pool[pos] = null;
+            currentThreadsBusy++;
+
+        }
+        return c;
+    }
+
+    private static void logFull(Log loghelper, int currentThreadCount,
+                                int maxThreads) {
+	if( logfull ) {
+            log.error(sm.getString("threadpool.busy",
+                                   new Integer(currentThreadCount),
+                                   new Integer(maxThreads)));
+            logfull=false;
+        } else if( log.isDebugEnabled() ) {
+            log.debug("All threads are busy " + currentThreadCount + " " +
+                      maxThreads );
+        }
+    }
+
+    /**
+     * Stop the thread pool
+     */
+    public synchronized void shutdown() {
+        if(!stopThePool) {
+            stopThePool = true;
+            if (monitor != null) {
+                monitor.terminate();
+                monitor = null;
+            }
+            for(int i = 0; i < currentThreadCount - currentThreadsBusy; i++) {
+                try {
+                    pool[i].terminate();
+                } catch(Throwable t) {
+                    /*
+		     * Do nothing... The show must go on, we are shutting
+		     * down the pool and nothing should stop that.
+		     */
+		    log.error("Ignored exception while shutting down thread pool", t);
+                }
+            }
+            currentThreadsBusy = currentThreadCount = 0;
+            pool = null;
+            notifyAll();
+        }
+    }
+
+    /**
+     * Called by the monitor thread to harvest idle threads.
+     */
+    protected synchronized void checkSpareControllers() {
+
+        if(stopThePool) {
+            return;
+        }
+
+        if((currentThreadCount - currentThreadsBusy) > maxSpareThreads) {
+            int toFree = currentThreadCount -
+                         currentThreadsBusy -
+                         maxSpareThreads;
+
+            for(int i = 0 ; i < toFree ; i++) {
+                ControlRunnable c = pool[currentThreadCount - currentThreadsBusy - 1];
+                c.terminate();
+                pool[currentThreadCount - currentThreadsBusy - 1] = null;
+                currentThreadCount --;
+            }
+
+        }
+
+    }
+
+    /**
+     * Returns the thread to the pool.
+     * Called by threads as they are becoming idel.
+     */
+    protected synchronized void returnController(ControlRunnable c) {
+
+        if(0 == currentThreadCount || stopThePool) {
+            c.terminate();
+            return;
+        }
+
+        // atomic
+        currentThreadsBusy--;
+
+        pool[currentThreadCount - currentThreadsBusy - 1] = c;
+        notify();
+    }
+
+    /**
+     * Inform the pool that the specific thread finish.
+     *
+     * Called by the ControlRunnable.run() when the runnable
+     * throws an exception.
+     */
+    protected synchronized void notifyThreadEnd(ControlRunnable c) {
+        currentThreadsBusy--;
+        currentThreadCount --;
+        notify();
+    }
+
+
+    /*
+     * Checks for problematic configuration and fix it.
+     * The fix provides reasonable settings for a single CPU
+     * with medium load.
+     */
+    protected void adjustLimits() {
+        if(maxThreads <= 0) {
+            maxThreads = MAX_THREADS;
+        } else if (maxThreads < MAX_THREADS_MIN) {
+            log.warn(sm.getString("threadpool.max_threads_too_low",
+                                  new Integer(maxThreads),
+                                  new Integer(MAX_THREADS_MIN)));
+            maxThreads = MAX_THREADS_MIN;
+        }
+
+        if(maxSpareThreads >= maxThreads) {
+            maxSpareThreads = maxThreads;
+        }
+
+        if(maxSpareThreads <= 0) {
+            if(1 == maxThreads) {
+                maxSpareThreads = 1;
+            } else {
+                maxSpareThreads = maxThreads/2;
+            }
+        }
+
+        if(minSpareThreads >  maxSpareThreads) {
+            minSpareThreads =  maxSpareThreads;
+        }
+
+        if(minSpareThreads <= 0) {
+            if(1 == maxSpareThreads) {
+                minSpareThreads = 1;
+            } else {
+                minSpareThreads = maxSpareThreads/2;
+            }
+        }
+    }
+
+    /** Create missing threads.
+     *
+     * @param toOpen Total number of threads we'll have open
+     */
+    protected void openThreads(int toOpen) {
+
+        if(toOpen > maxThreads) {
+            toOpen = maxThreads;
+        }
+
+        for(int i = currentThreadCount ; i < toOpen ; i++) {
+            pool[i - currentThreadsBusy] = new ControlRunnable(this);
+        }
+
+        currentThreadCount = toOpen;
+    }
+
+    /** @deprecated */
+    void log( String s ) {
+	log.info(s);
+	//loghelper.flush();
+    }
+    
+    /** 
+     * Periodically execute an action - cleanup in this case
+     */
+    public static class MonitorRunnable implements Runnable {
+        ThreadPool p;
+        Thread     t;
+        int interval=WORK_WAIT_TIMEOUT;
+        boolean    shouldTerminate;
+
+        MonitorRunnable(ThreadPool p) {
+            this.p=p;
+            this.start();
+        }
+
+        public void start() {
+            shouldTerminate = false;
+            t = new Thread(this);
+            t.setDaemon(p.getDaemon() );
+	    t.setName(p.getName() + "-Monitor");
+            t.start();
+        }
+
+        public void setInterval(int i ) {
+            this.interval=i;
+        }
+
+        public void run() {
+            while(true) {
+                try {
+
+                    // Sleep for a while.
+                    synchronized(this) {
+                        this.wait(WORK_WAIT_TIMEOUT);
+                    }
+
+                    // Check if should terminate.
+                    // termination happens when the pool is shutting down.
+                    if(shouldTerminate) {
+                        break;
+                    }
+
+                    // Harvest idle threads.
+                    p.checkSpareControllers();
+
+                } catch(Throwable t) {
+		    ThreadPool.log.error("Unexpected exception", t);
+                }
+            }
+        }
+
+        public void stop() {
+            this.terminate();
+        }
+
+	/** Stop the monitor
+	 */
+        public synchronized void terminate() {
+            shouldTerminate = true;
+            this.notify();
+        }
+    }
+
+    /**
+     * A Thread object that executes various actions ( ThreadPoolRunnable )
+     *  under control of ThreadPool
+     */
+    public static class ControlRunnable implements Runnable {
+        /**
+	 * ThreadPool where this thread will be returned
+	 */
+        private ThreadPool p;
+
+	/**
+	 * The thread that executes the actions
+	 */
+        private ThreadWithAttributes     t;
+
+	/**
+	 * The method that is executed in this thread
+	 */
+        
+        private ThreadPoolRunnable   toRun;
+        private Runnable toRunRunnable;
+
+	/**
+	 * Stop this thread
+	 */
+	private boolean    shouldTerminate;
+
+	/**
+	 * Activate the execution of the action
+	 */
+        private boolean    shouldRun;
+
+	/**
+	 * Per thread data - can be used only if all actions are
+	 *  of the same type.
+	 *  A better mechanism is possible ( that would allow association of
+	 *  thread data with action type ), but right now it's enough.
+	 */
+	private boolean noThData;
+
+	/**
+	 * Start a new thread, with no method in it
+	 */
+        ControlRunnable(ThreadPool p) {
+            toRun = null;
+            shouldTerminate = false;
+            shouldRun = false;
+            this.p = p;
+            t = new ThreadWithAttributes(p, this);
+            t.setDaemon(true);
+            t.setName(p.getName() + "-Processor" + p.getSequence());
+            t.setPriority(p.getThreadPriority());
+            p.addThread( t, this );
+	    noThData=true;
+            t.start();
+        }
+
+        public void run() {
+            boolean _shouldRun = false;
+            boolean _shouldTerminate = false;
+            ThreadPoolRunnable _toRun = null;
+            try {
+                while (true) {
+                    try {
+                        /* Wait for work. */
+                        synchronized (this) {
+                            while (!shouldRun && !shouldTerminate) {
+                                this.wait();
+                            }
+                            _shouldRun = shouldRun;
+                            _shouldTerminate = shouldTerminate;
+                            _toRun = toRun;
+                        }
+
+                        if (_shouldTerminate) {
+                            if (ThreadPool.log.isDebugEnabled())
+                                ThreadPool.log.debug("Terminate");
+                            break;
+                        }
+
+                        /* Check if should execute a runnable.  */
+                        try {
+                            if (noThData) {
+                                if (_toRun != null) {
+                                    Object thData[] = _toRun.getInitData();
+                                    t.setThreadData(p, thData);
+                                    if (ThreadPool.log.isDebugEnabled())
+                                        ThreadPool.log.debug(
+                                            "Getting new thread data");
+                                }
+                                noThData = false;
+                            }
+
+                            if (_shouldRun) {
+                                if (_toRun != null) {
+                                    _toRun.runIt(t.getThreadData(p));
+                                } else if (toRunRunnable != null) {
+                                    toRunRunnable.run();
+                                } else {
+                                    if (ThreadPool.log.isDebugEnabled())
+                                    ThreadPool.log.debug("No toRun ???");
+                                }
+                            }
+                        } catch (Throwable t) {
+                            ThreadPool.log.error(sm.getString
+                                ("threadpool.thread_error", t, toRun.toString()));
+                            /*
+                             * The runnable throw an exception (can be even a ThreadDeath),
+                             * signalling that the thread die.
+                             *
+                            * The meaning is that we should release the thread from
+                            * the pool.
+                            */
+                            shouldTerminate = true;
+                            shouldRun = false;
+                            p.notifyThreadEnd(this);
+                        } finally {
+                            if (_shouldRun) {
+                                shouldRun = false;
+                                /*
+                                * Notify the pool that the thread is now idle.
+                                 */
+                                p.returnController(this);
+                            }
+                        }
+
+                        /*
+                        * Check if should terminate.
+                        * termination happens when the pool is shutting down.
+                        */
+                        if (_shouldTerminate) {
+                            break;
+                        }
+                    } catch (InterruptedException ie) { /* for the wait operation */
+                        // can never happen, since we don't call interrupt
+                        ThreadPool.log.error("Unexpected exception", ie);
+                    }
+                }
+            } finally {
+                p.removeThread(Thread.currentThread());
+            }
+        }
+        /** Run a task
+         *
+         * @param toRun
+         */
+        public synchronized void runIt(Runnable toRun) {
+	    this.toRunRunnable = toRun;
+	    // Do not re-init, the whole idea is to run init only once per
+	    // thread - the pool is supposed to run a single task, that is
+	    // initialized once.
+            // noThData = true;
+            shouldRun = true;
+            this.notify();
+        }
+
+        /** Run a task
+         *
+         * @param toRun
+         */
+        public synchronized void runIt(ThreadPoolRunnable toRun) {
+	    this.toRun = toRun;
+	    // Do not re-init, the whole idea is to run init only once per
+	    // thread - the pool is supposed to run a single task, that is
+	    // initialized once.
+            // noThData = true;
+            shouldRun = true;
+            this.notify();
+        }
+
+        public void stop() {
+            this.terminate();
+        }
+
+        public void kill() {
+            t.stop();
+        }
+
+        public synchronized void terminate() {
+            shouldTerminate = true;
+            this.notify();
+        }
+    }
+
+    /** 
+     * Debug display of the stage of each thread. The return is html style,
+     * for display in the console ( it can be easily parsed too ).
+     *
+     * @return The thread status display
+     */
+    public String threadStatusString() {
+        StringBuffer sb=new StringBuffer();
+        Iterator it=threads.keySet().iterator();
+        sb.append("<ul>");
+        while( it.hasNext()) {
+            sb.append("<li>");
+            ThreadWithAttributes twa=(ThreadWithAttributes)
+                    it.next();
+            sb.append(twa.getCurrentStage(this) ).append(" ");
+            sb.append( twa.getParam(this));
+            sb.append( "</li>\n");
+        }
+        sb.append("</ul>");
+        return sb.toString();
+    }
+
+    /** Return an array with the status of each thread. The status
+     * indicates the current request processing stage ( for tomcat ) or
+     * whatever the thread is doing ( if the application using TP provide
+     * this info )
+     *
+     * @return The status of all threads
+     */
+    public String[] getThreadStatus() {
+        String status[]=new String[ threads.size()];
+        Iterator it=threads.keySet().iterator();
+        for( int i=0; ( i<status.length && it.hasNext()); i++ ) {
+            ThreadWithAttributes twa=(ThreadWithAttributes)
+                    it.next();
+            status[i]=twa.getCurrentStage(this);
+        }
+        return status;
+    }
+
+    /** Return an array with the current "param" ( XXX better name ? )
+     * of each thread. This is typically the last request.
+     *
+     * @return The params of all threads
+     */
+    public String[] getThreadParam() {
+        String status[]=new String[ threads.size()];
+        Iterator it=threads.keySet().iterator();
+        for( int i=0; ( i<status.length && it.hasNext()); i++ ) {
+            ThreadWithAttributes twa=(ThreadWithAttributes)
+                    it.next();
+            Object o=twa.getParam(this);
+            status[i]=(o==null)? null : o.toString();
+        }
+        return status;
+    }
+    
+    /** Interface to allow applications to be notified when
+     * a threads are created and stopped.
+     */
+    public static interface ThreadPoolListener {
+        public void threadStart( ThreadPool tp, Thread t);
+
+        public void threadEnd( ThreadPool tp, Thread t);
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/threads/ThreadPoolRunnable.java b/connectors/util/java/org/apache/tomcat/util/threads/ThreadPoolRunnable.java
new file mode 100644
index 0000000..324e13b
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/threads/ThreadPoolRunnable.java
@@ -0,0 +1,38 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.threads;
+
+
+/** Implemented if you want to run a piece of code inside a thread pool.
+ */
+public interface ThreadPoolRunnable {
+    // XXX use notes or a hashtable-like
+    // Important: ThreadData in JDK1.2 is implemented as a Hashtable( Thread -> object ),
+    // expensive.
+    
+    /** Called when this object is first loaded in the thread pool.
+     *  Important: all workers in a pool must be of the same type,
+     *  otherwise the mechanism becomes more complex.
+     */
+    public Object[] getInitData();
+
+    /** This method will be executed in one of the pool's threads. The
+     *  thread will be returned to the pool.
+     */
+    public void runIt(Object thData[]);
+
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/threads/ThreadWithAttributes.java b/connectors/util/java/org/apache/tomcat/util/threads/ThreadWithAttributes.java
new file mode 100644
index 0000000..87e749d
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/threads/ThreadWithAttributes.java
@@ -0,0 +1,100 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.tomcat.util.threads;
+
+import java.util.Hashtable;
+
+/** Special thread that allows storing of attributes and notes.
+ *  A guard is used to prevent untrusted code from accessing the
+ *  attributes.
+ *
+ *  This avoids hash lookups and provide something very similar
+ * with ThreadLocal ( but compatible with JDK1.1 and faster on
+ * JDK < 1.4 ).
+ *
+ * The main use is to store 'state' for monitoring ( like "processing
+ * request 'GET /' ").
+ */
+public class ThreadWithAttributes extends Thread {
+    
+    private Object control;
+    public static int MAX_NOTES=16;
+    private Object notes[]=new Object[MAX_NOTES];
+    private Hashtable attributes=new Hashtable();
+    private String currentStage;
+    private Object param;
+    
+    private Object thData[];
+
+    public ThreadWithAttributes(Object control, Runnable r) {
+        super(r);
+        this.control=control;
+    }
+    
+    public final Object[] getThreadData(Object control ) {
+        return thData;
+    }
+    
+    public final void setThreadData(Object control, Object thData[] ) {
+        this.thData=thData;
+    }
+
+    /** Notes - for attributes that need fast access ( array )
+     * The application is responsible for id management
+     */
+    public final void setNote( Object control, int id, Object value ) {
+        if( this.control != control ) return;
+        notes[id]=value;
+    }
+
+    /** Information about the curent performed operation
+     */
+    public final String getCurrentStage(Object control) {
+        if( this.control != control ) return null;
+        return currentStage;
+    }
+
+    /** Information about the current request ( or the main object
+     * we are processing )
+     */
+    public final Object getParam(Object control) {
+        if( this.control != control ) return null;
+        return param;
+    }
+
+    public final void setCurrentStage(Object control, String currentStage) {
+        if( this.control != control ) return;
+        this.currentStage = currentStage;
+    }
+
+    public final void setParam( Object control, Object param ) {
+        if( this.control != control ) return;
+        this.param=param;
+    }
+
+    public final Object getNote(Object control, int id ) {
+        if( this.control != control ) return null;
+        return notes[id];
+    }
+
+    /** Generic attributes. You'll need a hashtable lookup -
+     * you can use notes for array access.
+     */
+    public final Hashtable getAttributes(Object control) {
+        return attributes;
+    }
+}
diff --git a/connectors/util/java/org/apache/tomcat/util/threads/res/LocalStrings.properties b/connectors/util/java/org/apache/tomcat/util/threads/res/LocalStrings.properties
new file mode 100644
index 0000000..ede6561
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/threads/res/LocalStrings.properties
@@ -0,0 +1,3 @@
+threadpool.busy=All threads ({0}) are currently busy, waiting. Increase maxThreads ({1}) or check the servlet status
+threadpool.max_threads_too_low=maxThreads setting ({0}) too low, set to {1}
+threadpool.thread_error=Caught exception ({0}) executing {1}, terminating thread
diff --git a/connectors/util/java/org/apache/tomcat/util/threads/res/LocalStrings_es.properties b/connectors/util/java/org/apache/tomcat/util/threads/res/LocalStrings_es.properties
new file mode 100644
index 0000000..4004714
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/threads/res/LocalStrings_es.properties
@@ -0,0 +1,3 @@
+threadpool.busy=Todos los hilos ({0}) están ahora ocupados, esperando. Incremente maxThreads ({1}) o revise el estado del servlet
+threadpool.max_threads_too_low=valor de maxThreads ({0}) demasiado bajo, puesto a {1}
+threadpool.thread_error=Cogida excepción ({0}) ejecutando {1}, terminando hilo
diff --git a/connectors/util/java/org/apache/tomcat/util/threads/res/LocalStrings_fr.properties b/connectors/util/java/org/apache/tomcat/util/threads/res/LocalStrings_fr.properties
new file mode 100644
index 0000000..7a144d9
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/threads/res/LocalStrings_fr.properties
@@ -0,0 +1,3 @@
+threadpool.busy=Tous les threads ({0}) sont actuellement occupés, attente. Augmentez maxThreads ({1}) ou vérifiez le servlet status
+threadpool.max_threads_too_low=le réglage maxThreads ({0}) est trop bas, mis à {1}
+threadpool.thread_error=Réception d''une exception ({0}) en exécutant {1}, arrêt du thread
diff --git a/connectors/util/java/org/apache/tomcat/util/threads/res/LocalStrings_ja.properties b/connectors/util/java/org/apache/tomcat/util/threads/res/LocalStrings_ja.properties
new file mode 100644
index 0000000..ffff30e
--- /dev/null
+++ b/connectors/util/java/org/apache/tomcat/util/threads/res/LocalStrings_ja.properties
@@ -0,0 +1,3 @@
+threadpool.busy=\u3059\u3079\u3066\u306e\u30b9\u30ec\u30c3\u30c9 ({0}) \u304c\u73fe\u5728\u7a3c\u50cd\u4e2d\u3067\u5f85\u6a5f\u3057\u3066\u3044\u307e\u3059\u3002maxThreads ({1}) \u3092\u5897\u3084\u3059\u304b\u3001\u305d\u306e\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u306e\u30b9\u30c6\u30fc\u30bf\u30b9\u3092\u30c1\u30a7\u30c3\u30af\u3057\u3066\u304f\u3060\u3055\u3044
+threadpool.max_threads_too_low=maxThreads\u306e\u8a2d\u5b9a ({0}) \u304c\u5c0f\u3055\u3059\u304e\u308b\u306e\u3067\u3001{1}\u306b\u8a2d\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044
+threadpool.thread_error={1} \u3092\u5b9f\u884c\u4e2d\u306b\u4f8b\u5916 ({0}) \u3092\u30ad\u30e3\u30c3\u30c1\u3057\u305f\u306e\u3067\u3001\u30b9\u30ec\u30c3\u30c9\u3092\u7d42\u4e86\u3057\u307e\u3059
diff --git a/connectors/util/java/tomcat-util.manifest b/connectors/util/java/tomcat-util.manifest
new file mode 100644
index 0000000..2f25925
--- /dev/null
+++ b/connectors/util/java/tomcat-util.manifest
@@ -0,0 +1,7 @@
+Manifest-version: 1.0
+Extension-Name: org.apache.tomcat.util
+Specification-Vendor: Apache Software Foundation
+Specification-Version: 3.0
+Implementation-Vendor-Id: org.apache
+Implementation-Vendor: Apache Software Foundation
+Implementation-Version: 5.1
diff --git a/connectors/webapp/.cvsignore b/connectors/webapp/.cvsignore
new file mode 100644
index 0000000..8e23728
--- /dev/null
+++ b/connectors/webapp/.cvsignore
@@ -0,0 +1,12 @@
+.DS_Store
+.FBCIndex
+.FBCLockFolder
+Makedefs
+Makefile
+apr
+build
+config.cache
+config.log
+config.status
+configure
+localmake.bat
diff --git a/connectors/webapp/CHANGES b/connectors/webapp/CHANGES
new file mode 100644
index 0000000..754a817
--- /dev/null
+++ b/connectors/webapp/CHANGES
@@ -0,0 +1,11 @@
+Changes with webapp-1.1:
+  *) Update to Apache licence 2.0
+  
+  *) Fix some the of the bugs.
+
+  *) Add SSL support for Apache-1.3. [JFrederic.Clere@fujitsu-siemens.com]
+
+  *) Fix configure.in to prevent building with a broken apxs [JFC]
+
+  *) Fix configure.in to prevent mixing native cc and gcc on Solaris [JFC]
+
diff --git a/connectors/webapp/INSTALL.txt b/connectors/webapp/INSTALL.txt
new file mode 100644
index 0000000..be3d4a4
--- /dev/null
+++ b/connectors/webapp/INSTALL.txt
@@ -0,0 +1,143 @@
+Installing mod_webapp and using it with Apache 1.3
+--------------------------------------------------
+
+Notice
+------
+
+NO, IT DOES NOT RUN WITH WINDOWS (your images don't appear and the
+whole thing hangs?) AND SINCE I DON'T USE NEITHER POSSESS A MICROSOFT
+WINDOWS BASED MACHINE, THERE ARE NO CURRENT PLANS ON MAKING IT WORK
+OVER THERE (from my side).
+
+Installing
+----------
+
+Once you have successfully built the mod_webapp DSO as described in the
+README.txt file coming with the sources, installing the module is pretty
+easy.
+
+First of all, copy the resulting mod_webapp.so file you will find in this
+directory (or in the apache-1.3 directory if you're building from sources)
+into your Apache 1.3 "libexec" directory (that directory where all DSO modules
+for Apache reside).
+
+If you're using Apache 1.3 for Windows, your modules directory will be called
+"modules" (there's a "libexec directory, but it contains other stuff). Please
+take care when loading the module. For Windows your "LoadModule" directive
+looks like:
+
+    LoadModule webapp_module modules/mod_webapp.so
+
+Another note for Windows: copy also the "libapr.dll" file with the module, or
+your Apache 1.3 web server will refuse to start reporting that the WebApp
+module cannot be loaded.
+
+Once you have done that, edit your "httpd.conf" configuration file and
+add a few lines to load that module at startup (Windows users, read above,
+you have to "replace" libexec with "modules" - I'm paranoid, sorry):
+
+    LoadModule webapp_module libexec/mod_webapp.so
+and
+    AddModule mod_webapp.c
+
+NOTE: It was reported that sometimes Apache under windows doesn't like the
+AddModule line in the configuration file. Please, if you can't start your
+Apache service, try commenting out that line in your httpd.conf file.
+
+Looking at the default "httpd.conf" file coming with Apache 1.3, I usually
+add the "LoadModule ..." line at the end of all pre-written and commented out
+"LoadModule" directives, and my "AddModule ..." directive at the end of all
+commented out "AddModule" directives. My "httpd.conf" file looks something
+like this:
+
+    [...]
+    #LoadModule unique_id_module  libexec/mod_unique_id.so
+    #LoadModule dav_module        libexec/libdav.so
+    #LoadModule ssl_module        libexec/libssl.so
+    LoadModule webapp_module      libexec/mod_webapp.so
+    [...]
+    ClearModuleList
+    [...]
+    #AddModule  mod_unique_id.c
+    #AddModule  mod_dav.c
+    #AddModule  mod_ssl.c
+    AddModule   mod_webapp.c
+    [...]
+
+Once you've edited your "httpd.conf" in such fashion, it's better to check
+if everything still works within the Apache core. You can test your newly
+constructed configuration by issuing:
+
+    apachectl configtest
+
+The apachectl script comes with your Apache 1.3 distribution. It usually
+lies in /usr/local/apache/bin, but depending on _YOUR_ apache distribution,
+that might change.
+
+Once you verified that "apachectl" reports "Syntax OK" (meaning that all
+modules have been successfully loaded and started), you can start adding your
+web application connections and context into the Apache configurations.
+Back to the "httpd.conf" file, you need to add something like:
+
+    WebAppConnection conn      warp  localhost:8008
+    WebAppDeploy     examples  conn  /examples
+
+In this example, I'm instructing the WebApp connector to connect to the
+servlet container waiting for requests on the current "localhost" host and
+bound to port 8008 (note, this port is the one you specified in your
+"server.xml" file for the "org.apache.catalina.connector.warp.WarpConnector"
+connector, not your HTTP one).
+
+A brief detailed description of the above-mentioned directives is:
+
+    WebAppConnection [connection name] [provider] [host:port]
+
+        [connection name]
+            A unique name for the connection to be created between Apache
+            and Tomcat.
+
+        [provider]
+            The name of the provider used to connect to the servlet container.
+            Currently only the "warp" provider is available.
+
+        [host:port]
+            The host name and port number to which the WARP connection must
+            attempt to connect. The port is the one you specified in your
+            "server.xml" file for the "...WarpConnector" connector, not your
+            HTTP one.
+
+    WebAppDeploy [application name] [connection name] [url path]
+
+        [application name]
+            The application name as present in your "webapps" directory in
+            Tomcat. For example, if you want to deploy a WAR-based web
+            application, your application name will look something like
+            "myApplication.war".
+
+        [connection name]
+            The name of a previously declared WebAppConnection directive.
+
+        [url path]
+            The URL path where this application will be deployed.
+
+The "WebAppDeploy" directive is scoped around the current host, meaning that
+if you insert it into a "<VirtualHost ...>" tag, your application will be
+deployed only for that particular virtual host. To deploy the same application
+on several virtual hosts, you will have to declare it once inside each
+"<VirtualHost ...>" tag. This, in accordance with the Servlet specification,
+will create a new instance of the web-application per virtual host.
+
+Another directive is available for the WebApp connector. The "WebAppInfo"
+directive will allow you to see the current status of all configured
+connections and deployed applications. To use it, simply add something like:
+
+    WebAppInfo /webapp-info
+
+You can then access the information page hitting your web server for the
+following URL:
+
+    http://server.name:port/webapp-info/
+
+Have fun...
+
+    Pier <pier@betaversion.org>
diff --git a/connectors/webapp/LICENSE.txt b/connectors/webapp/LICENSE.txt
new file mode 100644
index 0000000..5d83b9d
--- /dev/null
+++ b/connectors/webapp/LICENSE.txt
@@ -0,0 +1,204 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+CVS Revision $Id$
diff --git a/connectors/webapp/Makedefs.in b/connectors/webapp/Makedefs.in
new file mode 100644
index 0000000..7233e1b
--- /dev/null
+++ b/connectors/webapp/Makedefs.in
@@ -0,0 +1,84 @@
+#
+# Copyright 1999-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# @author  Pier Fumagalli <mailto:pier@betaversion.org>
+# @version $Id$
+
+.SUFFIXES: .c .o .lo
+
+# Building tools
+CC      = @CC@
+CPP     = @CPP@
+SHELL   = @SHELL@
+LIBTOOL = @LIBTOOL@
+INSTALL = $(SHELL) $(SRC_DIR)/support/install.sh -c
+APXS    = @APXS@
+
+# Build flags
+CPPFLAGS = @CPPFLAGS@
+CFLAGS   = @CFLAGS@
+LDFLAGS  = @LDFLAGS@
+INCLUDES = @INCLUDES@
+
+# Extra build flags
+EXTRA_CPPFLAGS = @EXTRA_CPPFLAGS@
+EXTRA_CFLAGS   = @EXTRA_CFLAGS@
+EXTRA_LDFLAGS  = @EXTRA_LDFLAGS@
+EXTRA_INCLUDES = @EXTRA_INCLUDES@
+
+# apr library name
+APR_LIBNAME = @APR_LIBNAME@
+APR_LIB = @APR_LIB@
+
+# Module to build
+MODULE = @MODULE@
+
+# Directories location
+APR_DIR = @APR_DIR@
+SRC_DIR = @SRC_DIR@
+TGT_DIR = @TGT_DIR@
+BLD_DIR = $(TGT_DIR)/build
+OBJ_DIR = $(BLD_DIR)/objs
+LIB_DIR = $(BLD_DIR)/libs
+DOC_DIR = $(BLD_DIR)/docs
+API_DIR = $(DOC_DIR)/api-c
+
+# Distribution file names
+TARBALL = webapp-module-$(WEBAPP_VERSION)
+MODFILE = mod_webapp.so
+
+# Related tools used in the build process
+PERL        = @PERL@
+ANT         = @ANT@
+ANT_TARGETS = @ANT_TARGETS@
+
+# Versions
+APACHE_VERSION = @APACHE_VERSION@
+WEBAPP_VERSION = @WEBAPP_VERSION@
+HOST           = @HOST@
+DATE           = @DATE@
+
+# Compilation template
+%.lo: $(LOCAL_SRC_DIR)/%.c 
+	$(LIBTOOL) --mode=compile \
+	  $(CC) -c $< -o $@ \
+	    $(INCLUDES) \
+	    $(CPPFLAGS) \
+	    $(CFLAGS) 
+	$(LIBTOOL) --mode=install \
+	  $(INSTALL) \
+	    $@ \
+	    $(OBJ_DIR)
diff --git a/connectors/webapp/Makefile.in b/connectors/webapp/Makefile.in
new file mode 100644
index 0000000..1c018e4
--- /dev/null
+++ b/connectors/webapp/Makefile.in
@@ -0,0 +1,141 @@
+#
+# Copyright 1999-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# @author  Pier Fumagalli <mailto:pier@betaversion.org>
+# @version $Id$
+
+include @TGT_DIR@/Makedefs
+
+# ========================================================================= #
+# Generic/global compilation targets                                        #
+# ========================================================================= #
+all: build
+
+distclean: extra-clean
+	rm -f $(MODULE)/Makefile
+	rm -f lib/Makefile
+	rm -f Makedefs
+	rm -f Makefile
+	rm -f DETAILS.txt
+	rm -fr $(DST_DIR)
+
+build: $(OBJ_DIR) @EXTRA_BUILD@ lib-build $(MODULE)-build $(BLD_DIR)/DETAILS.txt
+	@echo ""
+	@cat $(BLD_DIR)/DETAILS.txt
+	@echo ""
+	@echo All done...
+
+extra-clean: @EXTRA_CLEAN@ clean
+
+clean: lib-clean $(MODULE)-clean
+	rm -rf $(OBJ_DIR)
+
+# ========================================================================= #
+# Build the MODULE module, and copy the resulting file in the build dir.    #
+# ========================================================================= #
+$(MODULE)-build: $(BLD_DIR)
+	@$(MAKE) SUBF="$(MAKEFLAGS)" SUBD="$(TGT_DIR)/$(MODULE)" SUBT="build" subdir
+	@$(INSTALL) $(TGT_DIR)/$(MODULE)/$(MODFILE) $(BLD_DIR)
+
+$(MODULE)-clean:
+	@$(MAKE) SUBF="$(MAKEFLAGS)" SUBD="$(TGT_DIR)/$(MODULE)" SUBT="clean" subdir
+	@rm -f $(BLD_DIR)/$(MODFILE)
+
+# ========================================================================= #
+# Build all WebApp library files                                            #
+# ========================================================================= #
+lib-build:
+	@$(MAKE) SUBF="$(MAKEFLAGS)" SUBD="$(TGT_DIR)/lib" SUBT="build" subdir
+
+lib-clean:
+	@$(MAKE) SUBF="$(MAKEFLAGS)" SUBD="$(TGT_DIR)/lib" SUBT="clean" subdir
+
+# ========================================================================= #
+# Run ANT to generate all stuff it generates (jar,docs,javadocs)            #
+# ========================================================================= #
+ant-build:
+	$(ANT) -buildfile "$(SRC_DIR)/build.xml" init $(ANT_TARGETS)
+
+ant-clean:
+	$(ANT) -buildfile "$(SRC_DIR)/build.xml" clean
+
+# ========================================================================= #
+# Run PERL to generate our C-API documentation with scandoc                 #
+# ========================================================================= #
+capi-build: $(API_DIR)
+	$(PERL) "$(SRC_DIR)/support/scandoc.pl" \
+	  -i "$(SRC_DIR)/support/template.pl" \
+	  -p "$(API_DIR)/" \
+	  $(SRC_DIR)/include/*.h
+
+capi-clean:
+	@echo "Cleaning up generated C-API documentation"
+	@rm -rf "$(API_DIR)/*"
+
+# ========================================================================= #
+# Call APR and generate the library when we are building for Apache 1.3     #
+# ========================================================================= #
+apr-build: $(LIB_DIR)
+	@$(MAKE) SUBF="$(MAKEFLAGS)" SUBD="$(APR_DIR)" SUBT="all" subdir
+	$(LIBTOOL) --mode=install \
+	  cp $(APR_DIR)/$(APR_LIBNAME) $(LIB_DIR)/$(APR_LIBNAME)
+	$(LIBTOOL) --mode=finish $(LIB_DIR)
+
+apr-clean:
+	@$(MAKE) SUBF="$(MAKEFLAGS)" SUBD="$(APR_DIR)" SUBT="clean" subdir
+
+# ========================================================================= #
+# Invoke MAKE into a subdir (nicely notified by some messages)              #
+# ========================================================================= #
+subdir:
+	@echo ""
+	@echo "$(MAKE)[$(MAKELEVEL)]: Entering directory \"$(SUBD)\""
+	@$(MAKE) -C "$(SUBD)" $(SUBF) "$(SUBT)"
+	@echo "$(MAKE)[$(MAKELEVEL)]: Exiting directory \"$(SUBD)\""
+
+# ========================================================================= #
+# Targets to build the directory structure (non-PHONY)                      #
+# ========================================================================= #
+$(LIB_DIR): $(BLD_DIR)
+	@if test ! -d "$(LIB_DIR)" ; then mkdir $(LIB_DIR) ; fi
+
+$(OBJ_DIR): $(BLD_DIR)
+	@if test ! -d "$(OBJ_DIR)" ; then mkdir $(OBJ_DIR) ; fi
+
+$(DOC_DIR): $(BLD_DIR)
+	@if test ! -d "$(DOC_DIR)" ; then mkdir $(DOC_DIR) ; fi
+
+$(API_DIR): $(DOC_DIR)
+	@if test ! -d "$(API_DIR)" ; then mkdir $(API_DIR) ; fi
+
+$(BLD_DIR):
+	@if test ! -d "$(BLD_DIR)" ; then mkdir $(BLD_DIR) ; fi
+
+$(DST_DIR):
+	@if test ! -d "$(DST_DIR)" ; then mkdir $(DST_DIR) ; fi
+
+# ========================================================================= #
+# Our compilation details file                                              #
+# ========================================================================= #
+$(BLD_DIR)/DETAILS.txt: $(BLD_DIR)
+	@echo "Storing build details" ; { \
+	    echo "Coonfiguration details:" ; \
+	    echo "" ; \
+	    echo "module version:  $(WEBAPP_VERSION)" ; \
+	    echo "httpd version:   $(APACHE_VERSION)" ; \
+	    echo "host machine/os: $(HOST)" ; \
+	    echo "cration date:    $(DATE)" ; \
+	} > $@
diff --git a/connectors/webapp/Makefile.win b/connectors/webapp/Makefile.win
new file mode 100644
index 0000000..666ee28
--- /dev/null
+++ b/connectors/webapp/Makefile.win
@@ -0,0 +1,84 @@
+#
+# Copyright 1999-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# @author  Pier Fumagalli <mailto:pier@betaversion.org>
+# @version $Id$
+
+# Analyze and normalyze the DEBUG compilation flag
+!IF "$(DEBUG)" == "true"
+APRCFG = libapr - Win32 Debug
+!ELSE
+DEBUG = false
+APRCFG = libapr - Win32 Release
+!ENDIF
+
+# Makefile rules
+all: apr-all local-all
+
+dist: all
+    -mkdir dist
+!IF "$(DEBUG)" == "true"
+    copy .\apr\Debug\libapr.dll .\dist\libapr.dll
+!ELSE
+    copy .\apr\Release\libapr.dll .\dist\libapr.dll
+!ENDIF
+    copy .\apache-1.3\mod_webapp.dll .\dist\mod_webapp.so
+    copy INSTALL.txt .\dist\INSTALL.txt
+    copy LICENSE.txt .\dist\LICENSE.txt
+
+clean: apr-clean local-clean
+    -@erase localmake.bat
+
+apr-all: localmake.bat
+    @localmake.bat .\apr /f libapr.mak "CFG=$(APRCFG)" all
+    @echo DEBUG IS -$(DEBUG)-
+
+apr-clean: localmake.bat
+    @localmake.bat .\apr /f libapr.mak "CFG=$(APRCFG)" clean
+    -@erase .\apr\apr.lib
+
+local-all: localmake.bat
+    @localmake.bat .\lib /f Makefile.win "DEBUG=$(DEBUG)" all
+!IF "$(APACHE)" != ""
+    @localmake.bat .\apache-1.3 /f Makefile.win \
+        "DEBUG=$(DEBUG)" "APACHE=$(APACHE)" all
+!ENDIF
+
+local-clean: localmake.bat
+    @localmake.bat .\lib /f Makefile.win "DEBUG=$(DEBUG)" clean
+!IF "$(APACHE)" != ""
+    @localmake.bat .\apache-1.3 /f Makefile.win \
+        "DEBUG=$(DEBUG)" "APACHE=$(APACHE)" clean
+!ENDIF
+
+# Create the localmake.bat script
+localmake.bat: Makefile.win
+    @echo Creating "localmake.bat" script
+    @type << > localmake.bat
+@echo off
+echo === Entering directory "%1"
+cd %1
+echo --- Invoking $(MAKE) /nologo %2 %3 %4 %5 %6 %7 %8 %9
+$(MAKE) /nologo %2 %3 %4 %5 %6 %7 %8 %9
+set result=%errorlevel%
+cd ..
+if not "%result%" == "0" goto error
+echo === All done in "%1"
+exit 0
+:error
+echo === Error in "%1" (exit code %result%)
+exit %result%
+<<
diff --git a/connectors/webapp/README.txt b/connectors/webapp/README.txt
new file mode 100644
index 0000000..9258f29
--- /dev/null
+++ b/connectors/webapp/README.txt
@@ -0,0 +1,142 @@
+README for WebApp Library and Related Modules
+---------------------------------------------
+
+Notice
+------
+
+NO, IT DOES NOT RUN WITH WINDOWS (your images don't appear and the
+whole thing hangs?) AND SINCE I DON'T USE NEITHER POSSESS A MICROSOFT
+WINDOWS BASED MACHINE, THERE ARE NO CURRENT PLANS ON MAKING IT WORK 
+OVER THERE (from my side).
+
+If you want to do it (port it to Windows), those are few hints:
+- Apache 1.3 on Windows is MultiThreaded
+- Therefore the error coming out is a multi-threading issue
+- The WARP socket is unique in WebApp
+- You need to change that and build up a socket pool
+- And add some locking mechanism to prevent race conditions
+
+If you don't know what the above means, simply forget it (and I suggest
+you doing a "format c:" and install Solaris 8 - my favorite preference
+or throw out your x86 hardware out of in the dumpster and get an
+Apple PowerMac with MacOS/X installed). If you are willing to do the
+job, you're more than welcome to write to me or to the mailing list
+with some patches.
+
+How to obtain the WebApp and Apache Portable Runtime sources:
+-------------------------------------------------------------
+
+NOTE: If you downloaded a source distribution from our website or a
+mirror (the file is called webapp-module...src.tar.gz) you don't need
+to obtain any other file. Please follow this chapter only if you want
+to obtain the latest CVS version of the sources.
+
+Check out the module sources from CVS using the following commands:
+
+    cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic login
+    (Logging in to anoncvs@cvs.apache.org)
+    CVS password: anoncvs
+    cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic \
+        checkout jakarta-tomcat-connectors/webapp
+
+Once CVS downloads the WebApp module sources, we need to download the
+APR (Apache Portable Runtime) sources. To do this simply:
+
+    cd ./jakarta-tomcat-connectors/webapp
+    cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic \
+        checkout apr
+An other way is to use a release version of APR:
+    - You can download it from http://www.apache.org/dist/apr/.
+    - Extract it in jakarta-tomcat-connectors/webapp (Remember use a Gnu tar). 
+
+When the APR sources are in place, we need to create the configure
+scripts. It is done by running the command:
+
+    ./support/buildconf.sh
+
+To build the sources, now follow the steps in the next chapters.
+
+How to build the tomcat-webapp.jar module from CVS sources:
+------------------------------------------------
+
+The tomcat-webapp.jar is build using Ant.
+To get Ant see (http://jakarta.apache.org/ant/index.html).
+
+To build the tomcat-webapp.jar you have to do the following:
+
+* Edit build.properties to taste. This file is created by ./configure when
+  the WebApp module is configured, see below.
+
+* Run "ant". It'll build the tomcat-webapp.jar
+
+* Copy the build/lib/tomcat-webapp.jar in the Tomcat server/lib directory.
+
+* If using cygwin make sure of the following:
+  - cygwin1.dll is in your PATH variable.
+  - grep is installed (GNU grep, egrep and fgrep: Category Base).
+  - sed is installed (sed: GNU sed stream editor: Category Base).
+
+How to build the WebApp module from CVS sources:
+------------------------------------------------
+
+If you downloaded the CVS sources (as described above) or downloaded a
+source distribution of the WebApp module, now all you need to do is build
+the binary module for your platform. To do so, start by doing a:
+
+    ./configure --with-apxs
+    make
+
+In case your platform needs some flags for APR just put them before the
+configure. For example:
+   ./support/buildconf.sh
+   CC=/usr/bin/cc \
+   CFLAGS=-DXTI_SUPPORT \
+   ./configure --with-apxs=/opt/apache/bin/apxs
+
+This will configure and build APR, and build the WebApp module for
+Apache 1.3. The available options for the configure script are:
+
+    --with-apxs[=FILE]
+        Use the APXS Apache 1.3 Extension Tool. If this option is
+        not specified, the Apache module will not be built (only the
+        APR and WEBAPP libraries will be build).
+        The "FILE" parameter specifies the full path for the apxs
+        executable. If this is not specified apxs will be searched in
+        the current path.
+
+    --with-apr=DIR
+        If you already have the APR sources lying around somewhere, and
+        want to use them instead of checking them out from CVS, you can
+        specify where these can be found.
+
+    --enable-debug
+        Enable compiled-in debugging output. Using this option the WebApp
+        module, library, and Java counterpart will be built with debugging
+        information. This will create a lot of output in your log files,
+        and will kill performances, but it's a good starting poing when
+        something goes wrong.
+    --enable-java=DIR
+        Enable the build of java part of mod_webapp: tomcat-warp.jar.
+        DIR is the directory containing the Tomcat distribution.
+        It works for Tomcat 4.x.
+
+Once built, the DSO module will be found in the webapp/apache-1.3 directory.
+
+To install it  copy the mod_webapp.so file in your Apache 1.3 libexec
+directory, and add the following lines to httpd.conf:
+
+    LoadModule webapp_module [path to mod_webapp.so]
+    AddModule mod_webapp.c
+
+To check out if everything is correctly configured, issue the following:
+
+    apachectl configtest
+
+If the output of the apachectl command doesn't include "Syntax OK", something
+went wrong with the build process. Please report that through our bug tracking
+database at <http://nagoya.apache.org/bugzilla> or to the Tomcat developers
+mailing list <mailto:tomcat-dev@jakarta.apache.org>
+
+Have fun...
+
+    Pier <pier@betaversion.org>
diff --git a/connectors/webapp/apache-1.3/.cvsignore b/connectors/webapp/apache-1.3/.cvsignore
new file mode 100644
index 0000000..6293ef0
--- /dev/null
+++ b/connectors/webapp/apache-1.3/.cvsignore
@@ -0,0 +1,11 @@
+.DS_Store
+.libs
+Makefile
+mod_webapp.dll
+mod_webapp.exp
+mod_webapp.idb
+mod_webapp.lib
+mod_webapp.lo
+mod_webapp.o
+mod_webapp.obj
+mod_webapp.pdb
diff --git a/connectors/webapp/apache-1.3/Makefile.in b/connectors/webapp/apache-1.3/Makefile.in
new file mode 100644
index 0000000..9708f3a
--- /dev/null
+++ b/connectors/webapp/apache-1.3/Makefile.in
@@ -0,0 +1,62 @@
+#
+# Copyright 1999-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# @author  Pier Fumagalli <mailto:pier@betaversion.org>
+# @version $Id$
+
+LOCAL_TGT_DIR = @TGT_DIR@/apache-1.3
+LOCAL_SRC_DIR = @SRC_DIR@/apache-1.3
+include @TGT_DIR@/Makedefs
+
+SOURCE = mod_webapp.c
+OBJECT = mod_webapp.o
+
+.PHONY: build clean
+
+build: $(MODFILE)
+
+clean:
+	rm -f $(MODFILE)
+	rm -f $(OBJECT)
+	@if test -h $(SOURCE) ; then \
+	  echo rm -f $(SOURCE) ; \
+	  rm -f $(SOURCE) ; \
+	fi
+
+$(SOURCE): $(LOCAL_SRC_DIR)/$(SOURCE)
+	ln -s $< $@
+	  
+$(MODFILE): $(SOURCE)
+	$(APXS) -c -o $@ \
+	  $(INCLUDES) $(LOCAL_INCLUDES) \
+	  -Wc,"$(CPPFLAGS) $(CFLAGS)" \
+	  -Wl,"$(LDFLAGS) $(LIBS)" \
+	  -L$(LIB_DIR) -l$(APR_LIB) \
+	  $(OBJ_DIR)/*.o $<
+
+# +++ EXPERIMENTAL +++ LIBTOOL COMPILE, APXS LINK +++
+# SOURCE = mod_webapp.c
+# OBJECT = mod_webapp.lo
+# 
+# $(OBJECT): $(LOCAL_SRC_DIR)/$(SOURCE)
+# 	$(LIBTOOL) --mode=compile \
+# 	  $(CC) -c $< -o $@ \
+# 	    $(INCLUDES) $(EXTRA_INCLUDES) \
+# 	    $(CPPFLAGS) $(EXTRA_CPPFLAGS) \
+# 	    $(CFLAGS) $(EXTRA_CFLAGS)
+# 
+# $(MODFILE): $(OBJECT)
+# 	$(APXS) -c -o $@ -L$(OBJ_DIR) -l$(APR_LIB) $< $(OBJ_DIR)/*.o
diff --git a/connectors/webapp/apache-1.3/Makefile.win b/connectors/webapp/apache-1.3/Makefile.win
new file mode 100644
index 0000000..1533759
--- /dev/null
+++ b/connectors/webapp/apache-1.3/Makefile.win
@@ -0,0 +1,100 @@
+#
+# Copyright 1999-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# @author  Pier Fumagalli <mailto:pier@betaversion.org>
+# @version $Id$
+
+# All our object files
+OBJS =   mod_webapp.obj
+
+# Files created by this script (for removal only)
+GENS =   mod_webapp.idb
+
+# The target library
+LIBS =   mod_webapp.dll
+
+# Microsoft Visual C/C++ 6.0 compilation and linking programs
+CC =     cl.exe
+LINK =   link.exe
+
+# Flags for the C compiler
+CFLAGS = /nologo \
+         /W3 \
+!IF "$(DEBUG)" == "true"
+         /MDd \
+         /GX \
+         /Zi \
+         /Od \
+         /Yd \
+         /D"DEBUG" \
+!ELSE
+	     /MD \
+	     /O2 \
+!ENDIF
+	     /I "." \
+	     /I "..\include" \
+	     /I "..\apr\include" \
+	     /I "$(APACHE)\include" \
+	     /D"WIN32" \
+	     /Fd".\mod_webapp" \
+	     /FD
+
+# Flags for the library linker
+LFLAGS = /nologo \
+         apachecore.lib \
+         libapr.lib \
+         webapp.lib \
+         advapi32.lib \
+         comdlg32.lib \
+         gdi32.lib \
+         kernel32.lib \
+         odbc32.lib \
+         odbccp32.lib \
+         ole32.lib \
+         oleaut32.lib \
+         shell32.lib \
+         user32.lib \
+         uuid.lib \
+         winspool.lib \
+         ws2_32.lib \
+         /nologo \
+         /dll \
+         /pdb:"mod_webapp.pdb" \
+         /machine:I386 \
+         /implib:"mod_webapp.lib" \
+         /libpath:"..\lib" \
+         /libpath:"$(APACHE)\libexec" \
+!IF "$(DEBUG)" == "true"
+         /libpath:"..\apr\Debug" \
+         /debug \
+         /pdbtype:sept \
+!ELSE
+         /libpath:"..\apr\Release" \
+!ENDIF
+         /incremental:no
+
+# Makefile rules
+all: $(LIBS)
+
+mod_webapp.dll: $(OBJS)
+    $(LINK) $(LFLAGS) /out:$@ $(OBJS)
+
+clean:
+    -@for %%i in ($(OBJS) $(GENS) $(LIBS)) do \
+        @erase "%%i"
+
+.c.obj:
+	$(CC) $(CFLAGS) /Fo"$@" /c "$<"
diff --git a/connectors/webapp/apache-1.3/mod_webapp.c b/connectors/webapp/apache-1.3/mod_webapp.c
new file mode 100644
index 0000000..f8eda34
--- /dev/null
+++ b/connectors/webapp/apache-1.3/mod_webapp.c
@@ -0,0 +1,520 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * @author  Pier Fumagalli <mailto:pier@betaversion.org>
+ * @version $Id$
+ */
+
+#include <httpd.h>
+#include <http_config.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_main.h>
+#include <http_protocol.h>
+#include <util_script.h>
+#include <wa.h>
+
+/* ************************************************************************* */
+/* GENERIC DECLARATIONS                                                      */
+/* ************************************************************************* */
+
+/* Module declaration */
+module MODULE_VAR_EXPORT webapp_module;
+/* Wether the WebApp Library has been initialized or not */
+static wa_boolean wam_initialized=wa_false;
+/* The list of configured connections */
+static wa_chain *wam_connections=NULL;
+/* The main server using for logging error not related to requests */
+static server_rec *server=NULL;
+
+/* ************************************************************************* */
+/* MODULE AND LIBRARY INITIALIZATION AND DESTRUCTION                         */
+/* ************************************************************************* */
+
+/* Destroy the module and the WebApp Library */
+static void wam_child_destroy(server_rec *s, pool *p) {
+    if (!wam_initialized) return;
+    wa_shutdown();
+    wam_initialized=wa_false;
+}
+
+/* Startup the module and the WebApp Library */
+static void wam_child_init(server_rec *s, pool *p) {
+    if (!wam_initialized) return;
+    server=s;
+    wa_startup();
+}
+
+/* Initialize the module, by adding our version info in Apache. */
+static void wam_module_init(server_rec *s, ap_pool *p) {
+    ap_add_version_component(WA_VERSION);
+}
+
+/* Initialize the module and the WebApp Library */
+static const char *wam_init(pool *p) {
+    const char *ret=NULL;
+
+    if(wam_initialized==wa_true) return(NULL);
+    if ((ret=wa_init())!=NULL) return(ret);
+    wam_initialized=wa_true;
+    return(NULL);
+}
+
+/* ************************************************************************* */
+/* CONFIGURATION DIRECTIVES HANDLING                                         */
+/* ************************************************************************* */
+
+/* Retrieve or create a wa_virtualhost structure for an Apache server_rec
+   and store it as the per-server module configuration */
+const char *wam_server(server_rec *svr, wa_virtualhost **h) {
+    wa_virtualhost *host=NULL;
+    char *name=svr->server_hostname;
+    int port=(int)svr->port;
+    const char *ret=NULL;
+
+    /* Attempt to retrieve the wa_virtualhost structure and create it
+       if necessary, storing it into the server_rec structure. */
+    host=ap_get_module_config(svr->module_config,&webapp_module);
+
+    /* If we already configured the wa_virtualhost, simply return it */
+    if (host!=NULL) {
+        *h=host;
+        return(NULL);
+    }
+
+    /* The wa_virtualhost was not found in the per-server module configuration
+       so we'll try to create it. */
+    ret=wa_cvirtualhost(&host,name,port);
+    if (ret!=NULL) {
+        *h=NULL;
+        return(ret);
+    }
+
+    /* We successfully created a wa_virtualhost structure, store it in the
+       per-server configuration member and return it. */
+    ap_set_module_config(svr->module_config,&webapp_module,host);
+    *h=host;
+    return(NULL);
+}
+
+/* Process the WebAppConnection directive. */
+static const char *wam_directive_connection(cmd_parms *cmd, void *mconfig,
+                                            char *name, char *prov, char *p) {
+    wa_connection *conn=NULL;
+    const char *ret=NULL;
+    wa_chain *elem=NULL;
+
+    /* Initialize the library */
+    if ((ret=wam_init(cmd->pool))!=NULL) return(ret);
+
+    /* Attempt to create a new wa_connection structure */
+    if ((ret=wa_cconnection(&conn,name,prov,p))!=NULL) return(ret);
+
+    /* Check if we have a duplicate connection with this name */
+    elem=wam_connections;
+    while (elem!=NULL) {
+        wa_connection *curr=(wa_connection *)elem->curr;
+        if (strcasecmp(conn->name,curr->name)==0)
+            return("Duplicate connection name");
+        elem=elem->next;
+    }
+
+    /* We don't have a duplicate connection, store it locally */
+    elem=apr_palloc(wa_pool,sizeof(wa_chain));
+    elem->curr=conn;
+    elem->next=wam_connections;
+    wam_connections=elem;
+    return(NULL);
+}
+
+/* Process the WebAppDeploy directive */
+static const char *wam_directive_deploy(cmd_parms *cmd, void *mconfig,
+                                      char *name, char *cnam, char *path) {
+    wa_virtualhost *host=NULL;
+    wa_application *appl=NULL;
+    wa_connection *conn=NULL;
+    wa_chain *elem=NULL;
+    const char *ret=NULL;
+
+    /* Initialize the library and retrieve/create the host structure */
+    if ((ret=wam_init(cmd->pool))!=NULL) return(ret);
+    if ((ret=wam_server(cmd->server,&host))!=NULL) return(ret);
+
+    /* Retrieve the connection */
+    elem=wam_connections;
+    while(elem!=NULL) {
+        wa_connection *curr=(wa_connection *)elem->curr;
+        if (strcasecmp(curr->name,cnam)==0) {
+            conn=curr;
+            break;
+        }
+        elem=elem->next;
+    }
+    if (conn==NULL) return("Specified connection not configured");
+
+    /* Create a new wa_application member */
+    if ((ret=wa_capplication(&appl,name,path))!=NULL) return(ret);
+
+    /* Deploy the web application */
+    if ((ret=wa_deploy(appl,host,conn))!=NULL) return(ret);
+
+    /* Done */
+    return(NULL);
+}
+
+/* Process the WebAppInfo directive */
+static const char *wam_directive_info(cmd_parms *cmd, void *mconfig,
+                                      char *path) {
+    const char *ret;
+
+    /* We simply divert this call to a WebAppConnection and a WebAppDeploy
+       calls */
+    if ((ret=wam_directive_connection(cmd,mconfig,"_INFO_","info",""))!=NULL)
+        return(ret);
+    if ((ret=wam_directive_deploy(cmd,mconfig,"_INFO_","_INFO_",path))!=NULL)
+        return(ret);
+
+    return(NULL);
+}
+
+/* The list of Directives for the WebApp module */
+static const command_rec wam_directives[] = {
+    {
+        "WebAppInfo",               /* directive name */
+        wam_directive_info,         /* config action routine */
+        NULL,                       /* argument to include in call */
+        OR_OPTIONS,                 /* where available */
+        TAKE1,                      /* arguments */
+        "<uri-path>"
+    },{
+        "WebAppConnection",         /* directive name */
+        wam_directive_connection,   /* config action routine */
+        NULL,                       /* argument to include in call */
+        RSRC_CONF,                  /* where available */
+        TAKE23,                     /* arguments */
+        "<name> <provider> [optional parameter]"
+    }, {
+        "WebAppDeploy",             /* directive name */
+        wam_directive_deploy,       /* config action routine */
+        NULL,                       /* argument to include in call */
+        RSRC_CONF,                  /* where available */
+        TAKE3,                      /* arguments */
+        "<name> <connection> <uri-path>"
+    }, {NULL}
+
+};
+
+/* ************************************************************************* */
+/* CALLBACKS TO WEB SERVER                                                   */
+/* ************************************************************************* */
+
+/* Log a generic error */
+void wa_log(const char *f, const int l, const char *fmt, ...) {
+    va_list ap;
+    char buf[1024];
+#ifdef DEBUG
+    char tmp[1024];
+#endif
+
+    va_start(ap,fmt);
+#ifdef DEBUG
+    apr_vsnprintf(tmp,1024,fmt,ap);
+    apr_snprintf(buf,1024,"[%s:%d] %s",f,l,tmp);
+#else
+    apr_vsnprintf(buf,1024,fmt,ap);
+#endif
+    va_end(ap);
+
+    ap_log_error(f,l,APLOG_NOERRNO|APLOG_ERR,server,"%s",buf);
+}
+
+/* Log a message associated with a request */
+void wam_handler_log(wa_request *r, const char *f, const int l, char *msg) {
+    request_rec *req=(request_rec *)r->data;
+    server_rec *svr=req->server;
+
+    ap_log_error(f,l,APLOG_NOERRNO|APLOG_ERR,svr,"%s",msg);
+}
+
+/* Set the HTTP status of the response. */
+void wam_handler_setstatus(wa_request *r, int status, char *message) {
+    request_rec *req=(request_rec *)r->data;
+
+    if ((message!=NULL) && (message[0]!='\0'))
+        req->status_line=ap_psprintf(req->pool,"%03d %s", status, message);
+
+    req->status=status;
+}
+
+/* Set the MIME Content-Type of the response. */
+void wam_handler_setctype(wa_request *r, char *type) {
+    request_rec *req=(request_rec *)r->data;
+
+    if (type==NULL) return;
+
+    req->content_type=ap_pstrdup(req->pool,type);
+    ap_table_add(req->headers_out,"Content-Type",ap_pstrdup(req->pool,type));
+}
+
+/* Set a header in the HTTP response. */
+void wam_handler_setheader(wa_request *r, char *name, char *value) {
+    request_rec *req=(request_rec *)r->data;
+
+    if (name==NULL) return;
+    if (value==NULL) value="";
+
+    ap_table_add(req->headers_out,ap_pstrdup(req->pool,name),
+                 ap_pstrdup(req->pool,value));
+}
+
+/* Commit the first part of the response (status and headers) */
+void wam_handler_commit(wa_request *r) {
+    request_rec *req=(request_rec *)r->data;
+
+    ap_send_http_header(req);
+    ap_rflush(req);
+}
+
+/* Flush all data in the response buffer */
+void wam_handler_flush(wa_request *r) {
+    request_rec *req=(request_rec *)r->data;
+
+    ap_rflush(req);
+}
+
+/* Read a chunk of text from the request body */
+int wam_handler_read(wa_request *r, char *buf, int len) {
+    request_rec *req=(request_rec *)r->data;
+    long ret=0;
+
+    /* Check if we have something to read. */
+    if (r->clen==0) return(0);
+
+    /* Check if we had an error previously. */
+    if (r->rlen==-1) return(-1);
+
+    /* Send HTTP_CONTINUE to client when we're ready to read for the first
+       time. */
+    if (r->rlen==0) {
+        if (ap_should_client_block(req)==0) return(0);
+    }
+
+    /* Read some data from the client and fill the buffer. */
+    ret=ap_get_client_block(req,buf,len);
+    if (ret==-1) {
+        r->rlen=-1;
+        return(-1);
+    }
+
+    /* We did read some bytes, increment the current rlen and return. */
+    r->rlen+=ret;
+    return((int)ret);
+}
+
+/* Write a chunk of text into the response body. */
+int wam_handler_write(wa_request *r, char *buf, int len) {
+    request_rec *req=(request_rec *)r->data;
+
+    return(ap_rwrite(buf, len, req));
+}
+
+/* The structure holding all callback handling functions for the library */
+static wa_handler wam_handler = {
+    wam_handler_log,
+    wam_handler_setstatus,
+    wam_handler_setctype,
+    wam_handler_setheader,
+    wam_handler_commit,
+    wam_handler_flush,
+    wam_handler_read,
+    wam_handler_write,
+};
+
+/* ************************************************************************* */
+/* REQUEST HANDLING                                                          */
+/* ************************************************************************* */
+
+/* Match an Apache request */
+static int wam_match(request_rec *r) {
+    wa_virtualhost *host=NULL;
+    wa_application *appl=NULL;
+    wa_chain *elem=NULL;
+
+    /* Paranoid check */
+    if (!wam_initialized) return(DECLINED);
+
+    /* Check if this host was recognized */
+    host=ap_get_module_config(r->server->module_config,&webapp_module);
+    if (host==NULL) return(DECLINED);
+
+    /* Check if the uri is contained in one of our applications root path */
+    elem=host->apps;
+    while(elem!=NULL) {
+        appl=(wa_application *)elem->curr;
+        if (strncmp(appl->rpth,r->uri,strlen(appl->rpth))==0) break;
+
+        appl=NULL;
+        elem=elem->next;
+    }
+    if (appl==NULL) return(DECLINED);
+
+    /* The uri path is matched: set the handler and return */
+    r->handler=ap_pstrdup(r->pool,"webapp-handler");
+
+    /* Set the webapp request structure into Apache's request structure */
+    ap_set_module_config(r->request_config, &webapp_module, appl);
+    return(OK);
+}
+
+/* Handle the current request */
+static int wam_invoke(request_rec *r) {
+    server_rec *svr=r->server;
+    conn_rec *con=r->connection;
+    wa_application *appl=NULL;
+    wa_request *req=NULL;
+    const char *msg=NULL;
+    char *stmp=NULL;
+    char *ctmp=NULL;
+    char *ssl_temp;
+    int ret=0;
+
+    /* Paranoid check */
+    if (!wam_initialized) return(DECLINED);
+
+    /* Try to get a hold on the webapp request structure */
+    appl=(wa_application *)ap_get_module_config(r->request_config,
+                                                &webapp_module);
+    if (appl==NULL) return(DECLINED);
+
+    /* Allocate the webapp request structure */
+    if ((msg=wa_ralloc(&req, &wam_handler, r))!=NULL) {
+        ap_log_error(APLOG_MARK,APLOG_NOERRNO|APLOG_ERR,svr,"%s",msg);
+        return(HTTP_INTERNAL_SERVER_ERROR);
+    }
+
+    /* Set up the WebApp Library request structure client and server host
+       data (from the connection */
+    ap_add_common_vars(r);
+    stmp=(char *)r->hostname;
+    ctmp=(char *)ap_get_remote_host(con,r->per_dir_config, REMOTE_NAME);
+    if (stmp==NULL) req->serv->host="";
+    else req->serv->host=apr_pstrdup(req->pool,stmp);
+    if (ctmp==NULL) req->clnt->host="";
+    else req->clnt->host=apr_pstrdup(req->pool,ctmp);
+    req->serv->addr=apr_pstrdup(req->pool,con->local_ip);
+    req->clnt->addr=apr_pstrdup(req->pool,con->remote_ip);
+    req->serv->port=ntohs(con->local_addr.sin_port);
+    req->clnt->port=ntohs(con->remote_addr.sin_port);
+
+    /* Set up all other members of the request structure */
+    req->meth=apr_pstrdup(req->pool,(char *)r->method);
+    req->ruri=apr_pstrdup(req->pool,r->uri);
+    req->args=apr_pstrdup(req->pool,r->args);
+    req->prot=apr_pstrdup(req->pool,r->protocol);
+    req->user=apr_pstrdup(req->pool,con->user);
+    req->auth=apr_pstrdup(req->pool,con->ap_auth_type);
+    req->clen=0;
+    req->ctyp="\0";
+    req->rlen=0;
+
+    /* SSL logic */
+    ssl_temp = (char *)ap_table_get(r->subprocess_env,"HTTPS");
+    if ( ssl_temp && !strcasecmp(ssl_temp, "on")) {
+        req->schm=apr_pstrdup(req->pool,"https");
+
+        req->ssld=(wa_ssldata *) apr_palloc(req->pool,sizeof(wa_ssldata));
+
+        req->ssld->ciph = (char *)ap_table_get(
+            r->subprocess_env,"SSL_CIPHER");
+        req->ssld->sess = (char *)ap_table_get(
+            r->subprocess_env,"SSL_SESSION_ID");
+
+        ssl_temp = (char *)ap_table_get(
+            r->subprocess_env,"SSL_CIPHER_USEKEYSIZE");
+        if (ssl_temp)
+            req->ssld->size = atoi(ssl_temp);
+        else
+            req->ssld->size = 0;
+
+        req->ssld->cert = (char *)ap_table_get(
+            r->subprocess_env,"SSL_CLIENT_CERT");
+    } else {
+        req->schm=apr_pstrdup(req->pool,"http");
+        req->ssld=NULL;
+    }
+
+    /* Copy headers into webapp request structure */
+    if (r->headers_in!=NULL) {
+        array_header *arr=ap_table_elts(r->headers_in);
+        table_entry *ele=(table_entry *)arr->elts;
+        int x=0;
+
+        /* Copy headers one by one */
+        for (x=0; x<arr->nelts;x++) {
+            if (ele[x].key==NULL) continue;
+            if (ele[x].val==NULL) continue;
+            apr_table_add(req->hdrs,apr_pstrdup(req->pool,ele[x].key),
+                                    apr_pstrdup(req->pool,ele[x].val));
+            if (strcasecmp(ele[x].key,"Content-Length")==0)
+                req->clen=atol(ele[x].val);
+            if (strcasecmp(ele[x].key,"Content-Type")==0)
+                req->ctyp=apr_pstrdup(req->pool,ele[x].val);
+        }
+    }
+
+    /* Check if we can read something from the request */
+    ret=ap_setup_client_block(r,REQUEST_CHUNKED_DECHUNK);
+    if (ret!=OK) return(ret);
+
+    /* Invoke the request */
+    ret=wa_rinvoke(req,appl);
+
+    /* Destroy the request member */
+    wa_rfree(req);
+    ap_rflush(r);
+
+    return(OK);
+}
+
+/* List of all available Apache handlers */
+static const handler_rec wam_handlers[] = {
+    {"webapp-handler", wam_invoke},
+    {NULL}
+};
+
+/* Apache module declaration */
+module MODULE_VAR_EXPORT webapp_module = {
+    STANDARD_MODULE_STUFF,
+    wam_module_init,                    /* module initializer */
+    NULL,                               /* per-directory config creator */
+    NULL,                               /* dir config merger */
+    NULL,                               /* server config creator */
+    NULL,                               /* server config merger */
+    wam_directives,                     /* command table */
+    wam_handlers,                       /* [9] list of handlers */
+    wam_match,                          /* [2] filename-to-URI translation */
+    NULL,                               /* [5] check/validate user_id */
+    NULL,                               /* [6] check user_id is valid *here* */
+    NULL,                               /* [4] check access by host address */
+    NULL,                               /* [7] MIME type checker/setter */
+    NULL,                               /* [8] fixups */
+    NULL,                               /* [10] logger */
+    NULL,                               /* [3] header parser */
+    wam_child_init,                     /* child initializer */
+    wam_child_destroy,                  /* child exit/cleanup */
+    NULL                                /* [1] post read_request handling */
+};
diff --git a/connectors/webapp/apache-2.0/.cvsignore b/connectors/webapp/apache-2.0/.cvsignore
new file mode 100644
index 0000000..8729fe3
--- /dev/null
+++ b/connectors/webapp/apache-2.0/.cvsignore
@@ -0,0 +1,5 @@
+.libs
+Makefile
+mod_webapp.la
+mod_webapp.lo
+mod_webapp.slo
diff --git a/connectors/webapp/apache-2.0/Makefile.in b/connectors/webapp/apache-2.0/Makefile.in
new file mode 100644
index 0000000..16fdc2b
--- /dev/null
+++ b/connectors/webapp/apache-2.0/Makefile.in
@@ -0,0 +1,54 @@
+#
+# Copyright 1999-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# @author  Pier Fumagalli <mailto:pier@betaversion.org>
+# @version $Id$
+
+LOCAL_TGT_DIR = @TGT_DIR@/apache-2.0
+LOCAL_SRC_DIR = @SRC_DIR@/apache-2.0
+include @TGT_DIR@/Makedefs
+
+SOURCE = mod_webapp.c
+OBJECT = mod_webapp.lo
+LIBDIR = .libs
+LIBFIL = libwebapp
+LIBEXT = .la
+
+.PHONY: build clean
+
+build: $(LIBFIL)$(LIBEXT)
+	$(INSTALL) \
+	  $(LOCAL_TGT_DIR)/$(LIBDIR)/$(LIBFIL).so \
+	  $(LOCAL_TGT_DIR)/$(MODFILE)
+
+clean:
+	rm -f $(OBJECT)
+	rm -f mod_webapp.o
+	rm -f mod_webapp.slo
+	@if test -h $(SOURCE) ; then \
+	  echo rm -f $(SOURCE) ; \
+	  rm -f $(SOURCE) ; \
+	fi
+	rm -f $(LIBFIL).*
+	rm -rf $(LIBDIR)
+
+$(SOURCE): $(LOCAL_SRC_DIR)/$(SOURCE)
+	ln -s "$<" "$@"
+
+$(LIBFIL)$(LIBEXT): $(SOURCE)
+	$(APXS) -c -o $@ \
+	  $(INCLUDES) \
+	  $(OBJ_DIR)/*.lo $<
diff --git a/connectors/webapp/apache-2.0/mod_webapp.c b/connectors/webapp/apache-2.0/mod_webapp.c
new file mode 100644
index 0000000..ff2129e
--- /dev/null
+++ b/connectors/webapp/apache-2.0/mod_webapp.c
@@ -0,0 +1,514 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * @author  Pier Fumagalli <mailto:pier@betaversion.org>
+ * @version $Id$
+ */
+
+#include <httpd.h>
+#include <http_request.h>
+#include <http_config.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_main.h>
+#include <http_protocol.h>
+#include <util_script.h>
+#include <wa.h>
+#include <apr_tables.h>
+
+/* ************************************************************************* */
+/* GENERIC DECLARATIONS                                                      */
+/* ************************************************************************* */
+
+/* Module declaration */
+module AP_MODULE_DECLARE_DATA webapp_module;
+/* Wether the WebApp Library has been initialized or not */
+static wa_boolean wam_initialized=wa_false;
+/* The list of configured connections */
+static wa_chain *wam_connections=NULL;
+/* The main server using for logging error not related to requests */
+static server_rec *server=NULL;
+
+/* ************************************************************************* */
+/* MODULE AND LIBRARY INITIALIZATION AND DESTRUCTION                         */
+/* ************************************************************************* */
+
+static int wam_module_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
+                            server_rec *s) {
+    ap_add_version_component(p, WA_VERSION);
+    return OK;
+}
+
+/* Destroy the module and the WebApp Library */
+static apr_status_t wam_child_destroy(void *data) {/*void *nil) { */
+    if (!wam_initialized) return APR_SUCCESS;
+    wa_shutdown();
+    wam_initialized=wa_false;
+    return APR_SUCCESS;
+}
+
+/* Startup the module and the WebApp Library */
+static void wam_child_init(apr_pool_t *p, server_rec *s) {
+    if (!wam_initialized) return;
+    server=s;
+    wa_startup();
+    apr_pool_cleanup_register(p, NULL, wam_child_destroy, apr_pool_cleanup_null);
+}
+
+/* Initialize the module and the WebApp Library */
+static const char *wam_init(apr_pool_t *p) {
+    const char *ret=NULL;
+
+    if(wam_initialized==wa_true) return(NULL);
+    if ((ret=wa_init())!=NULL) return(ret);
+    wam_initialized=wa_true;
+    return(NULL);
+}
+
+/* ************************************************************************* */
+/* CONFIGURATION DIRECTIVES HANDLING                                         */
+/* ************************************************************************* */
+
+/* Retrieve or create a wa_virtualhost structure for an Apache server_rec
+   and store it as the per-server module configuration */
+static const char *wam_server(server_rec *svr, wa_virtualhost **h) {
+    wa_virtualhost *host=NULL;
+    char *name=svr->server_hostname;
+    int port=(int)svr->port;
+    const char *ret=NULL;
+
+    /* Attempt to retrieve the wa_virtualhost structure and create it
+       if necessary, storing it into the server_rec structure. */
+    host=ap_get_module_config(svr->module_config,&webapp_module);
+
+    /* If we already configured the wa_virtualhost, simply return it */
+    if (host!=NULL) {
+        *h=host;
+        return(NULL);
+    }
+
+    /* The wa_virtualhost was not found in the per-server module configuration
+       so we'll try to create it. */
+    ret=wa_cvirtualhost(&host,name,port);
+    if (ret!=NULL) {
+        *h=NULL;
+        return(ret);
+    }
+
+    /* We successfully created a wa_virtualhost structure, store it in the
+       per-server configuration member and return it. */
+    ap_set_module_config(svr->module_config,&webapp_module,host);
+    *h=host;
+    return(NULL);
+}
+
+/* Process the WebAppConnection directive. */
+static const char *wam_directive_connection(cmd_parms *cmd, void *mconfig,
+                                            const char *name, const char *prov, const char *p) {
+    wa_connection *conn=NULL;
+    const char *ret=NULL;
+    wa_chain *elem=NULL;
+
+    /* Initialize the library */
+    if ((ret=wam_init(cmd->pool))!=NULL) return(ret);
+
+    /* Attempt to create a new wa_connection structure */
+    if ((ret=wa_cconnection(&conn,name,prov,p))!=NULL) return(ret);
+
+    /* Check if we have a duplicate connection with this name */
+    elem=wam_connections;
+    while (elem!=NULL) {
+        wa_connection *curr=(wa_connection *)elem->curr;
+        if (strcasecmp(conn->name,curr->name)==0)
+            return("Duplicate connection name");
+        elem=elem->next;
+    }
+
+    /* We don't have a duplicate connection, store it locally */
+    elem=apr_palloc(wa_pool,sizeof(wa_chain));
+    elem->curr=conn;
+    elem->next=wam_connections;
+    wam_connections=elem;
+    return(NULL);
+}
+
+/* Process the WebAppDeploy directive */
+static const char *wam_directive_deploy(cmd_parms *cmd, void *mconfig,
+                                      const char *name, const char *cnam, const char *path) {
+    wa_virtualhost *host=NULL;
+    wa_application *appl=NULL;
+    wa_connection *conn=NULL;
+    wa_chain *elem=NULL;
+    const char *ret=NULL;
+
+    /* Initialize the library and retrieve/create the host structure */
+    if ((ret=wam_init(cmd->pool))!=NULL) return(ret);
+    if ((ret=wam_server(cmd->server,&host))!=NULL) return(ret);
+
+    /* Retrieve the connection */
+    elem=wam_connections;
+    while(elem!=NULL) {
+        wa_connection *curr=(wa_connection *)elem->curr;
+        if (strcasecmp(curr->name,cnam)==0) {
+            conn=curr;
+            break;
+        }
+        elem=elem->next;
+    }
+    if (conn==NULL) return("Specified connection not configured");
+
+    /* Create a new wa_application member */
+    if ((ret=wa_capplication(&appl,name,path))!=NULL) return(ret);
+
+    /* Deploy the web application */
+    if ((ret=wa_deploy(appl,host,conn))!=NULL) return(ret);
+
+    /* Done */
+    return(NULL);
+}
+
+/* Process the WebAppInfo directive */
+static const char *wam_directive_info(cmd_parms *cmd, void *mconfig,
+                                      const char *path) {
+    const char *ret;
+
+    /* We simply divert this call to a WebAppConnection and a WebAppDeploy
+       calls */
+    if ((ret=wam_directive_connection(cmd,mconfig,"_INFO_","info",""))!=NULL)
+        return(ret);
+    if ((ret=wam_directive_deploy(cmd,mconfig,"_INFO_","_INFO_",path))!=NULL)
+        return(ret);
+
+    return(NULL);
+}
+
+/* The list of Directives for the WebApp module */
+static const command_rec wam_directives[] = {
+    AP_INIT_TAKE1(
+        "WebAppInfo",               /* directive name */
+        wam_directive_info,         /* config action routine */
+        NULL,                       /* argument to include in call */
+        OR_OPTIONS,                 /* where available */
+        "<uri-path>"),
+    AP_INIT_TAKE23(
+        "WebAppConnection",         /* directive name */
+        wam_directive_connection,   /* config action routine */
+        NULL,                       /* argument to include in call */
+        RSRC_CONF,                  /* where available */
+        "<name> <provider> [optional parameter]"),
+    AP_INIT_TAKE3(
+        "WebAppDeploy",             /* directive name */
+        wam_directive_deploy,       /* config action routine */
+        NULL,                       /* argument to include in call */
+        RSRC_CONF,                  /* where available */
+        "<name> <connection> <uri-path>"),
+    {NULL}
+
+};
+
+/* ************************************************************************* */
+/* CALLBACKS TO WEB SERVER                                                   */
+/* ************************************************************************* */
+
+/* Log a generic error */
+void wa_log(const char *f, const int l, const char *fmt, ...) {
+    va_list ap;
+    char buf[1024];
+#ifdef DEBUG
+    char tmp[1024];
+#endif
+
+    va_start(ap,fmt);
+#ifdef DEBUG
+    apr_vsnprintf(tmp,1024,fmt,ap);
+    apr_snprintf(buf,1024,"[%s:%d] %s",f,l,tmp);
+#else
+    apr_vsnprintf(buf,1024,fmt,ap);
+#endif
+    va_end(ap);
+
+    ap_log_error(f,l,APLOG_NOERRNO|APLOG_ERR,0,server,"%s",buf);
+}
+
+/* Log a message associated with a request */
+static void wam_handler_log(wa_request *r, const char *f, const int l, char *msg) {
+    request_rec *req=(request_rec *)r->data;
+    server_rec *svr=req->server;
+
+    ap_log_error(f,l,APLOG_NOERRNO|APLOG_ERR,0,svr,"%s",msg);
+}
+
+/* Set the HTTP status of the response. */
+static void wam_handler_setstatus(wa_request *r, int status, char *message) {
+    request_rec *req=(request_rec *)r->data;
+
+    if ((message!=NULL) && (message[0]!='\0'))
+        req->status_line=apr_psprintf(req->pool,"%03d %s", status, message);
+
+    req->status=status;
+}
+
+/* Set the MIME Content-Type of the response. */
+static void wam_handler_setctype(wa_request *r, char *type) {
+    request_rec *req=(request_rec *)r->data;
+
+    if (type==NULL) return;
+
+/*    req->content_type=apr_pstrdup(req->pool,type); */
+    /* It should be done like this in Apache 2.0 */
+    /* This way, Apache 2.0 will be able to set the output filter */
+    /* and it make webapp useable with deflate using AddOutputFilterByType DEFLATE text/html */
+    ap_set_content_type(req, apr_pstrdup(req->pool,type));
+    
+    apr_table_add(req->headers_out,"Content-Type",apr_pstrdup(req->pool,type));
+}
+
+/* Set a header in the HTTP response. */
+static void wam_handler_setheader(wa_request *r, char *name, char *value) {
+    request_rec *req=(request_rec *)r->data;
+
+    if (name==NULL) return;
+    if (value==NULL) value="";
+
+    apr_table_add(req->headers_out,apr_pstrdup(req->pool,name),
+                 apr_pstrdup(req->pool,value));
+}
+
+/* Commit the first part of the response (status and headers) */
+static void wam_handler_commit(wa_request *r) {
+#if 0
+/* Modules can completely forget about headers in Apache 2.0, the
+ * core server always makes sure they are sent at the correct time. rbb
+ */
+    request_rec *req=(request_rec *)r->data;
+
+    ap_send_http_header(req);
+    ap_rflush(req);
+#endif
+}
+
+/* Flush all data in the response buffer */
+static void wam_handler_flush(wa_request *r) {
+    request_rec *req=(request_rec *)r->data;
+
+    ap_rflush(req);
+}
+
+/* Read a chunk of text from the request body */
+static int wam_handler_read(wa_request *r, char *buf, int len) {
+    request_rec *req=(request_rec *)r->data;
+    long ret=0;
+
+    /* Check if we have something to read. */
+    if (r->clen==0) return(0);
+
+    /* Check if we had an error previously. */
+    if (r->rlen==-1) return(-1);
+
+    /* Send HTTP_CONTINUE to client when we're ready to read for the first
+       time. */
+    if (r->rlen==0) {
+        if (ap_should_client_block(req)==0) return(0);
+    }
+
+    /* Read some data from the client and fill the buffer. */
+    ret=ap_get_client_block(req,buf,len);
+    if (ret==-1) {
+        r->rlen=-1;
+        return(-1);
+    }
+
+    /* We did read some bytes, increment the current rlen and return. */
+    r->rlen+=ret;
+    return((int)ret);
+}
+
+/* Write a chunk of text into the response body. */
+static int wam_handler_write(wa_request *r, char *buf, int len) {
+    request_rec *req=(request_rec *)r->data;
+
+    return(ap_rwrite(buf, len, req));
+}
+
+/* The structure holding all callback handling functions for the library */
+static wa_handler wam_handler = {
+    wam_handler_log,
+    wam_handler_setstatus,
+    wam_handler_setctype,
+    wam_handler_setheader,
+    wam_handler_commit,
+    wam_handler_flush,
+    wam_handler_read,
+    wam_handler_write,
+};
+
+/* ************************************************************************* */
+/* REQUEST HANDLING                                                          */
+/* ************************************************************************* */
+
+/* Match an Apache request */
+static int wam_match(request_rec *r) {
+    wa_virtualhost *host=NULL;
+    wa_application *appl=NULL;
+    wa_chain *elem=NULL;
+
+    /* Paranoid check */
+    if (!wam_initialized) return(DECLINED);
+
+    /* Check if this host was recognized */
+    host=ap_get_module_config(r->server->module_config,&webapp_module);
+    if (host==NULL) return(DECLINED);
+
+    /* Check if the uri is contained in one of our applications root path */
+    elem=host->apps;
+    while(elem!=NULL) {
+        appl=(wa_application *)elem->curr;
+        if (strncmp(appl->rpth,r->uri,strlen(appl->rpth))==0) break;
+
+        appl=NULL;
+        elem=elem->next;
+    }
+    if (appl==NULL) return(DECLINED);
+
+    /* The uri path is matched: set the handler and return */
+    r->handler=apr_pstrdup(r->pool,"webapp-handler");
+    apr_table_setn(r->notes,"webapp-handler", "webapp-handler");
+
+    /* Set the webapp request structure into Apache's request structure */
+    ap_set_module_config(r->request_config, &webapp_module, appl);
+    return(OK);
+}
+
+/* Handle the current request */
+static int wam_invoke(request_rec *r) {
+    server_rec *svr=r->server;
+    conn_rec *con=r->connection;
+    wa_application *appl=NULL;
+    wa_request *req=NULL;
+    const char *msg=NULL;
+    char *stmp=NULL;
+    char *ctmp=NULL;
+    int ret=0;
+    apr_port_t port;
+
+    if (strcmp(r->handler, "webapp-handler")) return(DECLINED);
+
+    /* Paranoid check */
+    if (!wam_initialized) return(DECLINED);
+
+    /* Try to get a hold on the webapp request structure */
+    appl=(wa_application *)ap_get_module_config(r->request_config,
+                                                &webapp_module);
+    if (appl==NULL) return(DECLINED);
+
+    /* Allocate the webapp request structure */
+    if ((msg=wa_ralloc(&req, &wam_handler, r))!=NULL) {
+        ap_log_error(APLOG_MARK,APLOG_NOERRNO|APLOG_ERR,0,svr,"%s",msg);
+        return(HTTP_INTERNAL_SERVER_ERROR);
+    }
+
+    /* Set up the WebApp Library request structure client and server host
+       data (from the connection */
+    stmp=(char *)r->hostname;
+    if (stmp==NULL) req->serv->host="";
+    else req->serv->host=apr_pstrdup(req->pool,stmp);
+
+    ctmp=(char *)ap_get_remote_host(con,r->per_dir_config, REMOTE_NAME, NULL);
+    if (ctmp==NULL) req->clnt->host="";
+    else req->clnt->host=apr_pstrdup(req->pool,ctmp);
+
+    req->serv->addr=apr_pstrdup(req->pool,con->local_ip);
+    req->clnt->addr=apr_pstrdup(req->pool,con->remote_ip);
+    apr_sockaddr_port_get(&port, con->local_addr);
+    req->serv->port= (int) port;
+    apr_sockaddr_port_get(&port, con->remote_addr);
+    req->clnt->port= (int) port;
+
+    /* Set up all other members of the request structure */
+    req->meth=apr_pstrdup(req->pool,(char *)r->method);
+    req->ruri=apr_pstrdup(req->pool,r->uri);
+    req->args=apr_pstrdup(req->pool,r->args);
+    req->prot=apr_pstrdup(req->pool,r->protocol);
+    req->schm=apr_pstrdup(req->pool,ap_http_method(r));
+    req->user=apr_pstrdup(req->pool,r->user);
+    req->auth=apr_pstrdup(req->pool,r->ap_auth_type);
+    req->clen=0;
+    req->ctyp="\0";
+    req->rlen=0;
+
+    /* Copy headers into webapp request structure */
+    if (r->headers_in!=NULL) {
+        apr_array_header_t *arr=apr_table_elts(r->headers_in);
+        apr_table_entry_t *ele=(apr_table_entry_t *)arr->elts;
+        int x=0;
+
+        /* Copy headers one by one */
+        for (x=0; x<arr->nelts;x++) {
+            if (ele[x].key==NULL) continue;
+            if (ele[x].val==NULL) continue;
+            apr_table_add(req->hdrs,apr_pstrdup(req->pool,ele[x].key),
+                                    apr_pstrdup(req->pool,ele[x].val));
+            if (strcasecmp(ele[x].key,"Content-Length")==0)
+                req->clen=atol(ele[x].val);
+            if (strcasecmp(ele[x].key,"Content-Type")==0)
+                req->ctyp=apr_pstrdup(req->pool,ele[x].val);
+        }
+    }
+
+    /* Check if we can read something from the request */
+    ret=ap_setup_client_block(r,REQUEST_CHUNKED_DECHUNK);
+    if (ret!=OK) return(ret);
+
+    /* Invoke the request */
+    ret=wa_rinvoke(req,appl);
+
+    /* Destroy the request member */
+    wa_rfree(req);
+    ap_rflush(r);
+
+    return(OK);
+}
+
+/* bypass the directory_walk and file_walk for non-file requests */
+static int wam_map_to_storage(request_rec *r)
+{
+    if (apr_table_get(r->notes, "webapp-handler")) {
+        r->filename = (char *)apr_filename_of_pathname(r->uri);
+        return OK;
+    }
+    return DECLINED;
+}
+
+static void wam_hooks(apr_pool_t *p)
+{
+    ap_hook_post_config(wam_module_init, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_child_init(wam_child_init, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_map_to_storage(wam_map_to_storage, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_handler(wam_invoke, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_translate_name(wam_match, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+/* Apache module declaration */
+module AP_MODULE_DECLARE_DATA webapp_module = {
+    STANDARD20_MODULE_STUFF,
+    NULL,                               /* per-directory config creator */
+    NULL,                               /* dir config merger */
+    NULL,                               /* server config creator */
+    NULL,                               /* server config merger */
+    wam_directives,                     /* command table */
+    wam_hooks,
+};
diff --git a/connectors/webapp/build.properties.in b/connectors/webapp/build.properties.in
new file mode 100644
index 0000000..e7883ae
--- /dev/null
+++ b/connectors/webapp/build.properties.in
@@ -0,0 +1,31 @@
+#
+# Copyright 1999-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# The full path of the sources directory
+# (defaults to the directory where build.xml resides)
+basedir=@SRC_DIR@
+
+# The full path of the targets directory
+# (defaults to the current directory)
+targdir=@TGT_DIR@
+
+# The version of the WebApp module
+# (defaults to "unknown")
+version=@WEBAPP_VERSION@
+
+# The full path of a Tomcat 4.x distribution
+# (no default - compilation will fail)
+catalina.home=@TC4_DIR@
diff --git a/connectors/webapp/build.xml b/connectors/webapp/build.xml
new file mode 100644
index 0000000..5b6b51c
--- /dev/null
+++ b/connectors/webapp/build.xml
@@ -0,0 +1,269 @@
+<?xml version="1.0"?>
+
+<project name="Webapp Module" default="all" basedir=".">
+
+<!-- === BUILD ENVIRONMENT =============================================== -->
+
+  <!-- ANT FIX: basedir is alwats relative to build.xml -->
+  <property name="basedir"        value="${basedir}"/>
+  <property name="targdir"        value="${user.dir}"/>
+
+  <!-- The properties file which will override what's written below -->
+  <property file="${targdir}/build.properties"/>
+
+  <!-- The WebApp version string -->
+  <property name="version"        value="unknown"/>
+
+  <!-- The layout of the build directory (shared with Makefile(s) -->
+  <property name="build.home"     value="${targdir}/build"/>
+  <property name="build.jar"      value="${build.home}/tomcat-warp.jar"/>
+  <property name="build.classes"  value="${build.home}/classes"/>
+  <property name="build.docs"     value="${build.home}/docs"/>
+  <property name="build.javadoc"  value="${build.docs}/api-java"/>
+
+  <!-- The layout of the sources directory -->
+  <property name="source.home"    value="${basedir}"/>
+  <property name="source.java"    value="${source.home}/java"/>
+  <property name="source.docs"    value="${source.home}/docs"/>
+
+  <!-- Classpath where we need to find stuff (no matter what) -->
+  <path id="classpath">
+    <pathelement location="${build.classes.dir}"/>
+    <pathelement location="${catalina.home}/server/lib/catalina.jar"/>
+    <pathelement location="${catalina.home}/common/lib/servlet.jar"/>
+    <pathelement location="${catalina.home}/common/lib/servlet-api.jar"/>
+  </path>
+
+<!-- === PREPARATION AND GLOBAL TASKS ==================================== -->
+
+  <target
+      name="init"
+      description="Dump some messages for Make">
+    
+    <!-- Dump some messages about what we're going to do -->
+    <echo message="Building ${ant.project.name} (version ${version})"/>
+    <echo message="- source path: ${basedir}"/>
+    <echo message="- target path: ${targdir}"/>
+  </target>
+
+  <!--
+    Prepare the build directory creating all its subdirectories
+    and check for Tomcat 4 and Xalan in the classpath
+  -->
+  <target
+      name="prepare"
+      description="Prepare the build tree and check classes">
+
+    <!-- Create some directories -->
+    <mkdir dir="${build.home}"/>
+
+    <!-- Check if we can find Container class in the classpath -->
+    <available
+        property="avail.tomcat"
+        classname="org.apache.catalina.Container">
+      <classpath refid="classpath"/>
+    </available>
+
+    <!-- Check if we can find the XSLTProcessor class in the classpath -->
+    <available
+        property="avail.xalan"
+        classname="org.apache.xalan.xslt.Process">
+      <classpath refid="classpath"/>
+    </available>
+  </target>
+
+  <!--
+    Do-everything target: We want to build docs, javadocs, and compile
+    our sources all in one step...
+  -->
+  <target
+      name="all"
+      description="Build everything"
+      depends="compile,docs,javadoc"/>
+
+  <!--
+    Clean up all we generated using ANT (and nothing more)
+  -->
+  <target
+      name="clean"
+      description="Clean up what we built"
+      depends="compile.clean,docs.clean,javadoc.clean"/>
+
+
+<!-- === COMPILATION TASKS =============================================== -->
+
+  <!--
+    Check if we found the Tomcat 4.0 classes in our classpath and fail
+    miserably (don't even try to compile) if we didn't.
+  -->
+  <target
+      name="compile.check"
+      depends="prepare"
+      description="Fail build if we don't find Tomcat 4.0"
+      unless="avail.tomcat">
+    
+    <!-- Just jump out -->
+    <fail message="Cannot find Tomcat 4.0 classes"/>
+  </target>
+
+  <!--
+    Delete the generated classes and JAR file.
+  -->
+  <target
+      name="compile.clean"
+      description="Clean up the JAR file and all classes">
+    <delete file="${build.jar}"/>
+    <delete dir="${build.classes}"/>
+  </target>
+
+
+  <!--
+    Create the directory where classes will be compiled into, then compile
+    our sources, copy over all non-java files, and build up the final JAR.
+  -->
+  <target
+      name="compile"
+      depends="compile.check"
+      description="Compile the WARP connector"
+      if="avail.tomcat">
+
+    <!-- Create the directory where we're going to store the classes -->
+    <mkdir dir="${build.classes}"/>
+
+    <!-- Compile our sources -->
+    <javac
+        srcdir="${source.java}"
+        destdir="${build.classes}"
+        debug="${compile.debug}"
+        deprecation="${compile.deprecation}">
+      <classpath refid="classpath"/>
+    </javac>
+
+    <!-- Copy all non-java files into the target directory -->
+    <copy
+        todir="${build.classes}">
+      <fileset
+          dir="${source.java}">
+        <exclude name="**/*.java"/>
+      </fileset>
+    </copy>
+
+    <!-- Build up our JAR file -->
+    <jar
+        jarfile="${build.jar}"
+        basedir="${build.classes}" />
+  </target>
+
+
+<!-- === DOCUMENT GENERATION TASKS ======================================= -->
+
+  <!--
+    Check if we found Xalan in our classpath. We require Xalan because it has
+    some nifty functions that we use throughout the XSLT (and also because
+    we want people to eat our own food, right?)
+  -->
+  <target
+      name="docs.check"
+      depends="prepare"
+      description="Fail if we don't find Xalan"
+      unless="avail.xalan">
+    
+    <!-- Just jump out -->
+    <fail message="Cannot find the Apache Xalan XSLT processor"/>
+  </target>
+
+  <!--
+    Delete the generated documentation
+  -->
+  <target
+      name="docs.clean"
+      description="Clean up the generated docs directory">
+    <delete dir="${build.docs}"/>
+  </target>
+
+  <!--
+    Generate documentation from the XML sources.
+  -->
+  <target
+      name="docs"
+      depends="docs.check"
+      description="Create Documentation">
+
+    <!-- Create the directory where we're going to store the docs -->
+    <mkdir dir="${build.docs}"/>
+
+    <!-- Add some style to our otherwise  utterly ugly XML files -->
+    <style
+        basedir="${source.docs}"
+        destdir="${build.docs}"
+        style="${source.docs}/style.xsl"
+        includes="**.xml"/>
+
+    <!-- Copy all relevant (non processed) files from the sources -->
+    <copy
+        todir="${build.docs}" >
+      <fileset dir="${source.docs}">
+        <exclude name="**.xml"/>
+        <exclude name="**.xsl"/>
+        <exclude name="**.idx"/>
+        <exclude name="**/images/originals/**"/>
+      </fileset>
+    </copy>
+  </target>
+
+
+<!-- === JAVADOC TASKS =================================================== -->
+
+  <!--
+    Check if we found the Tomcat 4.0 classes in our classpath and echo a
+    message if we didn't find it (warnings are allright, ugly, but OK).
+  -->
+  <target
+      name="javadoc.check"
+      depends="prepare"
+      description="Warn if we didn't find Tomcat 4.0"
+      unless="avail.tomcat">
+    
+    <!-- Just output a simple warning message -->
+    <echo message="Cannot find Tomcat 4.0. Warnings in JavaDOC are ok"/>
+  </target>
+
+  <!--
+    Delete the generated JavaDOC output
+  -->
+  <target
+      name="javadoc.clean"
+      description="Clean up the generated javadoc directory">
+    <delete dir="${build.javadoc}"/>
+  </target>
+
+  <!--
+    Run JavaDOC over our set of java sources.
+  -->
+  <target
+      name="javadoc"
+      depends="prepare"
+      description="Create Java API documentation">
+
+    <!-- Create the directory where we're going to store the docs -->
+    <mkdir dir="${build.docs}"/>
+    <mkdir dir="${build.javadoc}"/>
+
+    <!-- Run JavaDoc -->
+    <javadoc
+        sourcepath="${source.java}"
+        destdir="${build.javadoc}"
+        packagenames="org.apache.catalina.connector.warp"
+        author="true"
+        private="false"
+        version="true"
+        doctitle="&lt;h1&gt;${component.title}&lt;/h1&gt;"
+        windowtitle="${component.title} (Version ${component.version})"
+        bottom="Copyright (c) 2001-2002 - Apache Software Foundation">
+      <classpath refid="classpath"/>
+    </javadoc>
+  </target>
+
+</project>
+
+
diff --git a/connectors/webapp/configure.in b/connectors/webapp/configure.in
new file mode 100644
index 0000000..ff33ae6
--- /dev/null
+++ b/connectors/webapp/configure.in
@@ -0,0 +1,442 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl Author Pier Fumagalli <mailto:pier@betaversion.org>
+dnl Version $Id$
+dnl 
+dnl NOTE: close all functions with ]) on the same line otherwise M4 will spit
+dnl       out the last argument of the function with an extra newline.
+dnl --------------------------------------------------------------------------
+
+sinclude(./support/wa_util.m4)
+sinclude(./support/wa_exec.m4)
+sinclude(./support/wa_version.m4)
+sinclude(./support/wa_apr.m4)
+sinclude(./support/wa_apxs.m4)
+sinclude(./support/wa_ant.m4)
+sinclude(./support/wa_perl.m4)
+
+dnl --------------------------------------------------------------------------
+dnl Initialize GNU Autoconf (note, this requires autoconf vers. >= 2.52)
+dnl --------------------------------------------------------------------------
+AC_INIT([WebApp Module],[""],[http://nagoya.apache.org/bugzilla])
+AC_CONFIG_SRCDIR([configure.in])
+AC_CONFIG_AUX_DIR([support])
+
+dnl --------------------------------------------------------------------------
+dnl Get rid of all that "--prefix" ... "--xxxdir" in the help message
+dnl --------------------------------------------------------------------------
+WA_HEADER([Configuring AC_PACKAGE_STRING])
+WA_HELP()
+
+dnl --------------------------------------------------------------------------
+dnl What are we compiling for
+dnl --------------------------------------------------------------------------
+AC_CANONICAL_HOST()
+
+dnl --------------------------------------------------------------------------
+dnl Directories locations are defined here
+dnl --------------------------------------------------------------------------
+WA_VARIABLE([APR_DIR])
+WA_VARIABLE([APR_LIB])
+WA_VARIABLE([APR_LIBNAME])
+WA_VARIABLE([SRC_DIR])
+WA_VARIABLE([TGT_DIR])
+WA_VARIABLE([TC4_DIR])
+WA_PATH_DIR([SRC_DIR],[${srcdir}],[sources])
+WA_PATH_DIR([TGT_DIR],[.],[build])
+
+dnl --------------------------------------------------------------------------
+dnl Define  the variables we need in Makedefs.in
+dnl --------------------------------------------------------------------------
+WA_VARIABLE([CC])
+WA_VARIABLE([CPP])
+WA_VARIABLE([SHELL])
+WA_VARIABLE([LIBTOOL])
+WA_VARIABLE([APXS])
+WA_VARIABLE([ANT])
+WA_VARIABLE([PERL])
+
+WA_VARIABLE([CPPFLAGS])
+WA_VARIABLE([CFLAGS])
+WA_VARIABLE([LDFLAGS])
+WA_VARIABLE([INCLUDES])
+INCLUDES="-I. -I${SRC_DIR}/include"
+
+WA_VARIABLE([EXTRA_CFLAGS])
+WA_VARIABLE([EXTRA_CPPFLAGS])
+WA_VARIABLE([EXTRA_LDFLAGS])
+WA_VARIABLE([EXTRA_INCLUDES])
+WA_VARIABLE([EXTRA_BUILD])
+WA_VARIABLE([EXTRA_CLEAN])
+
+WA_VARIABLE([MODULE])
+
+WA_VARIABLE([APACHE_VERSION])
+WA_VARIABLE([WEBAPP_VERSION])
+WA_VARIABLE([HOST])
+WA_VARIABLE([DATE])
+HOST="${host}"
+DATE="`date`"
+
+WA_VARIABLE([ANT_TARGETS])
+
+dnl --------------------------------------------------------------------------
+dnl Figure out if we have APXS, where it's located, and what version it is
+dnl --------------------------------------------------------------------------
+WA_HEADER([Checking Apache APXS])
+WA_APXS([APXS])
+WA_APXS_CHECK([APXS_VERSION],[${APXS}])
+
+dnl --------------------------------------------------------------------------
+dnl Decide based on what version of APXS we found
+dnl --------------------------------------------------------------------------
+if test "${APXS_VERSION}" = "1.3" ; then
+
+    dnl ----------------------------------------------------------------------
+    dnl APXS version 1.3, meaning Apache 1.3. We _require_ APR sources
+    dnl ----------------------------------------------------------------------
+    WA_HEADER([Apache 1.3 module compilation (APR checks)])
+
+    dnl ----------------------------------------------------------------------
+    dnl Check where the APR sources are and issue "configure"
+    dnl ----------------------------------------------------------------------
+    WA_APR([APR_DIR])
+    tempret="0"
+    tempval="--enable-static --disable-shared --disable-threads"
+    tempval="${tempval} --libdir=${TGT_DIR}/objects"
+    WA_EXEC(
+        [tempret],
+        [./configure ${tempval}],
+        [apr],
+        [${APR_DIR}])
+    AC_MSG_CHECKING([for proper command execution])
+    if test "${tempret}" = "0" ; then
+        AC_MSG_RESULT([ok (${tempret})])
+    else
+        WA_ERROR([command returned with ${tempret}])
+    fi
+    unset tempret
+    unset tempval
+
+    dnl ----------------------------------------------------------------------
+    dnl Retrieve APR variables
+    dnl ----------------------------------------------------------------------
+    WA_APR_GET([CC],[${APR_DIR}],[CC])
+    WA_APR_GET([CPP],[${APR_DIR}],[CPP])
+    WA_APR_GET([SHELL],[${APR_DIR}],[SHELL])
+
+    WA_APR_GET([CFLAGS],[${APR_DIR}],[CFLAGS])
+    WA_APR_GET([CPPFLAGS],[${APR_DIR}],[CPPFLAGS])
+    WA_APR_GET([LDFLAGS],[${APR_DIR}],[LDFLAGS])
+
+    WA_APR_GET([LDFLAGS],[${APR_DIR}],[LIBS])
+    WA_APR_GET([CPPFLAGS],[${APR_DIR}],[EXTRA_INCLUDES])
+
+    WA_APR_LIB([APR_LIB],[${APR_DIR}])
+    WA_APR_LIBNAME([APR_LIBNAME],[${APR_DIR}])
+
+    AC_MSG_CHECKING([for apr headers])
+    WA_APPEND([INCLUDES],[-I${APR_DIR}/include])
+    AC_MSG_RESULT([-I${APR_DIR}/include])
+
+    AC_MSG_CHECKING([for apr libtool])
+    LIBTOOL="${SHELL} ${APR_DIR}/libtool --silent"
+    AC_MSG_RESULT([${LIBTOOL}])
+
+    dnl ----------------------------------------------------------------------
+    dnl Done with APR, let's see what APXS says
+    dnl ----------------------------------------------------------------------
+    WA_HEADER([Apache 1.3 module compilation (APXS checks)])
+
+    dnl ----------------------------------------------------------------------
+    dnl Do a check on the C compiler to see if apxs reports the same one
+    dnl ----------------------------------------------------------------------
+    WA_APXS_GET([tempval],[${APXS}],[CC])
+    AC_MSG_CHECKING([for compiler coherency])
+    if test "${tempval}" != "${CC}" ; then
+      AC_MSG_RESULT([error])
+      AC_MSG_RESULT([compiler discovered by apr: ${CC}])
+      AC_MSG_RESULT([compiler used by apache apxs: ${tempval}])
+      AC_MSG_ERROR([the apache apxs and apr compilers must be the same])
+      exit 1
+    fi
+    AC_MSG_RESULT([ok (${CC})])
+    unset tempval
+
+    dnl ----------------------------------------------------------------------
+    dnl Apache 1.3 configuration complete
+    dnl ----------------------------------------------------------------------
+    MODULE="apache-1.3"
+    WA_APPEND([EXTRA_BUILD],[apr-build])
+    WA_APPEND([EXTRA_CLEAN],[apr-clean])
+
+    WA_APXS_GET([tempval],[${APXS}],[INCLUDEDIR])
+    WA_PATH_DIR([tempdir],[${tempval}],[apache 1.3 headers])
+    WA_APPEND([INCLUDES],[-I${tempdir}])
+    unset tempval
+    unset tempdir
+    WA_APXS_GET([EXTRA_CFLAGS],[${APXS}],[CFLAGS])
+    WA_APXS_GET([EXTRA_CFLAGS],[${APXS}],[CFLAGS_SHLIB])
+    WA_APXS_GET([EXTRA_LDFLAGS],[${APXS}],[LIBS_SHLIB])
+    WA_APXS_GET([EXTRA_LDFLAGS],[${APXS}],[LDFLAGS_SHLIB])
+
+else
+
+    dnl ----------------------------------------------------------------------
+    dnl APXS version 2.0, meaning Apache 2.0. We use APR from there
+    dnl ----------------------------------------------------------------------
+    WA_HEADER([Apache 2.0 module])
+
+    WA_APXS_GET([CC],[${APXS}],[CC])
+    WA_APXS_GET([CPP],[${APXS}],[CPP])
+    WA_APXS_GET([SHELL],[${APXS}],[SHELL])
+    WA_APXS_GET([LIBTOOL],[${APXS}],[LIBTOOL])
+
+    WA_APXS_GET([CPPFLAGS],[${APXS}],[CPPFLAGS])
+    WA_APXS_GET([CPPFLAGS],[${APXS}],[NOTEST_CPPFLAGS])
+    WA_APXS_GET([CPPFLAGS],[${APXS}],[EXTRA_CPPFLAGS])
+
+    WA_APXS_GET([LDFLAGS],[${APXS}],[LDFLAGS])
+    WA_APXS_GET([LDFLAGS],[${APXS}],[NOTEST_LDFLAGS])
+    WA_APXS_GET([LDFLAGS],[${APXS}],[EXTRA_LDFLAGS])
+
+    WA_APXS_GET([CFLAGS],[${APXS}],[CFLAGS])
+    WA_APXS_GET([CFLAGS],[${APXS}],[NOTEST_CFLAGS])
+    WA_APXS_GET([CFLAGS],[${APXS}],[EXTRA_CFLAGS])
+
+    WA_APXS_GET([tempval],[${APXS}],[INCLUDEDIR])
+    WA_PATH_DIR([tempdir],[${tempval}],[apache 2.0 headers])
+    WA_APPEND([INCLUDES],[-I${tempdir}])
+    unset tempval
+    unset tempdir
+
+    MODULE="apache-2.0"
+fi
+
+dnl --------------------------------------------------------------------------
+dnl Retrieve Apache and WebApp version
+dnl --------------------------------------------------------------------------
+WA_HEADER([Checking component versions])
+WA_APXS_SERVER([APACHE_VERSION],[${APXS}])
+WA_VERSION([WEBAPP_VERSION],[${CC}])
+
+dnl --------------------------------------------------------------------------
+dnl Detect another couple of binaries we _might_ require for compilation
+dnl --------------------------------------------------------------------------
+WA_HEADER([Checking optional binaries])
+WA_PERL([PERL])
+WA_ANT([ANT])
+
+dnl --------------------------------------------------------------------------
+dnl Optional targets
+dnl --------------------------------------------------------------------------
+WA_HEADER([Optional targets])
+
+dnl --------------------------------------------------------------------------
+dnl Do we want to build also tomcat-webapp.jar or not?
+dnl --------------------------------------------------------------------------
+AC_MSG_CHECKING([for java compilation])
+AC_ARG_ENABLE(
+    [java],
+    [  --enable-java[[=tc4dir]]  compile java code (tc4dir must be specifed)],
+    [
+        case "${enableval}" in
+        ""|"yes"|"YES"|"true"|"TRUE")
+            WA_ERROR([path of Tomcat 4.0 binary distribution unspecified])
+            ;;
+        "no"|"NO"|"false"|"FALSE")
+            AC_MSG_RESULT([no])
+            ;;
+        *)
+            dnl --------------------------------------------------------------
+            dnl We were given something as a parameter, we need to make sure
+            dnl that what we were given is the path to the Tomcat 4.0 distrib.
+            dnl --------------------------------------------------------------
+            if test -z "${ANT}" ; then
+                WA_ERROR([java compilation desired but ANT not found])
+            fi
+            AC_MSG_RESULT([yes])
+
+            WA_PATH_DIR([TC4_DIR],[${enableval}],[tomcat 4.0 distribution])
+            AC_MSG_CHECKING([for proper tomcat layout])
+            if test ! -f "${TC4_DIR}/server/lib/catalina.jar" ; then
+              WA_ERROR([cannot find "${TC4_DIR}/server/lib/catalina.jar"])
+            fi
+            if test ! -f "${TC4_DIR}/common/lib/servlet.jar" ; then
+              WA_ERROR([cannot find "${TC4_DIR}/common/lib/servlet.jar"])
+            fi
+
+            dnl --------------------------------------------------------------
+            dnl We found what we were looking for, make sure ANT gets called
+            dnl with the "compile" target
+            dnl --------------------------------------------------------------
+            WA_APPEND([ANT_TARGETS],[compile])
+            WA_APPEND([EXTRA_BUILD],[ant-build])
+            WA_APPEND([EXTRA_CLEAN],[ant-clean])
+            AC_MSG_RESULT([ok])
+            ;;
+        esac
+    ],[
+        AC_MSG_RESULT([no])
+    ])
+
+dnl --------------------------------------------------------------------------
+dnl Do we want to build also the documentation from XML?
+dnl --------------------------------------------------------------------------
+AC_MSG_CHECKING([for documentation generation])
+AC_ARG_ENABLE(
+    [docs],
+    [  --enable-docs           generate documentation (requires ANT)],
+    [
+        case "${enableval}" in
+        ""|"yes"|"YES"|"true"|"TRUE")
+            if test -z "${ANT}" ; then
+                WA_ERROR([docs generation desired but ANT not found])
+            fi
+            AC_MSG_RESULT([yes])
+
+            dnl --------------------------------------------------------------
+            dnl We were specifically requested to compile docs, and we found
+            dnl ANT, we need to call it with the "docs" target
+            dnl --------------------------------------------------------------
+            WA_APPEND([ANT_TARGETS],[docs])
+            WA_APPEND([EXTRA_BUILD],[ant-build])
+            WA_APPEND([EXTRA_CLEAN],[ant-clean])
+            ;;
+        "no"|"NO"|"false"|"FALSE")
+            AC_MSG_RESULT([no])
+            ;;
+        *)
+            WA_ERROR([invalid parameter --enable-docs="${enableval}"])
+            ;;
+        esac
+    ],[
+        dnl ------------------------------------------------------------------
+        dnl We are going to call ANT only if we found it, but making sure
+        dnl that we specify the "docs" target
+        dnl ------------------------------------------------------------------
+        if test -z "${ANT}" ; then
+            AC_MSG_RESULT([no (ant required)])
+        else
+            AC_MSG_RESULT([yes (ant detected)])
+            WA_APPEND([ANT_TARGETS],[docs])
+            WA_APPEND([EXTRA_BUILD],[ant-build])
+            WA_APPEND([EXTRA_CLEAN],[ant-clean])
+        fi
+    ])
+
+dnl --------------------------------------------------------------------------
+dnl Do we want to build also the Java API documentation?
+dnl --------------------------------------------------------------------------
+AC_MSG_CHECKING([for Java API docs generation])
+AC_ARG_ENABLE(
+    [apidoc-java],
+    [  --enable-apidoc-java    generate Java API docs (requires ANT)],
+    [
+        case "${enableval}" in
+        ""|"yes"|"YES"|"true"|"TRUE")
+            if test -z "${ANT}" ; then
+                WA_ERROR([docs generation desired but ANT not found])
+            fi
+            AC_MSG_RESULT([yes])
+
+            dnl --------------------------------------------------------------
+            dnl We were specifically requested to create javadocs, and we
+            dnl found ANT, we need to call it with the "javadoc" target
+            dnl --------------------------------------------------------------
+            WA_APPEND([ANT_TARGETS],[javadoc])
+            WA_APPEND([EXTRA_BUILD],[ant-build])
+            WA_APPEND([EXTRA_CLEAN],[ant-clean])
+            ;;
+        "no"|"NO"|"false"|"FALSE")
+            AC_MSG_RESULT([no])
+            ;;
+        *)
+            WA_ERROR([invalid parameter --enable-apidoc-java="${enableval}"])
+            ;;
+        esac
+    ],[
+        dnl ------------------------------------------------------------------
+        dnl We are going to call ANT only if we found it, but making sure
+        dnl that we specify the "javadoc" target
+        dnl ------------------------------------------------------------------
+        if test -z "${ANT}" ; then
+            AC_MSG_RESULT([no (ant required)])
+        else
+            AC_MSG_RESULT([yes (ant detected)])
+            WA_APPEND([ANT_TARGETS],[javadoc])
+            WA_APPEND([EXTRA_BUILD],[ant-build])
+            WA_APPEND([EXTRA_CLEAN],[ant-clean])
+        fi
+    ])
+
+dnl --------------------------------------------------------------------------
+dnl Do we want to build also the C API documentation?
+dnl --------------------------------------------------------------------------
+AC_MSG_CHECKING([for C API docs generation])
+AC_ARG_ENABLE(
+    [apidoc-c],
+    [  --enable-apidoc-c       generate C API docs (requires PERL)],
+    [
+        case "${enableval}" in
+        ""|"yes"|"YES"|"true"|"TRUE")
+            if test -z "${PERL}" ; then
+                WA_ERROR([docs generation desired but PERL not found])
+            fi
+            AC_MSG_RESULT([yes])
+
+            dnl --------------------------------------------------------------
+            dnl We were specifically requested to create c api docs, and we
+            dnl found PERL, we need to define a couple of new targets
+            dnl --------------------------------------------------------------
+            WA_APPEND([EXTRA_BUILD],[capi-build])
+            WA_APPEND([EXTRA_CLEAN],[capi-clean])
+            ;;
+        "no"|"NO"|"false"|"FALSE")
+            AC_MSG_RESULT([no])
+            ;;
+        *)
+            WA_ERROR([invalid parameter --enable-apidoc-c="${enableval}"])
+            ;;
+        esac
+    ],[
+        dnl ------------------------------------------------------------------
+        dnl We are going to call the C Api generation/cleanup targets only if
+        dnl we found PERL
+        dnl ------------------------------------------------------------------
+        if test -z "${PERL}" ; then
+            AC_MSG_RESULT([no (perl required)])
+        else
+            AC_MSG_RESULT([yes (perl detected)])
+            WA_APPEND([EXTRA_BUILD],[capi-build])
+            WA_APPEND([EXTRA_CLEAN],[capi-clean])
+        fi
+    ])
+
+dnl --------------------------------------------------------------------------
+dnl All done
+dnl --------------------------------------------------------------------------
+WA_HEADER([All done])
+AC_OUTPUT(
+  [
+    Makedefs
+    Makefile
+    lib/Makefile
+    ${MODULE}/Makefile
+    build.properties
+  ])
diff --git a/connectors/webapp/docs/.cvsignore b/connectors/webapp/docs/.cvsignore
new file mode 100644
index 0000000..2c48ca8
--- /dev/null
+++ b/connectors/webapp/docs/.cvsignore
@@ -0,0 +1,3 @@
+.DS_Store
+.FBCIndex
+.FBCLockFolder
diff --git a/connectors/webapp/docs/building.xml b/connectors/webapp/docs/building.xml
new file mode 100644
index 0000000..d91ffba
--- /dev/null
+++ b/connectors/webapp/docs/building.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+
+<document title="Building WebApp">
+  <description>
+    Building the WebApp Module
+  </description>
+
+  <section title="Windows Users">
+    <description>
+      Note for Microsoft Windows operating system users
+    </description>
+
+    <p>
+      <b>Nope, the WebApp module still doesn't run under Windows and currently 
+      any Microsoft operating system is not supported.</b> Since the past 
+      release, though, many improvements were made, and we had positive repors 
+      that the WebApp module actually tries to work also in that environment.
+    </p>
+
+    <p>
+      The effort to bring WebApp under Microsoft Windows lacks of active 
+      developers, people (<b>like you</b>) who have access to this set of 
+      operating systems (from Windows 98 to XP), have access to their 
+      development tools and can make the difference.
+    </p>
+
+    <p>
+      Multi-threaded support has now been added since the multi threaded 
+      environment is now available also under UNIX based operating systems via 
+      Apache 2.0, therefore the only things left to do are a little bit of 
+      porting (not much, since the WebApp module is based on the 
+      <a href="http://apr.apache.org/">Apache Portable Runtime</a>
+      library, which does most of the job for you) and a lot of testing.
+    </p>
+
+    <p>
+      <b>You</b> will be more than welcome to join the effort.
+    </p>
+  </section>
+
+  <section title="Sources or CVS">
+    <description>
+      Getting a hold on the sources: tarball or CVS?
+    </description>
+
+    <todo/>
+  </section>
+
+  <section title="Prerequisites">
+    <description>
+      What other software you need to have to build the module
+    </description>
+
+    <todo/>
+  </section>
+
+</document>
diff --git a/connectors/webapp/docs/cvs.xml b/connectors/webapp/docs/cvs.xml
new file mode 100644
index 0000000..64a737b
--- /dev/null
+++ b/connectors/webapp/docs/cvs.xml
@@ -0,0 +1,184 @@
+<?xml version="1.0"?>
+
+<document title="CVS Access">
+  <description>
+    How to download the WebApp sources from CVS
+  </description>
+
+  <section title="Environment">
+    <description>
+      Setting up your CVS work environment
+    </description>
+
+    <p>
+      First of all set up your CVSROOT environment variable and log in onto 
+      our CVS server. When prompted for a password, you can enter <b>anoncvs</b>:
+    </p>
+
+    <screen>
+      <note>if you're using CSH/TCSH</note>
+      <type>setenv CVSROOT :pserver:anoncvs@apache.org:/home/cvspublic</type>
+      <type/>
+
+      <note>if you're using SH/BASH</note>
+      <type>CVSROOT=":pserver:anoncvs@apache.org:/home/cvspublic</type>
+      <type>export CVSROOT</type>
+      <type/>
+
+      <note>and then log in on the CVS server</note>
+      <type>cvs login</type>
+      <read>(Logging in to anoncvs@cvs.apache.org)</read>
+      <read>CVS password: <enter>anoncvs</enter></read>
+      <type/>
+    </screen>
+
+  <p>
+    You then have to check out the sources of the WebApp module, those 
+    reside in the <b>jakarta-tomcat-connectors</b> repository, under the 
+    <b>webapp</b> directory. To check the sources out, do:
+  </p>
+
+  <screen>
+    <type>cvs checkout jakarta-tomcat-connectors/webapp</type>
+    <read>cvs server: Updating jakarta-tomcat-connectors/webapp</read>
+    <read>U jakarta-tomcat-connectors/webapp/.cvsignore</read>
+    <wait/>
+    <read>U jakarta-tomcat-connectors/webapp/support/wa_version.m4</read>
+    <read>cvs server: Updating webapplib</read>
+    <type/>
+
+    <note>and let's move them into a more accessible location</note>
+    <type>mv jakarta-tomcat-connectors/webapp webapp</type>
+    <type>rm -rf jakarta-tomcat-connectors</type>
+    <type/>
+
+    <note>then it's always better to clean up the checked out structure</note>
+    <type>cd webapp</type>
+    <type dir="webapp">cvs update -APd</type>
+    <read>cvs server: Updating .</read>
+    <read>cvs server: Updating apache-1.3</read>
+    <wait/>
+    <read>cvs server: Updating support</read>
+    <read>cvs server: Updating webapplib</read>
+    <type dir="webapp">cd ..</type>
+    <type/>
+  </screen>
+
+  </section>
+
+  <section title="APR sources">
+    <description>
+      Dealing with the Apache Portable Runtime sources
+    </description>
+
+    <p>
+      Perfect. Now, if you're building the WebApp module for Apache 2.0, you 
+      won't need to check out the Apache Portable Runtime library, since it is 
+      already included into the web server distribution. If, instead, you are 
+      building the WebApp module for Apache 1.3, you <b>need</b> to obtain a 
+      copy of APR, and you can do that checking out a copy of their CVS 
+      repository (namedly, <b>apr</b>):
+    </p>
+
+    <screen>
+      <type>cvs checkout apr</type>
+      <read>cvs server: Updating apr</read>
+      <read>U apr/.cvsignore</read>
+      <wait/>
+      <read>U apr/user/win32/groupinfo.c</read>
+      <read>U apr/user/win32/userinfo.c</read>
+      <type>mv apr jakarta-tomcat-connectors/webapp</type>
+      <note>and let's clean up this directory structure as well</note>
+      <type>cd apr</type>
+      <type dir="apr">cvs update -APd</type>
+      <read>cvs server: Updating .</read>
+      <read>cvs server: Updating atomic</read>
+      <wait/>
+      <read>cvs server: Updating user/unix</read>
+      <read>cvs server: Updating user/win32</read>
+      <type dir="apr">cd ..</type>
+      <type/>
+    </screen>
+
+  </section>
+
+  <section title="AutoConf">
+    <description>
+      All that AutoConf magic
+    </description>
+
+    <p>
+      Great, now, for semplicity's sake we can move the APR directory in the 
+      WebApp module directory, as it is in all pre-rolled source distribution 
+      tarballs, and create the <b>configure</b> script. Please note that 
+      WebApp <b>requires</b> GNU AutoConf version 2.52 or greater to build the 
+      <b>configure</b> script from its M4 sources, if you don't have this 
+      version, you can easily download a nigtly source distribution, which 
+      includes a pre-built <b>configure</b> script.
+    </p>
+
+    <screen>
+      <note>let's move APR within the WebApp module directory</note>
+      <type>mv apr webapp</type>
+      <type/>
+      <note>and create the configure script for both of them</note>
+      <type>cd webapp</type>
+      <type dir="webapp">./support/buildconf.sh</type>
+      <read>--- Checking "autoconf" version</read>
+      <read>autoconf version 2.52 detected.</read>
+      <wait/>
+      <read>Creating configure ...</read>
+      <read>--- All done</read>
+      <type dir="webapp">cd ..</type>
+      <type/>
+    </screen>
+
+    <p>
+      Please note that if you didn't move the APR directory inside the WebApp 
+      module directory, the <b>buildconf.sh</b> script will nag about not 
+      finding the APR sources, you just have to generate APR's configure as 
+      well, and then, when you're going to run the configure script for the 
+      WebApp module, you'll have to specify the <b>--with-apr=directory</b> 
+      parameter on the command line, of course replacing <b>directory</b> with 
+      the path of where you checked out the APR sources, right?
+    </p>
+
+    <screen>
+      <type dir="webapp">./support/buildconf.sh</type>
+      <wait/>
+      <read>--- Cannot run APR "buildconf" script</read>
+      <read>If you need to build the WebApp module for Apache 1.3</read>
+      <read>you will have to download the APR library sources from</read>
+      <read>http://apr.apache.org/ and run its "buildconf" script</read>
+      <wait/>
+      <read>Creating configure ...</read>
+      <read>--- All done</read>
+      <type dir="webapp">cd ..</type>
+      <type>cd apr</type>
+      <type dir="apr">./buildconf</type>
+      <read>buildconf: checking installation...</read>
+      <read>buildconf: autoconf version 2.52 (ok)</read>
+      <wait/>
+      <read>Creating include/arch/unix/apr_private.h.in ...</read>
+      <read>Creating configure ...</read>
+      <type dir="apr">cd ..</type>
+      <type/> 
+    </screen>
+
+  </section>
+
+  <section title="Conclusion">
+    <description>
+      Whoha, that's IT?
+    </description>
+
+    <p>
+      That's it, now you basically have a full WebApp module source 
+      distribution (well, apart from this documentation, which gets generated 
+      when we roll the sources tarballs). Now you're ready to go and try out 
+      the incredible wonders (and bugs) of the very latest sources of the 
+      WebApp module.
+    </p>
+  </section>
+
+</document>
diff --git a/connectors/webapp/docs/faq.xml b/connectors/webapp/docs/faq.xml
new file mode 100644
index 0000000..264e29b
--- /dev/null
+++ b/connectors/webapp/docs/faq.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0"?>
+
+<document title="FAQ about mod_webapp">
+  <description>
+    Foire Aux Questions de mod_webapp
+  </description>
+
+  <section title="Build">
+    <description>
+      FAQ about building mod_webapp
+    </description>
+
+    <screen>
+      <read>
+        ${warp.home}/build.xml:169: Cannot find the Apache Xalan XSLT processor
+      </read>
+      <note>
+        The xalan.jar file is missing in ${ant.home}/lib.
+      </note>
+      <note>
+        Dowload it from xml.apache.org:
+      </note>
+      <type>
+        wget http://xml.apache.org/dist/xalan-j/xalan-j_2_3_1-bin.tar.gz
+      </type><br/><type>
+        gzip -dc xalan-j_2_3_1-bin.tar.gz | tar xvf -
+      </type>
+      <note>
+       Xalan 2.3.1 was the lastest xalan version available when writting the
+       document.
+       tar must be a gnu tar. And it probably accept the option z.
+      </note>
+      <note>
+        Extract it and copy it in ${ant.home}/lib
+      </note>
+      <type>
+        cp ${xalan.home}/bin/xalan.jar ${ant.home}/lib
+      </type>
+    </screen>
+
+    <screen>
+      <read>
+        java.lang.NoClassDefFoundError: org/w3c/dom/traversal/NodeIterator
+      </read>
+      <note>
+        The xerces files are missing (xerces.jar or xercesImpl.jar and 
+        xmlParserAPIs.jar).
+        xercesImpl.jar and xmlParserAPIs.jar are for xerces-2.x
+        Download the xerces files and copy them in $ant.home/lib.
+        The 
+      </note>
+      <type>
+        wget http://xml.apache.org/dist/xerces-j/Xerces-J-bin.2.0.1.tar.gz
+      </type><type>
+        gzip -dc Xerces-J-bin.2.0.1.tar.gz | tar xvf -
+      </type><type>
+        cp ${xerces.home}/xercesImpl.jar ${ant.home}/bin
+      </type><type>
+        cp ${xerces.home}/xmlParserAPIs.jar ${ant.home}/bin
+      </type>
+      <note>
+       Xerces 2.0.1 was the lastest xerces version available when writting the
+       document.
+      </note>
+    </screen>
+
+    <screen>
+      <read>
+        /export/home/apache20/jakarta-tomcat-connectors/webapp/build.xml:197:
+        javax.xml.transform.TransformerException: Unknown error in XPath
+      </read>
+      <note>
+        The ant-optional.jar file is missing.
+        Download a ant-optional.jar corresponding to the ant version you use.
+        For example for ant1.4.1:
+      </note>
+      <type>
+        cd ${ant.home}/lib
+      </type><type>
+        wget http://jakarta.apache.org/builds/jakarta-ant/release/v1.4.1/bin/jakarta-ant-1.4.1-optional.jar
+      </type><type>
+        mv jakarta-ant-1.4.1-optional.jar optional.jar
+      </type>
+    </screen>
+
+  </section>
+
+</document>
diff --git a/connectors/webapp/docs/images/.cvsignore b/connectors/webapp/docs/images/.cvsignore
new file mode 100644
index 0000000..2c48ca8
--- /dev/null
+++ b/connectors/webapp/docs/images/.cvsignore
@@ -0,0 +1,3 @@
+.DS_Store
+.FBCIndex
+.FBCLockFolder
diff --git a/connectors/webapp/docs/images/corner.gif b/connectors/webapp/docs/images/corner.gif
new file mode 100644
index 0000000..e904bde
--- /dev/null
+++ b/connectors/webapp/docs/images/corner.gif
Binary files differ
diff --git a/connectors/webapp/docs/images/jakarta.gif b/connectors/webapp/docs/images/jakarta.gif
new file mode 100644
index 0000000..94084a7
--- /dev/null
+++ b/connectors/webapp/docs/images/jakarta.gif
Binary files differ
diff --git a/connectors/webapp/docs/images/japan.gif b/connectors/webapp/docs/images/japan.gif
new file mode 100644
index 0000000..09dd9b1
--- /dev/null
+++ b/connectors/webapp/docs/images/japan.gif
Binary files differ
diff --git a/connectors/webapp/docs/images/originals/.cvsignore b/connectors/webapp/docs/images/originals/.cvsignore
new file mode 100644
index 0000000..2c48ca8
--- /dev/null
+++ b/connectors/webapp/docs/images/originals/.cvsignore
@@ -0,0 +1,3 @@
+.DS_Store
+.FBCIndex
+.FBCLockFolder
diff --git a/connectors/webapp/docs/images/originals/byte.psd b/connectors/webapp/docs/images/originals/byte.psd
new file mode 100644
index 0000000..e15738f
--- /dev/null
+++ b/connectors/webapp/docs/images/originals/byte.psd
Binary files differ
diff --git a/connectors/webapp/docs/images/originals/header.psd b/connectors/webapp/docs/images/originals/header.psd
new file mode 100644
index 0000000..bf3baa6
--- /dev/null
+++ b/connectors/webapp/docs/images/originals/header.psd
Binary files differ
diff --git a/connectors/webapp/docs/images/originals/long.psd b/connectors/webapp/docs/images/originals/long.psd
new file mode 100644
index 0000000..3dc1423
--- /dev/null
+++ b/connectors/webapp/docs/images/originals/long.psd
Binary files differ
diff --git a/connectors/webapp/docs/images/originals/short.psd b/connectors/webapp/docs/images/originals/short.psd
new file mode 100644
index 0000000..46af94b
--- /dev/null
+++ b/connectors/webapp/docs/images/originals/short.psd
Binary files differ
diff --git a/connectors/webapp/docs/images/originals/variable.psd b/connectors/webapp/docs/images/originals/variable.psd
new file mode 100644
index 0000000..a64754f
--- /dev/null
+++ b/connectors/webapp/docs/images/originals/variable.psd
Binary files differ
diff --git a/connectors/webapp/docs/images/packet.gif b/connectors/webapp/docs/images/packet.gif
new file mode 100644
index 0000000..9616799
--- /dev/null
+++ b/connectors/webapp/docs/images/packet.gif
Binary files differ
diff --git a/connectors/webapp/docs/images/packet1.gif b/connectors/webapp/docs/images/packet1.gif
new file mode 100644
index 0000000..ee80d6b
--- /dev/null
+++ b/connectors/webapp/docs/images/packet1.gif
Binary files differ
diff --git a/connectors/webapp/docs/images/pixel.gif b/connectors/webapp/docs/images/pixel.gif
new file mode 100644
index 0000000..05411ad
--- /dev/null
+++ b/connectors/webapp/docs/images/pixel.gif
Binary files differ
diff --git a/connectors/webapp/docs/images/string.gif b/connectors/webapp/docs/images/string.gif
new file mode 100644
index 0000000..704b215
--- /dev/null
+++ b/connectors/webapp/docs/images/string.gif
Binary files differ
diff --git a/connectors/webapp/docs/images/tomcat.ico b/connectors/webapp/docs/images/tomcat.ico
new file mode 100644
index 0000000..20c9e73
--- /dev/null
+++ b/connectors/webapp/docs/images/tomcat.ico
Binary files differ
diff --git a/connectors/webapp/docs/images/webapp.gif b/connectors/webapp/docs/images/webapp.gif
new file mode 100644
index 0000000..fbb6e32
--- /dev/null
+++ b/connectors/webapp/docs/images/webapp.gif
Binary files differ
diff --git a/connectors/webapp/docs/index.xml b/connectors/webapp/docs/index.xml
new file mode 100644
index 0000000..cbef00b
--- /dev/null
+++ b/connectors/webapp/docs/index.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0"?>
+
+<document title="Introduction">
+  <description>
+    The WebApp Module for the Apache Web Server
+  </description>
+
+  <section title="Description">
+    <description>
+      What is the WebApp Module
+    </description>
+
+    <p>
+      Do you remember the old times of <b>mod_jserv</b>? Maybe you're too
+      young, but I do pretty well. At that time, the amazing <b>Apache
+      JServ</b> servlet container didn't have an HTTP stack per se, but
+      rather relied on the Apache 1.2 and 1.3 web server to have clients
+      accessing it.
+    </p>
+
+    <p>
+      And this is when a "module" came into the picture: a module such
+      as <b>mod_jserv</b> or the <b>WebApp</b> module connects the HTTP
+      stack of the front end web-server(s) with one or more instances of
+      a servlet container in the back end.
+    </p>
+
+    <p>
+      The WebApp module does exactly that, it's not finished (it might
+      never will), but it works, and it works pretty damn good. Plus (this
+      comes for free), we tried very hard to keep the internals clean and
+      well-designed, so that it will be so easy for anyone to simply provide
+      patches in case of bugs (yes, there are some!).
+    </p>
+
+  </section>
+
+  <section title="Purpose">
+    <description>
+      Why should I use the WebApp Module?
+    </description>
+
+    <p>
+      Because we say so? Not kidding, but it's really up to you, there are
+      several alternatives to "do the job", when we started this project
+      all of them were (in our not-so-humble-opinion), a pile of crap.
+    </p>
+
+    <p>
+      Lately there have been some improvements on those alternatives, and
+      probably, if you're wondering why you want to try out WebApp, you should
+      really stop reading now, and go and download something else.
+    </p>
+
+    <p>
+      If instead you're an adventurous geek who's up for a challenge, skip
+      the rest of this document and go to the <a href="building.xml"/>
+      section, there's where the fun starts.
+    </p>
+
+  </section>
+
+
+  <section title="Alternatives">
+    <description>
+      Are there alternative ways to connect Apache and Tomcat?
+    </description>
+
+    <p>
+      It would NOT be fair not to talk about our lovely (???) competitors,
+      of course, otherwise someone might even start calling us names, so,
+      in brief, you can use the following alternatives:
+    </p>
+    
+    <p>
+      <b>Tomcat's internal HTTP stack and mod_proxy</b>, which is not that
+      bad, but slow, well, of course Tomcat didn't have 8/9 years of C
+      routine optimization like the Apache Web Server, but it's quite good.
+      The only disadvantage I see is that it looks pretty ridiculous to
+      parse an HTTP request to generate it again (exactly the same), and
+      to parse again the response to send it back unchanged... Anyway.
+    </p>
+
+    <p>
+      <b>mod_jk</b>, is the same of the WebApp module, if you're wondering
+      why you should be using the latter instead of the former, well, I
+      believe I already answered this question (forget about WebApp).
+    </p>
+
+  </section>
+
+</document>
diff --git a/connectors/webapp/docs/menu.idx b/connectors/webapp/docs/menu.idx
new file mode 100644
index 0000000..585a1d3
--- /dev/null
+++ b/connectors/webapp/docs/menu.idx
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+
+<index>
+  <document href="index.xml"/>
+  <document href="building.xml"/>
+  <document href="cvs.xml"/>
+  <document href="warp.xml"/>
+  <document href="warp1.xml"/>
+  <document href="faq.xml"/>
+</index>
diff --git a/connectors/webapp/docs/style.css b/connectors/webapp/docs/style.css
new file mode 100644
index 0000000..93c62a4
--- /dev/null
+++ b/connectors/webapp/docs/style.css
@@ -0,0 +1,142 @@
+body {
+  margin: 0;
+  font-family: "verdana", "tahoma", "arial", "helvetica", sans-serif;
+}
+
+td.logo {
+  background-color: #666666;
+  border-style: none;
+}
+
+td.nil {
+  font-size: 1px;
+}
+
+
+td.head {
+  background-color: #999999;
+  border-style: solid;
+  border-width: 1px;
+  border-color: #cccccc #666666 #666666 #cccccc;
+  font-weight: bold;
+  font-size: smaller;
+  color: #cccccc;
+  padding: 2px;
+  text-align: right;
+}
+
+a.head:link {
+  text-decoration: none;
+  color: #cccccc;
+}
+
+a.head:visited {
+  text-decoration: none;
+  color: #cccccc;
+}
+
+a.head:active {
+  text-decoration: underline;
+  color: #ffffff;
+}
+
+a.head:hover {
+  text-decoration: underline;
+  color: #ffffff;
+}
+
+table.menu {
+  background-color: #cccccc;
+  border-style: solid;
+  border-width: 1px;
+  border-color: #cccccc #999999 #999999 #999999;
+  font-weight: bold;
+  font-size: smaller;
+  color: #000000;
+  padding: 2px;
+  text-align: left;
+  margin-left: 5px;
+}
+
+a.menu:link {
+  text-decoration: none;
+  color: #333333;
+}
+
+a.menu:visited {
+  text-decoration: none;
+  color: #333333;
+}
+
+a.menu:active {
+  text-decoration: underline;
+  color: #000000;
+}
+
+a.menu:hover {
+  text-decoration: underline;
+  color: #000000;
+}
+
+td.body {
+  background-color: #ffffff;
+  border-style: none;
+  padding: 4px;
+  text-align: justify;
+}
+
+td.section {
+  background-color: #666666;
+  border-style: none;
+  font-weight: bold;
+  font-size: bigger;
+  color: #ffffff;
+  padding: 0px;
+  text-align: left;
+}
+
+p.section {
+  background-color: #ffffff;
+  border-style: none;
+  color: #000000;
+  margin-left: 20px;
+  margin-right: 10px;
+  text-align: justify;
+}
+
+p.todo {
+  background-color: #ffffff;
+  border-style: none;
+  color: #000000;
+  margin-left: 20px;
+  margin-right: 10px;
+  text-align: justify;
+  font-size: smaller;
+}
+
+p.screen {
+  background-color: #ffffff;
+  border-style: none;
+  color: #000000;
+  margin-left: 10px;
+  margin-right: 0px;
+  text-align: left;
+}
+
+div.screen {
+  margin: 10px 0px 10px 20px;
+  font-size: smaller;
+  color: #666666;
+}
+
+em.screen {
+  font-weight: normal;
+  font-style: normal;
+  color: #666666;
+}
+
+b.screen {
+  font-weight: normal;
+  font-style: normal;
+  color: #0000ff;
+}
diff --git a/connectors/webapp/docs/style.xsl b/connectors/webapp/docs/style.xsl
new file mode 100644
index 0000000..0580dd0
--- /dev/null
+++ b/connectors/webapp/docs/style.xsl
@@ -0,0 +1,490 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet version="1.0"
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  xmlns="http://www.w3.org/TR/xhtml1/strict">
+
+  <!--
+    Let's start by declaring HOW this stylesheet must behave.
+  -->
+  <xsl:output method="html" indent="no"
+    doctype-public="-//W3C//DTD HTML 4.01//EN"
+    doctype-system="http://www.w3.org/TR/html4/strict.dtd"/>
+
+  <!--
+    Match the ROOT of the source document and process its "document" element.
+  -->
+  <xsl:template match="/">
+    <xsl:apply-templates select="document"/>
+  </xsl:template>
+
+  <!--
+    Match the roote "document" element, let's prepare the layout of the whole
+    page.
+  -->
+  <xsl:template match="document">
+    <html>
+
+      <!--
+        This is the page header, we want a title from this document title
+        the <meta> copyright statement and all authors in "meta" headers.
+      -->
+      <head>
+        <title>
+          <xsl:if test="string-length(description/text()) = 0">
+            <xsl:value-of select="@title"/>
+          </xsl:if>
+          <xsl:value-of select="description/text()"/>
+        </title>
+        <meta name="copyright" content="1999-2002 The Apache Software Foundation"/>
+        <xsl:for-each select="author">
+          <meta name="author" content="text()"/>
+          <meta name="author" content="@email"/>
+        </xsl:for-each>
+        <link rel="stylesheet" type="text/css" href="style.css"/>
+        <link rel="shortcut icon" href="images/tomcat.ico"/>
+      </head>
+
+      <!--
+        This describes the layout of the page
+      -->
+      <body bgcolor="#ffffff" text="#000000" alink="#666666" vlink="#333333" link="#666666">
+        <a name="TOP"/>
+
+        <table border="0" cellspacing="0" cellpadding="0" width="100%">
+          <!--
+            An empty row (thank you stupid IE).
+          -->
+          <tr height="1">
+            <td width="150" bgcolor="#666666" height="1" class="nil">
+              <img src="images/pixel.gif" border="0" width="150" height="1" vspace="0" hspace="0"/>
+            </td>
+            <td width="*" bgcolor="#666666" height="1" class="nil">
+              <img src="images/pixel.gif" border="0" width="570" height="1" vspace="0" hspace="0"/>
+            </td>
+          </tr>
+
+          <!--
+            Our first row contains the Jakarta and the WebApp logos.
+          -->
+          <tr>
+            <td bgcolor="#666666" class="logo" colspan="2" width="*">
+              <table border="0" cellspacing="0" cellpadding="0" width="100%">
+                <tr>
+                  <td align="left">
+                    <img src="images/jakarta.gif" border="0" width="270" height="75" align="left"/>
+                  </td>
+                  <td align="right">
+                    <img src="images/webapp.gif" border="0" width="400" height="75" align="right"/>
+                  </td>
+                </tr>
+              </table>
+            </td>
+          </tr>
+
+          <!--
+            A Turbine-style bar with links to the ASF, Jakarta and Tomcat.
+          -->
+          <tr>
+            <td bgcolor="#999999" class="head" align="right" width="*" colspan="2">
+              <nobr>
+                <a class="head" href="http://www.apache.org/">
+                  <xsl:text>Apache Software Foundation</xsl:text>
+                </a> |
+                <a class="head" href="http://jakarta.apache.org/">
+                  <xsl:text>Jakarta Project</xsl:text>
+                </a> |
+                <a class="head" href="http://jakarta.apache.org/tomcat/">
+                  <xsl:text>Apache Tomcat</xsl:text>
+                </a>
+              </nobr>
+            </td>
+          </tr>
+
+          <!--
+            Sidebar menu in a nested table and main content.
+          -->
+          <tr>
+            <td bgcolor="#ffffff" width="150" valign="top">
+
+              <!--
+                This is the sidebar menu, we have links to all documents specified
+                in "menu.idx", and if this is the current document, we go deeper
+                and write an index of the sections as well.
+              -->
+              <table border="0" width="150" cellspacing="0" cellpadding="0" class="menu">
+                <!-- Empty row, thanks IE -->
+                <tr height="1">
+                  <td width="10" bgcolor="#cccccc" height="1" class="nil">
+                    <img src="images/pixel.gif" border="0" width="10" height="1" vspace="0" hspace="0"/>
+                  </td>
+                  <td width="140" bgcolor="#cccccc" height="1" class="nil">
+                    <img src="images/pixel.gif" border="0" width="140" height="1" vspace="0" hspace="0"/>
+                  </td>
+                </tr>
+
+                <!--
+                  All the files we want to have processed in the final pages are
+                  stored (in order) in a file called "menu.idx". We set a variable
+                  name with the current URL, and then we process each "document"
+                  within the index.
+                -->
+                <xsl:variable name="root" select="document-location(.)"/>
+                <xsl:for-each select="document('menu.idx')/index/document">
+                  <tr>
+                    <td bgcolor="#cccccc" width="150" colspan="2">
+                      <nobr>
+                        <a class="menu">
+                          <xsl:call-template name="converturi">
+                            <xsl:with-param name="href" select="@href"/>
+                          </xsl:call-template>
+                        </a>
+                      </nobr>
+                    </td>
+                  </tr>
+
+                  <!--
+                    Slightly more complicated, we use the document-location function
+                    and compare against it to see whether we are in the same file or
+                    not. If we actually are, we expand to the "section" level.
+                  -->
+                  <xsl:if test="$root = document-location(document(@href))">
+                    <xsl:for-each select="document(@href)/document/section">
+                      <tr>
+                        <td bgcolor="#cccccc" width="10"/>
+                        <td bgcolor="#cccccc" width="140">
+                          <a class="menu" href="#section_{position()}">
+                            <xsl:value-of select="@title"/>
+                          </a>
+                        </td>
+                      </tr>
+                    </xsl:for-each>
+                  </xsl:if>
+                </xsl:for-each>
+
+                <!--
+                  The last thing to put down in the index are the API docs,
+                  both for C and for Java
+                -->
+                <tr>
+                  <td bgcolor="#cccccc" width="150" colspan="2">
+                    <nobr>
+                      <a class="menu" href="./api-java/index.html">Java API Documentation</a>
+                    </nobr>
+                  </td>
+                </tr>
+                <tr>
+                  <td bgcolor="#cccccc" width="150" colspan="2">
+                    <nobr>
+                      <a class="menu" href="./api-c/">C API Documentation</a>
+                    </nobr>
+                  </td>
+                </tr>
+              </table>
+            </td>
+
+            <!--
+              Done with the sidebar, now, do we want some content as well or WHAT?
+            -->
+            <td bgcolor="#ffffff" width="*" valign="top" class="body">
+              <xsl:apply-templates select="section"/>
+            </td>
+          </tr>
+        </table>
+      </body>
+    </html>
+  </xsl:template>
+
+  <!--
+    Match the "author" tag only in mode "header" (meaning that we have to
+    process it for the HTML <head> element.
+  -->
+  <xsl:template match="author" mode="header">
+    <meta name="author" content="{text()}"/>
+    <meta name="email" content="{@email}"/>
+  </xsl:template>
+
+  <!--
+    Present a canonical representation of an author.
+  -->
+  <xsl:template match="author">
+    <a href="mailto:{@email}"><xsl:value-of select="text()"/></a>
+  </xsl:template>
+
+  <xsl:template match="section">
+    <a name="section_{position()}">
+      <table border="0" cellspacing="0" cellpadding="0" width="100%">
+        <tr>
+          <td bgcolor="#666666" class="section" valign="top" align="left">
+            <img src="images/corner.gif" valign="top" align="left" hspace="0" vspace="0" border="0"/>
+              <xsl:if test="string-length(description/text()) = 0">
+                <xsl:value-of select="@title"/>
+              </xsl:if>
+              <xsl:value-of select="description/text()"/>
+          </td>
+        </tr>
+      </table>
+    </a>
+    <xsl:apply-templates select="p|ul|img|screen|todo"/>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="todo">
+    <p class="todo">
+      This paragraph has not been written yet, but <b>you</b> can contribute to it.
+      <xsl:if test="string-length(@note) > 0">
+        The original author left a note attached to this TO-DO item:
+        <b><xsl:value-of select="@note"/></b>
+      </xsl:if>
+    </p>
+  </xsl:template>
+
+  <xsl:template match="p">
+    <p class="section"><xsl:apply-templates select="table|ul|br|b|a|text()"/></p>
+  </xsl:template>
+
+  <xsl:template match="b">
+    <b><font color="#333333"><xsl:apply-templates select="text()"/></font></b>
+  </xsl:template>
+
+  <xsl:template match="br">
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="img">
+    <p>
+      <div align="center">
+        <xsl:value-of select="@alt"/><br/>
+        <img src="{@src}" alt="{@alt}" vspace="0" hspace="0" border="0"/>
+      </div>
+    </p>
+  </xsl:template>
+
+  <xsl:template match="ul">
+    <ul><xsl:apply-templates select="li"/></ul>
+  </xsl:template>
+
+  <xsl:template match="li">
+    <li><xsl:apply-templates select="br|b|a|text()"/></li>
+  </xsl:template>
+
+  <!-- JFC added -->
+  <xsl:template match="table">
+    <table border="1"><xsl:apply-templates select="tr"/></table>
+  </xsl:template>
+
+  <xsl:template match="tr">
+    <tr><xsl:apply-templates select="td|td15|td13|td6|td5|td3|td2"/></tr>
+  </xsl:template>
+
+  <xsl:template match="td">
+    <td><xsl:apply-templates select="b|a|text()"/></td>
+  </xsl:template>
+  <xsl:template match="td15">
+    <td colspan="15"><xsl:apply-templates select="b|a|text()"/></td>
+  </xsl:template>
+  <xsl:template match="td13">
+    <td colspan="13"><xsl:apply-templates select="b|a|text()"/></td>
+  </xsl:template>
+  <xsl:template match="td6">
+    <td colspan="6"><xsl:apply-templates select="b|a|text()"/></td>
+  </xsl:template>
+  <xsl:template match="td5">
+    <td colspan="5"><xsl:apply-templates select="b|a|text()"/></td>
+  </xsl:template>
+  <xsl:template match="td3">
+    <td colspan="3"><xsl:apply-templates select="b|a|text()"/></td>
+  </xsl:template>
+  <xsl:template match="td2">
+    <td colspan="2"><xsl:apply-templates select="b|a|text()"/></td>
+  </xsl:template>
+  <!-- end JFC -->
+
+  <xsl:template match="screen">
+    <p class="screen">
+      <div align="center">
+        <table width="80%" border="1" cellspacing="0" cellpadding="2" bgcolor="#cccccc">
+          <tr>
+            <td bgcolor="#cccccc">
+              <xsl:apply-templates select="note|wait|type|read"/>
+            </td>
+          </tr>
+        </table>
+      </div>
+    </p>
+  </xsl:template>
+  
+  <xsl:template match="note">
+    <div class="screen">
+      <xsl:value-of select="text()"/>
+    </div>
+  </xsl:template>
+
+  <xsl:template match="wait">
+    <div class="screen">[...]</div>
+  </xsl:template>
+
+  <xsl:template match="type">
+    <code>
+      <nobr>
+        <em class="screen">
+          <xsl:text>[user@host] ~</xsl:text>
+          <xsl:if test="string-length(@dir) > 0">
+            <xsl:text>/</xsl:text>
+            <xsl:value-of select="@dir"/>
+          </xsl:if>
+          <xsl:text> $ </xsl:text>
+        </em>
+        <xsl:if test="string-length(text()) > 0">
+          <b class="screen"><xsl:value-of select="text()"/></b>
+        </xsl:if>
+      </nobr>
+    </code>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="read">
+    <code>
+      <nobr>
+        <xsl:apply-templates select="text()|enter"/>
+      </nobr>
+    </code>
+    <br/>
+  </xsl:template>
+
+  <xsl:template match="enter">
+    <b class="screen"><xsl:value-of select="text()"/></b>
+  </xsl:template>
+
+  <xsl:template match="a">
+    <b>
+      <a>
+        <xsl:call-template name="converturi">
+          <xsl:with-param name="href" select="@href"/>
+          <xsl:with-param name="text" select="text()"/>
+          <xsl:with-param name="attr" select="'href'"/>
+        </xsl:call-template>
+      </a>
+    </b>
+  </xsl:template>
+
+  <!--
+    Convert the name of the matching "href" attribute (if needed) from
+    "file.xml#anchor" to "file.html#anchor", and insert the title of
+    the target document as the only text child of the resulting html
+    <a /> tag. (Of course, don't convert fully qualified URIs).
+  -->
+  <xsl:template name="converturi">
+    <xsl:param name="attr" select="'href'"/>
+    <xsl:param name="href" select="''"/>
+    <xsl:param name="text" select="''"/>
+
+    <xsl:choose>
+    
+      <!--
+        If the "href" parameter contains ":" this is most definitely an URL,
+        therefore we need to quote it "as is" without translating its value.
+        The text is either supplied, or it's the value of the URL itself
+        (without the trailing anchor, if any).
+      -->
+      <xsl:when test="contains($href,':')">
+        <xsl:attribute name="{$attr}"><xsl:value-of select="$href"/></xsl:attribute>
+        <xsl:if test="string-length($text) = 0">
+          <xsl:choose>
+            <xsl:when test="contains($href,'#')">
+              <xsl:value-of select="substring-before($href,'#')"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="$href"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:if>
+        <xsl:value-of select="$text"/>
+      </xsl:when>
+
+      <!--
+        Nope, we don't have a full URL, therefore we interpret this as a
+        relative hyperlink to another document. We need to translate its
+        name from "*.xml" to "*.html" (because this is how we convert the
+        names) and the text included in this will be the title of the
+        target document.
+      -->
+      <xsl:otherwise>
+        <!--
+          The "file" variable contains the part of the "href" before
+          the "#" character. Yes, the "file" name.
+        -->
+        <xsl:variable name="file">
+          <xsl:choose>
+            <xsl:when test="contains($href,'#')">
+              <xsl:value-of select="substring-before($href,'#')" />
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="$href" />
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:variable>
+
+        <!--
+          Like "file" the "anchor" variable contains the part of the "href"
+          after the "#" character.
+        -->
+        <xsl:variable name="anchor">
+          <xsl:if test="contains($href,'#')">
+            <xsl:value-of select="'#'" />
+            <xsl:value-of select="substring-after($href,'#')" />
+          </xsl:if>
+        </xsl:variable>
+
+        <!--
+          Good, now we check if "file" ends in ".xml", if so, we replace that
+          with ".html", otherwise we keep its original value, then we add the
+          anchor we gathered before. We call this "target".
+        -->
+        <xsl:variable name="target">
+          <xsl:if test="string-length($file) > 0">
+            <xsl:choose>
+              <xsl:when test="substring($file,string-length($file)-3) = '.xml'">
+                <xsl:value-of select="substring($file,1,string-length($file)-3)"/>
+                <xsl:value-of select="'html'"/>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:value-of select="$file"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:if>
+          <xsl:value-of select="$anchor"/>
+        </xsl:variable>
+
+        
+        <!--
+          Now, we want to set the attribute to contain the "target" variable.
+        -->
+        <xsl:attribute name="{$attr}">
+          <xsl:value-of select="$target"/>
+        </xsl:attribute>
+
+        <!--
+          To finish we want to set the body of this element: if we have "text"
+          the body of the element will be just that, otherwise, it will be
+          the "target" value (the translated href) if there was no text,
+          or the "title" of the target document if we actually translated
+          something
+        -->
+        <xsl:if test="string-length($text) = 0">
+          <xsl:choose>
+            <xsl:when test="$target = $href">
+              <xsl:value-of select="$file"/>
+            </xsl:when>
+            <xsl:otherwise>
+              <xsl:value-of select="document($file)/document/@title"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:if>
+        <xsl:value-of select="$text"/>
+
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+</xsl:stylesheet>
diff --git a/connectors/webapp/docs/warp.xml b/connectors/webapp/docs/warp.xml
new file mode 100644
index 0000000..c8df427
--- /dev/null
+++ b/connectors/webapp/docs/warp.xml
@@ -0,0 +1,887 @@
+<?xml version="1.0"?>
+
+<document title="WARP 0.10">
+  <description>
+    The WARP protocol version 0.10
+  </description>
+
+  <section title="Introduction">
+    <description>
+      An overview of the WARP protocol
+    </description>
+
+    <p>
+      The WARP protocol was inspired and designed by the great effort made
+      by the Apache JServ team in finding a efficent stream-based protocol
+      to transfer pre-parsed HTTP requests. Much of its structure was derived
+      from the AJPv21 protocol, and has been updated to add few features,
+      and remove its reparsing overhead.
+    </p>
+    
+    <p>
+      When thinking about forwarding HTTP requests between a web server (Apache
+      HTTPd) and a servlet container (Apache Tomcat), we must realize that much
+      of the pre-parsing of the HTTP data is already performed by the web-server.
+      Therefore, the scope of the WARP protocol is to minimize the overhead
+      of re-transmitting a pre-parsed HTTP request to the servlet container,
+      encoding data in a way that it's accessible in a very fast and efficent
+      way, without adding a subsequent post-parsing stage.
+    </p>
+
+    <p>
+      In addition, the WARP protocol defines a way over which the HTTP server and
+      the servlet container can negotiate their resources (in our case, web
+      applications), maintaining a tight integration between the applications
+      deployed by the servlet container, and their host mapping within the
+      web server.
+    </p>
+  </section>
+
+  <section title="Packet Structure">
+    <description>
+      The structure of a generic WARP packet.
+    </description>
+
+    <p>
+      To minimize parsing overhead on both sides (HTTP server and servlet
+      container) the WARP protocol defines a packet structure, which is
+      used to encapsulate all data passed over a reliable full-duplex
+      connection (the WARP connection).
+    </p>
+
+    <p>
+      Each byte (octet) in a WARP packet MUST be transmitted according to the
+      "network order" (or big endian style), and each packet is composed
+      of the following parts:
+    </p>
+
+    <ul>
+      <li>
+        An 8 bits long <b>packet type</b> describing the content of the
+        following payload (if any), an operation to perform by the peer,
+        or a reply to an operation requested by the peer.
+      </li>
+      <li>
+        A 16 bits long <b>payload length</b> specifying the length of the
+        payload carried by this packet.
+      </li>
+      <li>
+        A zero-or-more bytes long <b>payload</b>. The length (in bytes) of
+        this part must be specified in the previous two bytes (payload length).
+      </li>
+    </ul>
+
+    <img src="images/packet.gif" alt="The WARP Packet Structure"/>
+
+  </section>
+
+  <section title="Payload">
+    <description>
+      Payload Structure
+    </description>
+
+    <p>
+      The payload of a packet can be structured in two ways, depending on its 
+      type: the packet can either contain raw data (such as an HTTP request or 
+      response body) or a combination of numeric values and strings.
+    </p>
+  
+    <p>
+      By definition a numeric value is a 32 bit signed integer represented
+      in network byte order (the value of 6 decimal would be represented as
+      000000000000000000000000000000000110 binary (110 preceeded by 29
+      zeroes).
+    </p>
+
+    <p>
+      A string is somehow more complex, and it is defined in the following way:
+    </p>
+    
+    <ul>
+      <li>
+        The first two octets represent the string length as an unsigned 16 bits
+        value. The 0x0 and 0x0ffff values (namely the sequence of all zero bits
+        and the sequence of all one bits) have a special meaning.
+      </li>
+      <li>
+        If the string length (first two octets) is not 0x0 or 0x0ffff, a sequence
+        of bytes follow, as long as the number of bytes specified in length.
+        The bytes in this field are the UTF-8 representation of the string.
+      </li>
+      <li>
+        If length is 0x0, the string is assumed to be the empty string.
+      </li>
+      <li>
+        If length is 0x0ffff, the string is assumed to be the null string.
+      </li>
+    </ul>
+  
+    <img src="images/string.gif" alt="The &#34;FooX&#34; string"/>
+
+</section>
+
+<section title="Packet Types">
+  <description>
+   The following is a list of packet types and their relative expected
+   responses:
+  </description>
+<p>
+<table>
+<tr>
+<td5>Packet/States Table</td5>
+</tr>
+
+<tr>
+<td>Name</td>
+
+<td>Type</td>
+
+<td>Originator</td>
+
+<td>Description</td>
+
+<td>Expects</td>
+</tr>
+
+<tr>
+<td>ERROR</td>
+
+<td>0x00</td>
+
+<td>Client Server</td>
+
+<td>One end of the peer notifies the other of a fatal connection error</td>
+
+<td>N/A</td>
+</tr>
+
+<tr>
+<td>DISCONNECT</td>
+
+<td>0xfe</td>
+
+<td>Client Server</td>
+
+<td>One end of the peer notifies the other that the connections is going
+to be closed</td>
+
+<td>N/A</td>
+</tr>
+
+<tr>
+<td>FATAL</td>
+
+<td>0xff</td>
+
+<td>Client
+<br/>Serve</td>
+
+<td>A protocol error occourred, the connection must be closed.</td>
+
+<td>N/A</td>
+</tr>
+
+<tr>
+<td></td>
+
+<td></td>
+
+<td></td>
+
+<td></td>
+
+<td></td>
+</tr>
+
+<tr>
+<td>CONF_WELCOME</td>
+
+<td>0x01</td>
+
+<td>Server</td>
+
+<td>First message sent by Server after a client connection.</td>
+
+<td>CONF_DEPLOY</td>
+</tr>
+
+<tr>
+<td>CONF_DEPLOY</td>
+
+<td>0x05</td>
+
+<td>Client</td>
+
+<td>Deploy a web application.</td>
+
+<td>CONF_APPLIC</td>
+</tr>
+
+<tr>
+<td>CONF_APPLIC</td>
+
+<td>0x06</td>
+
+<td>Server</td>
+
+<td>Application deployed successfully.</td>
+
+<td>CONF_MAP, CONF_DEPLOY</td>
+</tr>
+
+<tr>
+<td>CONF_MAP</td>
+
+<td>0x07</td>
+
+<td>Client</td>
+
+<td>Require for application mapping information</td>
+
+<td>CONF_MAP_DONE,CONF_MAP_ALLOW,CONF_MAP_DENY</td>
+</tr>
+
+<tr>
+<td>CONF_MAP_ALLOW</td>
+
+<td>0x08</td>
+
+<td>Server</td>
+
+<td>Allow client to map the request</td>
+
+<td>N/A</td>
+</tr>
+
+<tr>
+<td>CONF_MAP_DENY</td>
+
+<td>0x09</td>
+
+<td>Server</td>
+
+<td>Deny client to map the request</td>
+
+<td>N/A</td>
+</tr>
+
+<tr>
+<td>CONF_MAP_DONE</td>
+
+<td>0x0a</td>
+
+<td>Server</td>
+
+<td>All the ALLOW/DENY map have been transfered</td>
+
+<td>CONF_DEPLOY, CONF_DONE</td>
+</tr>
+
+<tr>
+<td>CONF_DONE</td>
+
+<td>0x0e</td>
+
+<td>Client</td>
+
+<td>Configuration completly sent to the server</td>
+
+<td>CONF_PROCEED</td>
+</tr>
+
+<tr>
+<td>CONF_PROCEED</td>
+
+<td>0x0f</td>
+
+<td>Server</td>
+
+<td>Server ready to accept requests.</td>
+
+<td>Request elements (REQ_INIT, REQ_CONTENT, REQ_SCHEME, REQ_AUTH, REQ_HEADER,
+REQ_SERVER, REQ_CLIENT, REQ_PROCEED)</td>
+</tr>
+
+<tr>
+<td></td>
+
+<td></td>
+
+<td></td>
+
+<td></td>
+
+<td></td>
+</tr>
+
+<tr>
+<td>REQ_INIT</td>
+
+<td>0x10</td>
+
+<td>Client</td>
+
+<td>New request.</td>
+
+<td>N/A</td>
+</tr>
+
+<tr>
+<td>REQ_CONTENT</td>
+
+<td>0x11</td>
+
+<td>Client</td>
+
+<td>Mine type and length</td>
+
+<td>N/A</td>
+</tr>
+
+<tr>
+<td>REQ_SCHEME</td>
+
+<td>0x12</td>
+
+<td>Client</td>
+
+<td>Scheme</td>
+
+<td>N/A</td>
+</tr>
+
+<tr>
+<td>REQ_AUTH</td>
+
+<td>0x13</td>
+
+<td>Client</td>
+
+<td>Authentication</td>
+
+<td>N/A</td>
+</tr>
+
+<tr>
+<td>REQ_HEADER</td>
+
+<td>0x14</td>
+
+<td>Client</td>
+
+<td>Header element</td>
+
+<td>N/A</td>
+</tr>
+
+<tr>
+<td>REQ_SERVER</td>
+
+<td>0x15</td>
+
+<td>Client</td>
+
+<td>server information</td>
+
+<td>N/A</td>
+</tr>
+
+<tr>
+<td>REQ_CLIENT</td>
+
+<td>0x16</td>
+
+<td>Client</td>
+
+<td>remote peer information</td>
+
+<td>N/A</td>
+</tr>
+
+<tr>
+<td>REQ_PROCEED</td>
+
+<td>0x1f</td>
+
+<td>Client</td>
+
+<td>process request</td>
+
+<td>Reponse elements, ASK_SSL, ASK_SSL_CLIENT or CBK_READ.</td>
+</tr>
+
+<tr>
+<td></td>
+
+<td></td>
+
+<td></td>
+
+<td></td>
+
+<td></td>
+</tr>
+
+<tr>
+<td>ASK_SSL</td>
+
+<td>0x43</td>
+
+<td>Server</td>
+
+<td>Request SSL information</td>
+
+<td>REP_SSL_NO, REP_SSL</td>
+</tr>
+
+<tr>
+<td>ASK_SSL_CLIENT</td>
+
+<td>0x44</td>
+
+<td>Server</td>
+
+<td>Request for CC</td>
+
+<td>REP_SSL_NO, REP_SSL_CERT</td>
+</tr>
+
+<tr>
+<td>REP_SSL_NO</td>
+
+<td>0x5f</td>
+
+<td>Client</td>
+
+<td>SSL information not available.</td>
+
+<td>Reponse elements.</td>
+</tr>
+
+<tr>
+<td>REP_SSL</td>
+
+<td>0x52</td>
+
+<td>Client</td>
+
+<td>SSL information.</td>
+
+<td>Reponse elements.</td>
+</tr>
+
+<tr>
+<td>REP_SSL_CERT</td>
+
+<td>0x53</td>
+
+<td>Client</td>
+
+<td>Client certificate.</td>
+
+<td>Reponse elements.</td>
+</tr>
+
+<tr>
+<td></td>
+
+<td></td>
+
+<td></td>
+
+<td></td>
+
+<td></td>
+</tr>
+
+<tr>
+<td>CBK_READ</td>
+
+<td>0x40</td>
+
+<td>Server</td>
+
+<td>Request for body data.</td>
+
+<td>CBK_DATA, CBK_DONE</td>
+</tr>
+
+<tr>
+<td>CBK_DATA</td>
+
+<td>0x41</td>
+
+<td>Client</td>
+
+<td>Chunk of data.</td>
+
+<td>CBK_READ or Reponse elements.</td>
+</tr>
+
+<tr>
+<td>CBK_DONE</td>
+
+<td>0x42</td>
+
+<td>Client</td>
+
+<td>End of data.</td>
+
+<td>Reponse elements.</td>
+</tr>
+
+<tr>
+<td></td>
+
+<td></td>
+
+<td></td>
+
+<td></td>
+
+<td></td>
+</tr>
+
+<tr>
+<td>RES_STATUS</td>
+
+<td>0x20</td>
+
+<td>Server</td>
+
+<td>HTTP status.</td>
+
+<td>N/A</td>
+</tr>
+
+<tr>
+<td>RES_HEADER</td>
+
+<td>0x21</td>
+
+<td>Server</td>
+
+<td>response header.</td>
+
+<td>N/A</td>
+</tr>
+
+<tr>
+<td>RES_COMMIT</td>
+
+<td>0x2f</td>
+
+<td>Server</td>
+
+<td>send data to browser.</td>
+
+<td>N/A</td>
+</tr>
+
+<tr>
+<td>RES_BODY</td>
+
+<td>0x30</td>
+
+<td>Server</td>
+
+<td>data reponse element</td>
+
+<td>N/A</td>
+</tr>
+
+<tr>
+<td>RES_DONE</td>
+
+<td>0x3f</td>
+
+<td>Server</td>
+
+<td>end of reponse</td>
+
+<td>Request elements.</td>
+</tr>
+</table>
+</p>
+
+</section>
+
+<section  title="Type Details">
+  <description>
+    Packet Type Details
+  </description>
+
+<p><br/><b>ERROR</b> 0x00
+<br/>Specifies an unrecoverable protocol error. After the client or the
+server issues this packet the socket underlying the WARP connection must
+be closed.
+<br/>This message can be sent at any time by the client or the server.
+<ul>
+<li>
+[string]: A detail message on why the connection must be closed.</li>
+</ul>
+</p><p>
+<b>DISCONNECT</b> 0xfe
+<br/>The client or the server is going to close the connection (no fatal
+protocol error).
+</p><p><b>FATAL</b> 0x0ff
+<br/>A protocol error occourred, the connection must be closed.
+<ul>
+<li>
+[string]: An error message.</li>
+</ul>
+</p><p>
+<b>CONF_WELCOME</b> 0x01
+<br/>The server issues this packet when a connection is opened. The server
+awaits for configuration information.
+<ul>
+<li>
+[ushort] Major protocol version.</li>
+
+<li>
+[ushort] Minor protocol version.</li>
+
+<li>
+[integer] The server unique-id.</li>
+</ul>
+</p><p>
+<b>CONF_DEPLOY </b>0x05
+<br/>The client attempts to deploy a web application.
+<ul>
+<li>
+[string] The application name.</li>
+
+<li>
+[string] The virtual host name.</li>
+
+<li>
+[ushort] The virtual host port.</li>
+
+<li>
+[string] The web-application URL path.</li>
+</ul>
+</p><p>
+<b>CONF_APPLIC</b> 0x06
+<br/>The server replies to a CONF_DEPLOY message with the web application
+identifier of the configured application.
+<ul>
+<li>
+[integer] The web application unique id for this server.</li>
+
+<li>
+[string] The web application real path (where it's expanded).</li>
+</ul>
+</p><p>
+<b>CONF_MAP</b> 0x07
+<br/>The client requests to the server to enumerate all mappings for a specified
+web-application. The server replies to this message with a serie of MAP_ALLOW
+and MAP_DENY packets, terminated by a MAP_DONE packet.
+<ul>
+<li>
+[integer] The web application unique id for this server.</li>
+</ul>
+</p><p>
+<b>CONF_MAP_ALLOW</b> 0x08:
+<br/>The server replies to a CONF_MAP message with those packets to indicate
+a mapping to a static page, or a resource that can be served autonomously
+by the remote end (the web server).
+<ul>
+<li>
+[string] An url-pattern as defined by the Servlet specification.</li>
+</ul>
+</p><p>
+<b>CONF_MAP_DENY</b> 0x09
+<br/>The server replies to a CONF_MAP message with those packets to indicate
+a mapping to a resource that must be served by the server (servlet container).
+<ul>
+<li>
+[string] An url-pattern as defined by the Servlet specification.</li>
+</ul>
+</p><p>
+<b>CONF_MAP_DONE</b> 0x0a
+<br/>The server replies to a CONF_MAP message with this packet to indicate
+that all servlet mappings have been successfully transfered to the other
+end.
+</p><p>
+<b>CONF_DONE</b> 0x0e
+<br/>Client issues this message when all configurations have been processed.
+</p><p><b>CONF_PROCEED</b> 0x0f
+<br/>Server issues this message in response to a CONF_DONE message, to acknowledge
+its readiness to accept requests.
+</p><p><b>REQ_INIT</b> 0x10
+<br/>The client requests to the WARP server that a request is about to be
+processed.
+<ul>
+<li>
+[integer] The web-application unique ID for this server. (From the
+CONF_MAP).</li>
+
+<li>
+[string] The HTTP method used for this request.</li>
+
+<li>
+[string] The request URI.</li>
+
+<li>
+[string] The request query arguments.</li>
+
+<li>
+[string] The request protocol (HTTP/1.0, HTTP/1.1...).</li>
+</ul>
+</p><p>
+<b>REQ_CONTENT</b> 0x11
+<br/>The mime content type and length of this request.
+<ul>
+<li>
+[string] The MIME content type of this reques.</li>
+
+<li>
+[integer] The content length of this request.</li>
+</ul>
+</p><p>
+<b>REQ_SCHEME</b> 0x12
+<br/>The scheme description of this request.
+<ul>
+<li>
+[string] The scheme (part before :// in the URL) of this request.</li>
+</ul>
+</p><p>
+<b>REQ_AUTH</b> 0x13
+<br/>Authentication information of the HTTP remote peer.
+<ul>
+<li>
+[string] The remote-user name.</li>
+
+<li>
+[string] The authentication information.</li>
+</ul>
+</p><p>
+<b>REQ_HEADER</b> 0x14
+<br/>An HTTP request header. There are severals.
+<ul>
+<li>
+[string] The header name.</li>
+
+<li>
+[string] The header value.</li>
+</ul>
+</p><p>
+<b>REQ_SERVER </b>0x15
+<br/>The HTTP server information.
+<ul>
+<li>
+[string] The HTTP server host name.</li>
+
+<li>
+[string] The HTTP server IP address.</li>
+
+<li>
+[ushort] The port receiving the HTTP request.</li>
+</ul>
+</p><p>
+<b>REQ_CLIENT</b> 0x16
+<br/>The HTTP client (remote peer) information.
+<ul>
+<li>
+[string] The HTTP client host name.</li>
+
+<li>
+[string] The HTTP client IP address.</li>
+
+<li>
+[ushort] The remote port originating the HTTP request.</li>
+</ul>
+</p><p>
+<b>REQ_PROCEED</b> 0x1f
+<br/>The client finished transmitting the request. The server can now proceed
+and process the request.
+</p><p>
+<b>ASK_SSL</b> 0x43
+<br/>The WARP server (Tomcat) asks the WARP client to transfer the basic
+SSL information (cypher, keysize and session).
+</p><p><b>ASK_SSL_CLIENT</b> 0x44
+<br/>The WARP server (Tomcat) asks the WARP server to transfer the client
+certificate. (just the first element of the chain and the webserver
+should request for it to the browser if possible).
+</p><p>
+<b>REP_SSL_CERT</b> 0x52
+<br/>The client certificate (remote peer).
+<ul>
+<li>
+[string] The client certificate. (PEM format).</li>
+</ul>
+</p><p>
+<b>REP_SSL</b> 0x53
+<br/>SSL information.
+<ul>
+<li>
+[string] The cipher_suite.</li>
+
+<li>
+[string] The ssl session. (That is not in the spec's).</li>
+
+<li>
+[ushort] size of the algorithm (56-128).</li>
+</ul>
+</p><p>
+<b>REP_SSL_NO</b> 0x5F
+<br/>The requested SSL information is not available.
+</p><p>
+<b>CBK_READ</b> 0x40
+<br/>A request callback. The WARP server queries the WARP client (HTTP
+server) to transmit a chunk of the request body.
+<ul>
+<li>
+[ushort] The max number of bytes the server is able to receive.</li>
+</ul>
+</p><p>
+<b>CBK_DATA</b> 0x41
+<br/>As requested by the WARP server, the WARP client (HTTP server) transmits
+a chunk of the request body.
+<ul>
+<li>
+[raw] A chunk of the request body.</li>
+</ul>
+</p><p>
+<b>CBK_DONE</b> 0x42
+<br/>The WARP client (HTTP server) informs the WARP server that the request
+body has been fully transmitted.
+</p><p>
+<b>RES_STATUS</b> 0x20
+<br/>The server replies with the HTTP response status for the request.
+<ul>
+<li>
+[ushort] The HTTP status of the response.</li>
+
+<li>
+[string] The HTTP response message.</li>
+</ul>
+</p><p>
+<b>RES_HEADER</b> 0x21
+<br/>An HTTP MIME response header to send to the client. There are severals
+of these.
+<ul>
+<li>
+[string] The MIME header name.</li>
+
+<li>
+[string] The MIME header value.</li>
+</ul>
+</p><p>
+<b>RES_COMMIT</b> 0x2f
+<br/>The server indicates that the first part of the response (HTTP status
+line and MIME headers) are to be committed to the client (remote peer).
+</p><p>
+<b>RES_BODY</b> 0x30
+<br/>The HTTP response body.
+<ul>
+<li>[raw] A chunk of the response body. Up to 65535 bytes.</li>
+</ul>
+</p><p>
+<b>RES_DONE</b> 0x3f
+<br/>The server finished transmitting the response.
+</p>
+
+</section>
+
+</document>
diff --git a/connectors/webapp/docs/warp1.xml b/connectors/webapp/docs/warp1.xml
new file mode 100644
index 0000000..882cbaa
--- /dev/null
+++ b/connectors/webapp/docs/warp1.xml
@@ -0,0 +1,224 @@
+<?xml version="1.0"?>
+
+<document title="WARP 1.0 (draft)">
+  <description>
+    The WARP protocol version 1.0 (draft)
+  </description>
+
+  <section title="Introduction">
+    <description>
+      An overview of the WARP protocol
+    </description>
+
+    <p>
+      WARP was inspired by the great effort made by the Apache JServ team
+      in finding an efficent transport protocol allowing to connect over a
+      reliable full-duplex transmission channel (such as TCP over IP,
+      bi-directional pipes or UNIX sockets) a servlet container and an
+      HTTP protocol stack (normally, a web server).
+    </p>
+
+    <p>
+      Note, this revision of the WARP protocol has not been adopted yet by
+      the <b>WebApp module</b> for the Apache Web Server or by <b>Apache
+      Tomcat</b>.
+    </p>
+  </section>
+
+  <section title="The WARP name">
+    <description>
+      Maximum WARP, engage! (Tales about a name)
+    </description>
+
+    <p>
+      First of all, for non science fiction fanatics, WARP is an acronym and
+      means, (to use a syntax similar to Perl regular expressions) "Web
+      Application Remote (Access|Control)+ Protocol".
+    </p>
+
+    <p>
+      In "Star Trek" terms, WARP is a measuring unit for speed, such as
+      "miles per hour" or "meters per seconds". Always in "Star Trek" terms,
+      <b>Radio Free Tomorrow</b> gives us a very nice description about what
+      exactly the term "WARP" means (you can see the full text
+      <a href="http://rft.melm.org/story/2002/3/25/182619/336">here</a>):
+    </p>
+
+    <ul>
+      <li>
+        [...] Warping space as a means of traveling faster than light is a
+        method based in solid fact, and physicists have devised a mathematical
+        model of the universe which would allow it to work.
+        <br/>
+        The idea behind warp drive is this: you bend a small section of space
+        to the extent that it completely encloses your starship, effectively
+        isolating it from the outside universe. You then move this isolated
+        pocket of space time to your destination, and allow it to rejoin normal
+        space.
+        <br/>
+        Because it's not moving through normal physical space, the lightspeed
+        limit doesn't apply to the warp. It can travel as fast as you want it
+        to. And because space itself is being bent, the starship technically
+        isn't moving at all, so restrictions on normal Newtonian motion don't
+        affect it.
+      </li>
+    </ul>
+
+    <p>
+      In other terms, then, WARP is all about "bending" something (space), to
+      allow something else (the spaceship) to move faster from one point to
+      another.
+    </p>
+
+    <p>
+      How does this applies to our case? Given that we can't "bend" your OS
+      kernel to transmit data faster over a reliable full-duplex connection,
+      neither we can "bend" the data included into the HTTP request to be
+      transmitted from one point to another, the WARP protocol "bends" the
+      rules of HTTP, transmitting an HTTP request, with all operational data
+      attached to it, into a different and more efficent manner, to minimize
+      the computational time required by both parties to process it.
+    </p>
+
+    <p>
+      To simplify, although HTTP version 1.1 is a great protocol for hypertext
+      data, it is not suited to encapsulate a pre-parsed half-processed HTTP
+      request and transmit it to another party for further elaboration.
+    </p>
+
+    <p>
+      And by all means, we hope that when you fire up your servlet container,
+      you won't stand up in your cubicle sticking your index finger out and
+      screaming "Maximum WARP, engage!".
+    </p>
+  </section>
+
+  <section title="Packet structure">
+    <description>
+      The Warp 1.0 packet structure
+    </description>
+
+    <p>
+      Compared to previous releases of the WARP protocol, the new packet
+      structure looses its "packet lenght" field. This was done to allow
+      progressive memory allocation during process (we don't require the
+      packet to be fully read before starting to put data in the right
+      places) and because (apart from when raw data was transfered), its
+      value could be easily gathered by the content of the packet itself.
+    </p>
+    
+    <p>
+      The new structure of the WARP packet is therefore defined as follows:
+    </p>
+
+    <img src="images/packet1.gif"/>
+    
+    <ul>
+      <li>
+        <b>Packet Type</b>: is a unique one-byte value detailing what is
+        contained in the packet's payload.
+      </li>
+      <li>
+        <b>Packet Payload</b>: is a variable-length set of bytes containing
+        the data actually included in this packet. Its length and content
+        vary depending on the type of the packet.
+      </li>
+    </ul>
+  </section>
+
+  <section title="Packet payload">
+    <description>
+      The Warp 1.0 packet payload structure
+    </description>
+
+    <p>
+      Depending on the type of the packet, the payload can contain zero or
+      more fields (each packet type specifies exactly what or where those
+      fields appear in the payload). Here listed are all payload fields
+      recognized by the Warp 1.0 protocol, their field identifier is a
+      reference for the below mentioned packet type descriptions:
+    </p>
+
+    <p>
+      <b>Numeric packet payload fields:</b>
+    </p>
+
+    <ul>
+      <li>
+        <b>signed/unsigned byte</b>: is represented as a 8 bits sequence of
+        data. Its value can range between 0 and 255 decimal if unsigned and
+        between -128 and 127 decimal if signed, with the most significant bit
+        representing the sign. (field identifier: <b>BYTE/UBYTE</b>)
+      </li>
+      <li>
+        <b>signed/unsigned short integer</b>: is represented as a 16 bits
+        sequence of data, encoded in network-byte-order (most significant
+        bytes come first). Its value can range between 0 and 65535 decimal if
+        unsigned and between -32768 and 32767 when signed, with the most
+        significant bit representing the sign (field identifier:
+        <b>SHORT/USHORT</b>).
+      </li>
+      <li>
+        <b>signed/unsigned integer</b>: exactly as for short integers, apart
+        from the fact that it is represented as a sequence of 32 bits,
+        therefore its value can range between -2147483648 and 2147483647
+        decimal when signed or between 0 and 4294967295 when unsigned
+        (field identifier <b>INT/UINT</b>).
+      </li>
+      <li>
+        <b>signed/unsigned long integer</b>: exactly as short and integer, but
+        it is represented as a sequence of 64 bits (you do the maths).
+        (field identifier <b>LONG/ULONG</b>).
+      </li>
+    </ul>
+
+    <p>
+      <b>Variable-length packet payload fields:</b>
+    </p>
+
+    <ul>
+      <li>
+        <b>raw data</b>: a chunk of raw data is transferred following this
+        structure: a USHORT field representing the number of bytes that
+        will be transfered, or if this value is 65535 decimal (0xffff) the
+        "null" sequence of bytes, followed by a serie of bytes (zero or more).
+        (field identifier <b>RAW</b>).
+      </li>
+      <li>
+        <b>generic string</b>: a generic string follows the same structure
+        defined for RAW, but the byte sequence is a US-ASCII encoded
+        representation of a string, as outlined in the HTTP/1.1 specification
+        (<a href="http://www.rfc-editor.org/rfc/rfc2616.txt">RFC-2616</a>)
+        for everything but request and response bodies and header values
+        (field identifier <b>STRING</b>).
+      </li>
+      <li>
+        <b>mime string</b>: a mime string is exactly as a generic string,
+        but its byte-representation is supposed to be ISO-8859-1 encoded,
+        and must follow the rules defined by the HTTP/1.1 protocol
+        specification section 2.2 for TEXT (used by header values) referring
+        to <a href="http://www.rfc-editor.org/rfc/rfc2047.txt">RFC-2047</a>
+        (Message Header Extension for Non-ASCII Text). Thus (for example)
+        the string "I love Japan" with the word "Japan" translated in Japanese
+        ("Nihon") written in Kanji (in Unicode characters this would look like
+        U65E5 + U672C) and encoded in Shift_JIS would be represented
+        as <b>"I love =?Shift_JIS?q?=93=fa=96=7b?="</b> or if encoded in UTF-8
+        would look like <b>"I love =?UTF-8?q?=e6=97=a5=e6=9c=ac?="</b>.
+        (field identifier <b>MIME</b>).
+      </li>
+    </ul>
+
+    <img src="images/japan.gif"/>
+
+    <p>
+      For simplicity's sake, this is how one of the three above mentioned
+      variable-length packet payload fields should be transfered (given that
+      the three characters F, o and X have the same value in ISO-8859-1 and
+      US-ASCII, and their hexadecimal value is respectively 0x46, 0x6f and
+      0x58):
+    </p>
+
+    <img src="images/string.gif"/>
+
+  </section>
+</document>
diff --git a/connectors/webapp/include/.cvsignore b/connectors/webapp/include/.cvsignore
new file mode 100644
index 0000000..e43b0f9
--- /dev/null
+++ b/connectors/webapp/include/.cvsignore
@@ -0,0 +1 @@
+.DS_Store
diff --git a/connectors/webapp/include/wa.h b/connectors/webapp/include/wa.h
new file mode 100644
index 0000000..c0289b9
--- /dev/null
+++ b/connectors/webapp/include/wa.h
@@ -0,0 +1,82 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * @author  Pier Fumagalli <mailto:pier@betaversion.org>
+ * @version $Id$
+ */
+#ifndef _WA_H_
+#define _WA_H_
+
+/* C standard includes */
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#ifdef WIN32
+#define vsnprintf _vsnprintf
+#else
+#include <unistd.h>
+#endif /* ifdef WIN32 */
+
+/* APR Library includes */
+#include <apr_general.h>
+#include <apr_pools.h>
+#include <apr_strings.h>
+#include <apr_tables.h>
+#include <apr_tables.h>
+#include <apr_time.h>
+#include <apr_network_io.h>
+#include <apr_file_info.h>
+#if APR_HAS_THREADS
+#include <apr_thread_mutex.h>
+#include <apr_atomic.h>
+#endif
+
+/* WebApp Library type definitions. */
+typedef enum {
+    wa_false,
+    wa_true,
+} wa_boolean;
+
+typedef struct wa_chain wa_chain;
+
+typedef struct wa_application wa_application;
+typedef struct wa_virtualhost wa_virtualhost;
+typedef struct wa_connection wa_connection;
+
+typedef struct wa_request wa_request;
+typedef struct wa_ssldata wa_ssldata;
+typedef struct wa_hostdata wa_hostdata;
+typedef struct wa_handler wa_handler;
+
+typedef struct wa_provider wa_provider;
+
+/* All declared providers */
+extern wa_provider wa_provider_info;
+extern wa_provider wa_provider_warp;
+/*extern wa_provider wa_provider_jni;*/
+
+/* WebApp Library includes */
+#include <wa_main.h>
+#include <wa_config.h>
+#include <wa_request.h>
+#include <wa_version.h>
+
+/* Debugging mark */
+#define WA_MARK __FILE__,__LINE__
+
+#endif /* ifndef _WA_H_ */
diff --git a/connectors/webapp/include/wa_config.h b/connectors/webapp/include/wa_config.h
new file mode 100644
index 0000000..15ffa43
--- /dev/null
+++ b/connectors/webapp/include/wa_config.h
@@ -0,0 +1,130 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * @package Configuration
+ * @author  Pier Fumagalli <mailto:pier@betaversion.org>
+ * @version $Id$
+ */
+#ifndef _WA_CONFIG_H_
+#define _WA_CONFIG_H_
+
+/**
+ * The WebApp Library connection structure.
+ * <br>
+ * This structure holds all required data required by a connection provider
+ * to connect to a web-application container and to handle HTTP requests.
+ */
+struct wa_connection {
+    /** The connection name. */
+    char *name;
+    /** The connection provider. */
+    wa_provider *prov;
+    /** The connection parameter (as in the configuration file). */
+    char *parm;
+    /** The provider-specific configuration member for this connection. */
+    void *conf;
+};
+
+/**
+ * The WebApp Library virtual host structure.
+ * <br>
+ * This structure holds informations related to a virtual host under which
+ * web-applications are deployed.
+ */
+struct wa_virtualhost {
+    /** The virtual host name. */
+    char *name;
+    /** The virtual host port. */
+    int port;
+    /** The list of all applications deployed in this virtual hosts. */
+    wa_chain *apps;
+};
+
+/**
+ * The WebApp Library application structure.
+ * <br>
+ * This structure holds all informations associated with an application.
+ * Applications are not grouped in virtual hosts inside the library as in
+ * specific cases (like when load balancing is in use), multiple applications
+ * can share the same root URL path, or (like when applications are shared),
+ * a single web application can be shared across multiple virtual host.
+ */
+struct wa_application {
+    /** The application virtual host. */
+    wa_virtualhost *host;
+    /** The application connection. */
+    wa_connection *conn;
+    /** The provider-specific configuration member for this application. */
+    void *conf;
+    /** The application name. */
+    char *name;
+    /** The application root URL path. */
+    char *rpth;
+    /** The local expanded application path (if any). */
+    char *lpth;
+    /** The list of allowed (can be served by the web server) URL-patterns. */
+    wa_chain *allw;
+    /** The list of denied (can't be served by the web server) URL-patterns. */
+    wa_chain *deny;
+    /** Wether this web-application has been deployed or not. */
+    wa_boolean depl;
+};
+
+/**
+ * Allocate and set up a <code>wa_application</code> member.
+ *
+ * @param a Where the pointer to where the <code>wa_application</code> member
+ *          must be stored.
+ * @param n The application name. This parameter will be passed to the
+ *          application container as its unique selection key within its
+ *          array of deployable applications (for example the .war file name).
+ * @param p The root URL path of the web application to deploy.
+ * @return <b>NULL</b> on success or an error message on faliure.
+ */
+const char *wa_capplication(wa_application **a,
+                            const char *n,
+                            const char *p);
+
+/**
+ * Allocate and set up a <code>wa_virtualhost</code> member.
+ *
+ * @param h The pointer to where the <code>wa_virtualhost</code> member must
+ *          be stored.
+ * @param n The virtual host base name.
+ * @param p The virtual host primary port.
+ * @return <b>NULL</b> on success or an error message on faliure.
+ */
+const char *wa_cvirtualhost(wa_virtualhost **h,
+                            const char *n,
+                            int p);
+
+/**
+ * Allocate and set up a <code>wa_connection</code> member.
+ *
+ * @param c Where the pointer to where the <code>wa_connection</code> member
+ *          must be stored.
+ * @param n The connection name.
+ * @param p The connection provider name.
+ * @param a The connection provider parameter from a configuration file.
+ * @return <b>NULL</b> on success or an error message on faliure.
+ */
+const char *wa_cconnection(wa_connection **c,
+                           const char *n,
+                           const char *p,
+                           const char *a);
+
+#endif /* ifndef _WA_CONFIG_H_ */
diff --git a/connectors/webapp/include/wa_main.h b/connectors/webapp/include/wa_main.h
new file mode 100644
index 0000000..ff0864c
--- /dev/null
+++ b/connectors/webapp/include/wa_main.h
@@ -0,0 +1,213 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * @package Main
+ * @author  Pier Fumagalli <mailto:pier@betaversion.org>
+ * @version $Id$
+ */
+#ifndef _WA_MAIN_H_
+#define _WA_MAIN_H_
+
+/**
+ * A simple chain structure holding lists of pointers.
+ */
+struct wa_chain {
+    /** The pointer to the current element in the chain. */
+    void *curr;
+    /**
+     * The pointer to the next chain structure containing the next element
+     * or <b>NULL</b> if this is the last element in the chain. */
+    wa_chain *next;
+};
+
+/**
+ * The WebApp Library connection provider structure.
+ * <br>
+ * This structure contains all data and function pointers to be implemented
+ * by a connection provider.
+ */
+struct wa_provider {
+    /**
+     * The name of this provider.
+     */
+    const char *name;
+
+    /**
+     * Initialize a provider.
+     * <br>
+     * This function is called when the WebApp Library is initialized, before
+     * any web application is deployed.
+     *
+     * @return An error message or NULL.
+     */
+    const char *(*init)(void);
+
+    /**
+     * Notify the provider of its imminent startup.
+     * <br>
+     * After all configurations are generated, this function is called to
+     * notify the provider to expect requests to be handled.
+     */
+    void (*startup)(void);
+
+    /**
+     * Cleans up all resources allocated by the provider.
+     * <br>
+     * The provider is notified that no further requests will be handled, and
+     * the WebApp library is being shut down.
+     */
+    void (*shutdown)(void);
+
+    /**
+     * Configure a connection with the parameter from the web server
+     * configuration file.
+     *
+     * @param conn The connection to configure.
+     * @param param The extra parameter from web server configuration.
+     * @return An error message or NULL.
+     */
+    const char *(*connect)(wa_connection *conn, const char *param);
+
+    /**
+     * Receive notification of the deployment of an application.
+     *
+     * @param appl The application being deployed.
+     * @return An error message or NULL.
+     */
+    const char *(*deploy)(wa_application *appl);
+
+    /**
+     * Describe the configuration member found in a connection.
+     *
+     * @param conn The connection for wich a description must be produced.
+     * @param pool The memory pool where the returned string must be allocated.
+     * @return The description for a connection configuration or <b>NULL</b>.
+     */
+    char *(*conninfo)(wa_connection *conn, apr_pool_t *pool);
+
+    /**
+     * Describe the configuration member found in a web application.
+     *
+     * @param appl The application for wich a description must be produced.
+     * @param pool The memory pool where the returned string must be allocated.
+     * @return The description for an application configuration or <b>NULL</b>.
+     */
+    char *(*applinfo)(wa_application *appl, apr_pool_t *pool);
+
+    /**
+     * Handle a connection from the web server.
+     *
+     * @param req The request data.
+     * @param appl The application associated with the request.
+     * @param return The HTTP status code of the response.
+     */
+    int (*handle)(wa_request *req, wa_application *appl);
+};
+
+/**
+ * Initialize the WebApp Library.
+ * This function must be called <b>before</b> any other calls to any other
+ * function to set up the APR and WebApp Library internals. If any other
+ * function is called before this function has been invoked will result in
+ * impredictable results.
+ *
+ * @return <b>NULL</b> on success or an error message on faliure.
+ */
+const char *wa_init(void);
+
+/**
+ * Clean up the WebApp Library.
+ * This function releases all memory and resouces used by the WebApp library
+ * and must be called before the underlying web server process exits. Any call
+ * to any other WebApp Library function after this function has been invoked
+ * will result in impredictable results.
+ */
+void wa_startup(void);
+
+/**
+ * Clean up the WebApp Library.
+ * This function releases all memory and resouces used by the WebApp library
+ * and must be called before the underlying web server process exits. Any call
+ * to any other WebApp Library function after this function has been invoked
+ * will result in impredictable results.
+ */
+void wa_shutdown(void);
+
+/**
+ * Deploy a web-application.
+ *
+ * @param a The <code>wa_application</code> member of the web-application to
+ *          deploy.
+ * @param h The <code>wa_virtualhost</code> member of the host under which the
+ *          web-application has to be deployed.
+ * @param c The <code>wa_connection</code> member of the connection used to
+ *          reach the application.
+ * @return <b>NULL</b> on success or an error message on faliure.
+ */
+const char *wa_deploy(wa_application *a,
+                      wa_virtualhost *h,
+                      wa_connection *c);
+
+/**
+ * Dump some debugging information.
+ *
+ * @param f The file where this function was called.
+ * @param l The line number where this function was called.
+ * @param fmt The format string of the debug message (printf style).
+ * @param others The parameters to the format string.
+ */
+void wa_debug(const char *f, const int l, const char *fmt, ...);
+
+/**
+ * Report an error to the web-server log file.
+ * <br>
+ * NOTE: This function is _NOT_ defined in the WebApp library. It _MUST_ be
+ * defined and implemented within the web-server module.
+ *
+ * @param f The file where this function was called.
+ * @param l The line number where this function was called.
+ * @param fmt The format string of the debug message (printf style).
+ * @param others The parameters to the format string.
+ */
+void wa_log(const char *f, const int l, const char *fmt, ...);
+
+/**
+ * The WebApp library memory pool.
+ */
+extern apr_pool_t *wa_pool;
+
+/**
+ * The configuration member of the library.
+ * <br>
+ * This list of <code>wa_chain</code> structure contain the configuration of
+ * the WebApp Library in the form of all deployed <code>wa_application</code>,
+ * <code>wa_virtualhost</code> and <code>wa_connection</code> structures.
+ * <br>
+ * The <code>curr</code> member in the <code>wa_chain</code> structure contains
+ * always a <code>wa_virtualhost</code> structure, from which all configured
+ * <code>wa_application</code> members and relative <code>wa_connection</code>
+ * members can be retrieved.
+ */
+extern wa_chain *wa_configuration;
+
+/**
+ * A <b>NULL</b>-terminated array of all compiled-in <code>wa_provider</code>
+ * members.
+ */
+extern wa_provider *wa_providers[];
+
+#endif /* ifndef _WA_MAIN_H_ */
diff --git a/connectors/webapp/include/wa_request.h b/connectors/webapp/include/wa_request.h
new file mode 100644
index 0000000..0d83d83
--- /dev/null
+++ b/connectors/webapp/include/wa_request.h
@@ -0,0 +1,158 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/**
+ * @package Request Handling
+ * @author  Pier Fumagalli <mailto:pier@betaversion.org>
+ * @version $Id$
+ */
+#ifndef _WA_REQUEST_H_
+#define _WA_REQUEST_H_
+
+/**
+ * The host description structure.
+ */
+struct wa_hostdata {
+    /** The host name. */
+    char *host;
+    /** The host address (as a string - no worries about IPv6) */
+    char *addr;
+    /** The port number. */
+    int port;
+};
+
+/**
+ * The SSL data structure.
+ */
+struct wa_ssldata {
+    /** The client certificate. */
+    char *cert;
+    /** The cipher algorithm. */
+    char *ciph;
+    /** The SSL session name. */
+    char *sess;
+    /** Number of bits in the key. */
+    int size;
+};
+
+/**
+ * The webserver request handler callback structure.
+ */
+struct wa_handler {
+    void (*log)(wa_request *r, const char *file, const int line, char *msg);
+    void (*setstatus)(wa_request *r, int status, char *message);
+    void (*setctype)(wa_request *r, char *type);
+    void (*setheader)(wa_request *r, char *name, char *value);
+    void (*commit)(wa_request *r);
+    void (*flush)(wa_request *r);
+    int (*read)(wa_request *r, char *buf, int len);
+    int (*write)(wa_request *r, char *buf, int len);
+};
+
+/**
+ * The WebApp Library HTTP request structure.
+ * <br>
+ * This structure encapsulates an HTTP request to be handled within the scope
+ * of one of the configured applications.
+ */
+struct wa_request {
+    /** The APR memory pool where this request is allocated. */
+    apr_pool_t *pool;
+    /** The request handler structure associated with this request. */
+    wa_handler *hand;
+    /** The web-server specific callback data.*/
+    void *data;
+    /** The server host data. */
+    wa_hostdata *serv;
+    /** The client host data. */
+    wa_hostdata *clnt;
+    /** The HTTP method (ex. GET, POST...). */
+    char *meth;
+    /** The HTTP request URI (ex. /webapp/index.html). */
+    char *ruri;
+    /** The URL-encoded list of HTTP query arguments from the request. */
+    char *args;
+    /** The HTTP protocol name and version (ex. HTTP/1.0, HTTP/1.1...). */
+    char *prot;
+    /** The HTTP request URL scheme (the part before ://, ex http, https). */
+    char *schm;
+    /** The remote user name or <b>NULL</b>. */
+    char *user;
+    /** The authentication method or <b>NULL</b>. */
+    char *auth;
+    /** The content length of this request. */
+    long clen;
+    /** The content type of this request. */
+    char *ctyp;
+    /** The number of bytes read out of this request body. */
+    long rlen;
+    /** The SSL data structure. */
+    wa_ssldata *ssld;
+    /** The current headers table. */
+    apr_table_t *hdrs;
+};
+
+/**
+ * Allocate a new request structure.
+ *
+ * @param r A pointer to where the newly allocated <code>wa_request</code>
+ *          structure must be allocated.
+ * @param h The web-server specific handler for this request.
+ * @param d The web-server specific data for this request.
+ * @return An error message on faliure or <b>NULL</b>.
+ */
+const char *wa_ralloc(wa_request **r, wa_handler *h, void *d);
+
+/**
+ * Clean up and free the memory used by a request structure.
+ *
+ * @param r The request structure to destroy.
+ * @return An error message on faliure or <b>NULL</b>.
+ */
+const char *wa_rfree(wa_request *r);
+
+/**
+ * Report an HTTP error to the client.
+ *
+ * @param r The WebApp Library request structure.
+ * @param s The HTTP response status number.
+ * @param fmt The message format string (printf style).
+ * @param ... The parameters to the format string.
+ * @return The HTTP result code of this operation.
+ */
+int wa_rerror(const char *file, const int line, wa_request *r, int s,
+              const char *fmt, ...);
+
+/**
+ * Invoke a request in a web application.
+ *
+ * @param r The WebApp Library request structure.
+ * @param a The application to which this request needs to be forwarded.
+ * @return The HTTP result code of this operation.
+ */
+int wa_rinvoke(wa_request *r, wa_application *a);
+
+void wa_rlog(wa_request *r, const char *f, const int l, const char *fmt, ...);
+void wa_rsetstatus(wa_request *r, int status, char *message);
+void wa_rsetctype(wa_request *r, char *type);
+void wa_rsetheader(wa_request *r, char *name, char *value);
+void wa_rcommit(wa_request *r);
+void wa_rflush(wa_request *r);
+int wa_rread(wa_request *r, char *buf, int len);
+int wa_rwrite(wa_request *r, char *buf, int len);
+int wa_rprintf(wa_request *r, const char *fmt, ...);
+
+#endif /* ifndef _WA_REQUEST_H_ */
diff --git a/connectors/webapp/include/wa_version.h b/connectors/webapp/include/wa_version.h
new file mode 100755
index 0000000..0f9c6d1
--- /dev/null
+++ b/connectors/webapp/include/wa_version.h
@@ -0,0 +1,26 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#ifndef _WA_VERSION_H_
+#define _WA_VERSION_H_
+
+#define WA_VENDOR   "Apache Software Foundation"
+#define WA_PRODUCT  "WebApp Module"
+#define WA_NAME     "mod_webapp"
+#define WA_REVISION "1.2.0-dev"
+#define WA_VERSION  WA_NAME "/" WA_REVISION
+
+#endif /* _WA_VERSION_H_ */
diff --git a/connectors/webapp/java/org/apache/catalina/connector/warp/Constants.java b/connectors/webapp/java/org/apache/catalina/connector/warp/Constants.java
new file mode 100644
index 0000000..a368237
--- /dev/null
+++ b/connectors/webapp/java/org/apache/catalina/connector/warp/Constants.java
@@ -0,0 +1,370 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.catalina.connector.warp;
+
+public class Constants {
+    /** Our package name. */
+    public static final String PACKAGE="org.apache.catalina.connector.warp";
+
+    /** Compile-in debug flag. */
+    public static final boolean DEBUG=false;
+
+    /**
+     * The WARP protocol major version.
+     */
+    public static final int VERS_MAJOR=0;
+
+    /**
+     * The WARP protocol minor version.
+     */
+    public static final int VERS_MINOR=10;
+
+    /**
+     * INVALID: The packet type hasn't been set yet.
+     */
+    public static final int TYPE_INVALID=-1;
+
+    /**
+     * ERROR: The last operation didn't completed correctly.
+     * <br>
+     * Payload description:<br>
+     * [string] An error message.<br>
+     */
+    public static final int TYPE_ERROR=0x00;
+
+    /**
+     * DISCONNECT: The connection is being closed.
+     * <br>
+     * No payload:<br>
+     */
+    public static final int TYPE_DISCONNECT=0xfe;
+
+    /**
+     * FATAL: A protocol error occourred, the connection must be closed.
+     * <br>
+     * Payload description:<br>
+     * [string] An error message.<br>
+     */
+    public static final int TYPE_FATAL=0xff;
+
+    /**
+     * CONF_WELCOME: The server issues this packet when a connection is
+     * opened. The server awaits for configuration information.
+     * <br>
+     * Payload description:<br>
+     * [ushort] Major protocol version.<br>
+     * [ushort] Minor protocol version.<br>
+     * [integer] The server unique-id.<br>
+     */
+    public static final int TYPE_CONF_WELCOME=0x01;
+
+    /**
+     * CONF_ENUM: The client requests to the server to enumerate all
+     * web-applications available for a specified virtual host. In response
+     * to this request, the server replies with a set of CONF_ENUM messages
+     * terminated by a CONF_ENUM_DONE message.
+     * <br>
+     * Payload description:<br>
+     * [string] The virtual host name.<br>
+     * [ushort] The virtual host port.<br>
+     */
+    public static final int TYPE_CONF_ENUM=0x02;
+
+    /**
+     * CONF_ENUM_APPL: The server specifies the name of a web-application
+     * available for deployment in response to a CONF_ENUMERATE message.
+     * <br>
+     * Payload description:<br>
+     * [string] The web-application name.<br>
+     */
+    public static final int TYPE_CONF_ENUM_APPL=0x03;
+
+    /**
+     * CONF_ENUM_DONE: The server specifies all web-application names
+     * available for deployment for the host specified in the CONF_ENUM
+     * message have been transfered.
+     * <br>
+     * No payload:<br>
+     */
+    public static final int TYPE_CONF_ENUM_DONE=0x04;
+
+    /**
+     * CONF_DEPLOY: The client attempts deploy a web application.
+     * <br>
+     * Payload description:<br>
+     * [string] The application name.<br>
+     * [string] The virtual host name.<br>
+     * [ushort] The virtual host port.<br>
+     * [string] The web-application URL path.<br>
+     */
+    public static final int TYPE_CONF_DEPLOY=0x05;
+
+    /**
+     * CONF_APPLIC: The server replies to a CONF_DEPLOY message with the web
+     * application identifier of the configured application.
+     * <br>
+     * Payload description:<br>
+     * [integer] The web application unique id for this server.<br>
+     * [string] The web application real path (where it's expanded).<br>
+     */
+    public static final int TYPE_CONF_APPLIC=0x06;
+
+    /**
+     * CONF_MAP: The client requests to the server to enumerate all mappings
+     * for a specified web-application. The server replies to this message
+     * with a serie of MAP_ALLOW and MAP_DENY packets, terminated by a
+     * MAP_DONE packet.
+     * <br>
+     * Payload description:<br>
+     * [integer] The web application unique id for this server.<br>
+     */
+    public static final int TYPE_CONF_MAP=0x07;
+
+    /**
+     * CONF_MAP_ALLOW: The server replies to a CONF_MAP message with this
+     * packet to indicate a mapping to a static page, or a resource that
+     * can be served autonomously by the remote end (the web server).
+     * <br>
+     * Payload description:<br>
+     * [string] An url-pattern as defined by the Servlet specification.
+     */
+    public static final int TYPE_CONF_MAP_ALLOW=0x08;
+
+    /**
+     * CONF_MAP_DENY: The server replies to a CONF_MAP message with this
+     * packet to indicate a mapping to a resource that must be served by
+     * the server (servlet container).
+     * <br>
+     * Payload description:<br>
+     * [string] An url-pattern as defined by the Servlet specification.
+     */
+    public static final int TYPE_CONF_MAP_DENY=0x09;
+
+    /**
+     * CONF_MAP_DONE: The server replies to a CONF_MAP message with this
+     * packet to indicate that all servlet mappings have been successfully
+     * transfered to the other end
+     * <br>
+     * No payload:<br>
+     */
+    public static final int TYPE_CONF_MAP_DONE=0x0a;
+
+    /**
+     * CONF_DONE: Client issues this message when all configurations have been
+     * processed.
+     * <br>
+     * No payload:<br>
+     */
+    public static final int TYPE_CONF_DONE=0x0e;
+
+    /**
+     * CONF_PROCEED: Server issues this message in response to a CONF_DONE
+     * message, to acknowledge its readiness to accept requests.
+     * <br>
+     * No payload:<br>
+     */
+    public static final int TYPE_CONF_PROCEED=0x0f;
+
+    /**
+     * REQ_INIT: The client requests to the WARP server that a request is
+     * about to be processed.
+     * <br>
+     * Payload description:<br>
+     * [integer] The web-application unique ID for this server.<br>
+     * [string] The HTTP method used for this request.<br>
+     * [string] The request URI.<br>
+     * [string] The request query arguments.<br>
+     * [string] The request protocol (HTTP/1.0, HTTP/1.1...).<br>
+     */
+    public static final int TYPE_REQ_INIT=0x10;
+
+    /**
+     * REQ_CONTENT: The mime content type and length of this request.
+     * <br>
+     * Payload description:<br>
+     * [string] The MIME content type of this reques.<br>
+     * [integer] The content length of this request.<br>
+     */
+    public static final int TYPE_REQ_CONTENT=0x11;
+
+    /**
+     * REQ_SCHEME: The scheme description of this request.
+     * <br>
+     * Payload description:<br>
+     * [string] The scheme (part before :// in the URL) of this request.<br>
+     */
+    public static final int TYPE_REQ_SCHEME=0x12;
+
+    /**
+     * REQ_AUTH: Authentication information of the HTTP remote peer.
+     * <br>
+     * Payload description:<br>
+     * [string] The remote-user name.<br>
+     * [string] The authentication information.<br>
+     */
+    public static final int TYPE_REQ_AUTH=0x13;
+
+    /**
+     * REQ_HEADER: An HTTP request header.
+     * <br>
+     * Payload description:<br>
+     * [string] The header name.<br>
+     * [string] The header value.<br>
+     */
+    public static final int TYPE_REQ_HEADER=0x14;
+
+    /**
+     * REQ_SERVER: The HTTP server information.
+     * <br>
+     * Payload description:<br>
+     * [string] The HTTP server host name.
+     * [string] The HTTP server IP address.
+     * [ushort] The port receiving the HTTP request.
+     */
+    public static final int TYPE_REQ_SERVER=0x15;
+
+    /**
+     * REQ_CLIENT: The HTTP client (remote peer) information.
+     * <br>
+     * Payload description:<br>
+     * [string] The HTTP client host name.
+     * [string] The HTTP client IP address.
+     * [ushort] The remote port originating the HTTP request.
+     */
+    public static final int TYPE_REQ_CLIENT=0x16;
+
+    /**
+     * REQ_PROCEED: The client finished transmitting the request. The server
+     * can now proceed and process the request.
+     * <br>
+     * No payload.<br>
+     */
+    public static final int TYPE_REQ_PROCEED=0x1f;
+
+    /**
+     * RES_STATUS: The server replies with the HTTP response status for the
+     * request.
+     * <br>
+     * Payload description:<br>
+     * [ushort] The HTTP status of the response.<br>
+     * [string] The HTTP response message.<br>
+     */
+    public static final int TYPE_RES_STATUS=0x20;
+
+    /**
+     * RES_HEADER: An HTTP MIME response header to send to the client.
+     * <br>
+     * Payload description:<br>
+     * [string] The MIME header name.<br>
+     * [string] The MIME header value.<br>
+     */
+    public static final int TYPE_RES_HEADER=0x21;
+
+    /**
+     * RES_COMMIT: The server indicates that the first part of the response
+     * (HTTP status line and MIME headers) are to be committed to the client
+     * (remote peer).
+     * <br>
+     * No payload.<br>
+     */
+    public static final int TYPE_RES_COMMIT=0x2f;
+
+    /**
+     * RES_BODY: The HTTP response body.
+     * <br>
+     * Payload description:<br>
+     * [raw] A chunk of the response body.<br>
+     */
+    public static final int TYPE_RES_BODY=0x30;
+
+    /**
+     * RES_DONE: The server finished transmitting the response.
+     * <br>
+     * No payload.<br>
+     */
+    public static final int TYPE_RES_DONE=0x3f;
+
+    /**
+     * CBK_READ: A request callback. The WARP server queries the WARP client
+     * (HTTP server) to transmit a chunk of the request body.
+     * <br>
+     * Payload description:<br>
+     * [ushort] The number of bytes the server needs to transmit.<br>
+     */
+    public static final int TYPE_CBK_READ=0x40;
+
+    /**
+     * CBK_DATA: As requested by the WARP server, the WARP client (HTTP
+     * server) transmits a chunk of the request body.
+     * <br>
+     * Payload description:<br>
+     * [raw] A chunk of the request body.<br>
+     */
+    public static final int TYPE_CBK_DATA=0x41;
+
+    /**
+     * CBK_DATA: The WARP client (HTTP server) informs the WARP server that
+     * the request body has been fully transmitted.
+     * <br>
+     * No payload.<br>
+     */
+    public static final int TYPE_CBK_DONE=0x42;
+
+    /**
+     * ASK_SSL: The WARP server (Tomcat) asks the WARP client to
+     * transfer the basic SSL information (cypher, keysize and session).
+     * <br>
+     * No payload.<br>
+     */
+    public static final int TYPE_ASK_SSL=0x43;
+
+    /**
+     * ASK_SSL_CLIENT: The WARP server (Tomcat) asks the WARP server to
+     * transfer the client certificate.
+     * (just the first element of the chain and the webserver should request
+     * for it to the browser if possible).
+     * <br>
+     * No payload.<br>
+     */
+    public static final int TYPE_ASK_SSL_CLIENT=0x44;
+
+    /**
+     * REP_SSL_CERT: The client certificate (remote peer).
+     * <br>
+     * Payload description:<br>
+     * [string] The client certificate. (PEM format).
+     */
+    public static final int TYPE_REP_SSL_CERT=0x52;
+ 
+    /**
+     * REP_SSL: SSL information between 
+     * <br>
+     * Payload description:<br>
+     * [string] The cipher_suite.
+     * [string] The ssl session. (That is not in the spec's).
+     * [ushort] size of the algorithm (56-128).
+     */
+    public static final int TYPE_REP_SSL=0x53;
+ 
+    /**
+     * REP_SSL_NO: Request SSL information is not available.
+     * <br>
+     * No payload.<br>
+     */
+    public static final int TYPE_REP_SSL_NO=0x5F;
+}
+
diff --git a/connectors/webapp/java/org/apache/catalina/connector/warp/WarpCertificates.java b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpCertificates.java
new file mode 100644
index 0000000..edf9b54
--- /dev/null
+++ b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpCertificates.java
@@ -0,0 +1,55 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.catalina.connector.warp;
+ 
+import java.security.cert.X509Certificate;
+import java.security.cert.CertificateFactory;
+ 
+import java.io.ByteArrayInputStream;
+ 
+/*
+ * Certificates handling.
+ */
+ 
+public class WarpCertificates {
+    X509Certificate jsseCerts[] = null;
+    /**
+     * Create the certificate using the String.
+     */
+    public WarpCertificates(String certString) {
+        if (certString == null) return;
+
+        byte[] certData = certString.getBytes();
+        ByteArrayInputStream bais = new ByteArrayInputStream(certData);
+ 
+        // Fill the first element.
+        try {
+            CertificateFactory cf =
+                    CertificateFactory.getInstance("X.509");
+            X509Certificate cert = (X509Certificate)
+                    cf.generateCertificate(bais);
+            jsseCerts =  new X509Certificate[1];
+            jsseCerts[0] = cert;
+        } catch(java.security.cert.CertificateException e) {
+            // Certificate convertion failed.
+            return;
+        }
+    }
+    public X509Certificate [] getCertificates() {
+        return jsseCerts;
+    }
+}
diff --git a/connectors/webapp/java/org/apache/catalina/connector/warp/WarpConfigurationHandler.java b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpConfigurationHandler.java
new file mode 100644
index 0000000..ef5132c
--- /dev/null
+++ b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpConfigurationHandler.java
@@ -0,0 +1,284 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.catalina.connector.warp;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Deployer;
+import org.apache.catalina.Host;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.deploy.FilterMap;
+import org.apache.catalina.deploy.SecurityCollection;
+import org.apache.catalina.deploy.SecurityConstraint;
+
+public class WarpConfigurationHandler {
+
+    private static final String DEFAULT_SERVLET = 
+        "org.apache.catalina.servlets.DefaultServlet";
+
+    /* ==================================================================== */
+    /* Constructor                                                          */
+    /* ==================================================================== */
+
+    public WarpConfigurationHandler() {
+        super();
+    }
+
+    public boolean handle(WarpConnection connection, WarpPacket packet)
+    throws IOException {
+        WarpLogger logger=new WarpLogger(this);
+        logger.setContainer(connection.getConnector().getContainer());
+
+        // Prepare the Welcome packet
+        packet.setType(Constants.TYPE_CONF_WELCOME);
+        packet.writeUnsignedShort(Constants.VERS_MAJOR);
+        packet.writeUnsignedShort(Constants.VERS_MINOR);
+        packet.writeInteger(connection.getConnector().uniqueId);
+        connection.send(packet);
+
+        // Loop for configuration packets
+        while (true) {
+            connection.recv(packet);
+
+            switch (packet.getType()) {
+
+                case Constants.TYPE_CONF_DEPLOY: {
+                    String appl=packet.readString();
+                    String host=packet.readString();
+                    int port=packet.readUnsignedShort();
+                    String path=packet.readString();
+                    Context context=null;
+                    packet.reset();
+
+                    if (Constants.DEBUG)
+                        logger.log("Deploying web application \""+appl+"\" "+
+                                   "under <http://"+host+":"+port+path+">");
+                    try {
+                        context=deploy(connection,logger,appl,host,path);
+                    } catch (Exception e) {
+                        logger.log(e);
+                    }
+                    if (context==null) {
+                        String msg="Error deploying web application \""+appl+
+                                   "\" under <http://"+host+":"+port+path+">";
+                        logger.log(msg);
+                        packet.setType(Constants.TYPE_ERROR);
+                        packet.writeString(msg);
+                        connection.send(packet);
+                        break;
+                    }
+                    int k=connection.getConnector().applicationId(context);
+                    packet.setType(Constants.TYPE_CONF_APPLIC);
+                    packet.writeInteger(k);
+                    packet.writeString(context.getDocBase());
+                    connection.send(packet);
+                    if (Constants.DEBUG)
+                        logger.debug("Application \""+appl+"\" deployed "+
+                                     "under <http://"+host+":"+port+path+
+                                     "> with root="+context.getDocBase()+
+                                     " ID="+k);
+                    break;
+                }
+
+                case Constants.TYPE_CONF_MAP: {
+                    int id=packet.readInteger();
+                    Context context=connection.getConnector()
+                                    .applicationContext(id);
+
+                    if (context==null) {
+                        String msg="Invalid application ID for mappings "+id;
+                        logger.log(msg);
+                        packet.reset();
+                        packet.setType(Constants.TYPE_ERROR);
+                        packet.writeString(msg);
+                        connection.send(packet);
+                        break;
+                    }
+
+                    String smap[]=context.findServletMappings();
+                    if (smap!=null) {
+                        for (int x=0; x<smap.length; x++) {
+                            Container c=context.findChild(
+                                context.findServletMapping(smap[x]));
+                            packet.reset();
+                            packet.setType(Constants.TYPE_CONF_MAP_DENY);
+                            packet.writeString(smap[x]);
+
+                            if (c instanceof Wrapper) {
+                                String servlet=((Wrapper)c).getServletClass();
+                                if (DEFAULT_SERVLET.equals(servlet)) {
+                                    packet.setType(
+                                        Constants.TYPE_CONF_MAP_ALLOW);
+                                    if (Constants.DEBUG)
+                                        logger.debug("Servlet mapping \""+
+                                                     smap[x]+"\" allowed");
+                                } else if (Constants.DEBUG) {
+                                    logger.debug("Servlet mapping \""+smap[x]+
+                                                 "\" denied");
+                                }
+                            }
+                            connection.send(packet);
+                        }
+                    }
+
+                    FilterMap fmap[]=context.findFilterMaps();
+                    if (fmap!=null) {
+                        logger.log("Filter mappings ("+fmap.length+")");
+                        for (int x=0; x<fmap.length; x++) {
+                            String map=fmap[x].getURLPattern();
+                            if (map!=null) {
+                                if (Constants.DEBUG)
+                                    logger.debug("Filter mapping \""+map+
+                                                 "\" denied");
+                                packet.reset();
+                                packet.setType(Constants.TYPE_CONF_MAP_DENY);
+                                packet.writeString(map);
+                                connection.send(packet);
+                            }
+                        }
+                    }
+
+                    SecurityConstraint scon[]=context.findConstraints();
+                    if (scon!=null) {
+                        for (int x=0; x<scon.length; x++) {
+                            SecurityCollection col[]=scon[x].findCollections();
+                            if (col!=null) {
+                                for (int y=0; y<col.length; y++) {
+                                    String patt[]=col[y].findPatterns();
+                                    if (patt!=null) {
+                                        for (int q=0; q<patt.length; q++) {
+                                            packet.reset();
+                                            packet.setType(
+                                                Constants.TYPE_CONF_MAP_DENY);
+                                            packet.writeString(patt[q]);
+                                            connection.send(packet);
+                                            if (Constants.DEBUG) {
+                                                logger.debug("Seurity "+
+                                                    " mapping \""+patt[q]+
+                                                    "\"");
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    packet.reset();
+                    packet.setType(Constants.TYPE_CONF_MAP_DONE);
+                    connection.send(packet);
+                    break;
+                }
+
+                case Constants.TYPE_CONF_DONE: {
+                    return(true);
+                }
+
+                case Constants.TYPE_DISCONNECT: {
+                    return(false);
+                }
+
+                default: {
+                    String msg="Invalid packet with type "+packet.getType();
+                    logger.log(msg);
+                    packet.reset();
+                    packet.setType(Constants.TYPE_FATAL);
+                    packet.writeString(msg);
+                    connection.send(packet);
+                    return(false);
+                }
+            }
+        }
+    }
+
+    /** Deploy a web application */
+    private Context deploy(WarpConnection connection, WarpLogger logger,
+                           String applName, String hostName, String applPath)
+    throws IOException {
+        synchronized (connection.getConnector()) {
+
+        Container container=connection.getConnector().getContainer();
+
+        // the hostName should be in lowewr case (setName makes a toLowerCase).
+        Host host=(Host)container.findChild(hostName.toLowerCase());
+        if (host==null) {
+            WarpHost whost=new WarpHost();
+            whost.setName(hostName);
+            whost.setParent(container);
+            whost.setAppBase(connection.getConnector().getAppBase());
+            whost.setDebug(connection.getConnector().getDebug());
+            container.addChild(whost);
+            host=whost;
+            if (Constants.DEBUG)
+                logger.debug("Created new host "+host.getName());
+        } else if (Constants.DEBUG) {
+            logger.debug("Reusing instance of Host \""+host.getName()+"\"");
+        }
+
+        // TODO: Set up mapping mechanism for performance
+
+        if (applPath.endsWith("/"))
+            applPath=applPath.substring(0,applPath.length()-1);
+
+        Context appl=(Context)host.findChild(applPath);
+
+        if (appl==null) {
+
+            if (Constants.DEBUG)
+                logger.debug("No application for \""+applPath+"\"");
+
+            Deployer deployer=(Deployer)host;
+            File file=new File(applName);
+            if (!file.isAbsolute()) {
+                file=new File(host.getAppBase()+File.separator+applName);
+                if (!file.isAbsolute()) {
+               	    file=new File(System.getProperty("catalina.base"),
+                                  host.getAppBase()+File.separator+applName);
+                }
+            }
+
+            if (!file.exists()) {
+                logger.log("Cannot find \""+file.getPath()+"\" for appl. \""+
+                           applName+"\" host \""+host.getName()+"\"");
+                return(null);
+            }
+
+            String path=file.getCanonicalPath();
+            URL url=new URL("file",null,path);
+            if (path.toLowerCase().endsWith(".war"))
+                url=new URL("jar:"+url.toString()+"!/");
+
+            if (Constants.DEBUG)
+                logger.debug("Application URL \""+url.toString()+"\"");
+
+            deployer.install(applPath, url);
+            StandardContext context=null;
+            context=(StandardContext)deployer.findDeployedApp(applPath);
+            context.setDebug(connection.getConnector().getDebug());
+            return(context);
+        } else {
+            if (Constants.DEBUG)
+                logger.debug("Found application for \""+appl.getName()+"\"");
+            return(appl);
+        }
+        }
+    }
+}
diff --git a/connectors/webapp/java/org/apache/catalina/connector/warp/WarpConnection.java b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpConnection.java
new file mode 100644
index 0000000..5e3fdbe
--- /dev/null
+++ b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpConnection.java
@@ -0,0 +1,221 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.catalina.connector.warp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+
+public class WarpConnection implements LifecycleListener, Runnable {
+
+    /* ==================================================================== */
+    /* Instance variables                                                   */
+    /* ==================================================================== */
+
+    /* -------------------------------------------------------------------- */
+    /* Local variables */
+
+    /** Our socket input stream. */
+    private InputStream input=null;
+    /** Our socket output stream. */
+    private OutputStream output=null;
+    /** The started flag. */
+    private boolean started=false;
+    /** The local thread. */
+    private Thread thread=null;
+    /** Our logger. */
+    private WarpLogger logger=null;
+
+    /* -------------------------------------------------------------------- */
+    /* Bean variables */
+
+    /** The socket this connection is working on. */
+    private Socket socket=null;
+    /** The connector instance. */
+    private WarpConnector connector=null;
+
+    /* ==================================================================== */
+    /* Constructor                                                          */
+    /* ==================================================================== */
+
+    /**
+     * Construct a new instance of a <code>WarpConnection</code>.
+     */
+    public WarpConnection() {
+        super();
+        this.logger=new WarpLogger(this);
+    }
+
+    /* ==================================================================== */
+    /* Bean methods                                                         */
+    /* ==================================================================== */
+
+    /**
+     * Set the socket this connection is working on.
+     */
+    public void setSocket(Socket socket) {
+        this.socket=socket;
+    }
+
+    /**
+     * Return the socket this connection is working on.
+     */
+    public Socket getSocket() {
+        return(this.socket);
+    }
+
+    /**
+     * Set the <code>WarpConnector</code> associated with this connection.
+     */
+    public void setConnector(WarpConnector connector) {
+        this.connector=connector;
+        this.logger.setContainer(connector.getContainer());
+    }
+
+    /**
+     * Return the <code>WarpConnector</code> associated with this connection.
+     */
+    public WarpConnector getConnector() {
+        return(this.connector);
+    }
+
+    /* ==================================================================== */
+    /* Lifecycle methods                                                    */
+    /* ==================================================================== */
+
+    /**
+     * Get notified of events in the connector.
+     */
+    public void lifecycleEvent(LifecycleEvent event) {
+        if (Lifecycle.STOP_EVENT.equals(event.getType())) this.stop();
+    }
+
+    /**
+     * Start working on this connection.
+     */
+    public void start() {
+        synchronized(this) {
+            this.started=true;
+            this.thread=new Thread(this);
+            this.thread.start();
+        }
+    }
+
+    /**
+     * Stop all we're doing on the connection.
+     */
+    public void stop() {
+        synchronized(this) {
+            try {
+                this.started=false;
+                this.socket.close();
+                this.getConnector().removeLifecycleListener(this);
+            } catch (IOException e) {
+                logger.log("Cannot close socket",e);
+            }
+        }
+    }
+
+    /**
+     * Process data from the socket.
+     */
+    public void run() {
+        WarpPacket packet=new WarpPacket();
+
+        if (Constants.DEBUG) logger.debug("Connection starting");
+
+        try {
+            this.input=this.socket.getInputStream();
+            this.output=this.socket.getOutputStream();
+            if (!new WarpConfigurationHandler().handle(this,packet)) {
+                logger.log("Configuration handler returned false");
+                this.stop();
+            }
+            WarpRequestHandler requestHandler=new WarpRequestHandler();
+            while (requestHandler.handle(this,packet));
+        } catch (IOException e) {
+            logger.log("Exception on socket",e);
+        } finally {
+            this.stop();
+        }
+
+        if (Constants.DEBUG) logger.debug("Connection terminated");
+    }
+
+    /* ==================================================================== */
+    /* Public methods                                                       */
+    /* ==================================================================== */
+
+    /**
+     * Send a WARP packet over this connection.
+     */
+    public void send(WarpPacket packet)
+    throws IOException {
+        if (Constants.DEBUG) {
+            String typ=Integer.toHexString(packet.getType());
+            logger.debug(">> TYPE="+typ+" LENGTH="+packet.size);
+            //logger.debug(">> "+packet.dump());
+        }
+
+        this.output.write(packet.getType()&0x0ff);
+        this.output.write((packet.size>>8)&0x0ff);
+        this.output.write((packet.size>>0)&0x0ff);
+        this.output.write(packet.buffer,0,packet.size);
+        this.output.flush();
+        packet.reset();
+    }
+
+    /**
+     * Receive a WARP packet over this connection.
+     */
+    public void recv(WarpPacket packet)
+    throws IOException {
+        int t=this.input.read();
+        int l1=this.input.read();
+        int l2=this.input.read();
+
+        if ((t|l1|l2)==-1)
+            throw new IOException("Premature packet header end");
+
+        packet.reset();
+        packet.setType(t&0x0ff);
+        packet.size=(( l1 & 0x0ff ) << 8) | ( l2 & 0x0ff );
+
+        if (packet.size>0) {
+            int off=0;
+            int ret=0;
+            while (true) {
+                ret=this.input.read(packet.buffer,off,packet.size-off);
+                if (ret==-1)
+                    throw new IOException("Premature packet payload end");
+                off+=ret;
+                if(off==packet.size) break;
+            }
+        }
+
+        if (Constants.DEBUG) {
+            String typ=Integer.toHexString(packet.getType());
+            logger.debug("<< TYPE="+typ+" LENGTH="+packet.size);
+            // logger.debug("<< "+packet.dump());
+        }
+    }
+}
diff --git a/connectors/webapp/java/org/apache/catalina/connector/warp/WarpConnector.java b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpConnector.java
new file mode 100644
index 0000000..4bd0c48
--- /dev/null
+++ b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpConnector.java
@@ -0,0 +1,566 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.catalina.connector.warp;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Random;
+import java.util.Vector;
+
+import org.apache.catalina.Connector;
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Request;
+import org.apache.catalina.Response;
+import org.apache.catalina.Service;
+import org.apache.catalina.net.DefaultServerSocketFactory;
+import org.apache.catalina.net.ServerSocketFactory;
+import org.apache.catalina.util.LifecycleSupport;
+
+public class WarpConnector implements Connector, Lifecycle, Runnable {
+
+    /* ==================================================================== */
+    /* Instance variables                                                   */
+    /* ==================================================================== */
+
+    /* -------------------------------------------------------------------- */
+    /* Local variables */
+
+    /** The running thread accepting connections */
+    private Thread thread=null;
+    /** The server socket. */
+    private ServerSocket server=null;
+    /** Our <code>WarpLogger</code>. */
+    private WarpLogger logger=null;
+    /** Our list of deployed web applications. */
+    private Vector applications=new Vector();
+    /** The unique ID of this connector instance. */
+    protected int uniqueId=-1;
+
+    /* -------------------------------------------------------------------- */
+    /* Bean variables */
+
+    /** The <code>Container</code> instance processing requests. */
+    private Container container=null;
+    /** The "enable DNS lookups" flag. */
+    private boolean enableLookups=false;
+    /** The <code>ServerSocketFactory</code> used by this connector. */
+    private ServerSocketFactory factory=null;
+    /** The redirect port value for SSL requests. */
+    private int redirectPort=443;
+    /** The request scheme value. */
+    private String scheme="warp";
+    /** The secure flag of this <code>Connector</code>. */
+    private boolean secure=false;
+    /** The <code>Service</code> we are associated with (if any). */
+    private Service service=null;
+    /** Descriptive information of this <code>Connector</code>. */
+    private String info=null;
+    /** The address we need to bind to. */
+    private String address=null;
+    /** The port we need to bind to. */
+    private int port=8008;
+    /** The server socket backlog length. */
+    private int acceptCount=10;
+    /** The server appBase for hosts created via WARP. */
+    private String appBase="webapps";
+    /** The debug level. */
+    private int debug=0;
+
+    /* -------------------------------------------------------------------- */
+    /* Lifecycle variables */
+
+    /** The lifecycle event support for this component. */
+    private LifecycleSupport lifecycle=new LifecycleSupport(this);
+    /** The "initialized" flag. */
+    private boolean initialized=false;
+    /** The "started" flag. */
+    private boolean started=false;
+
+    /* ==================================================================== */
+    /* Constructor                                                          */
+    /* ==================================================================== */
+
+    /**
+     * Construct a new instance of a <code>WarpConnector</code>.
+     */
+    public WarpConnector() {
+        super();
+        this.logger=new WarpLogger(this);
+        this.uniqueId=new Random().nextInt();
+        if (Constants.DEBUG)
+            logger.debug("Instance created (ID="+this.uniqueId+")");
+    }
+
+    /* ==================================================================== */
+    /* Bean methods                                                         */
+    /* ==================================================================== */
+
+    /**
+     * Return the <code>Container</code> instance which will process all
+     * requests received by this <code>Connector</code>.
+     */
+    public Container getContainer() {
+        return(this.container);
+    }
+
+    /**
+     * Set the <code>Container</code> instance which will process all requests
+     * received by this <code>Connector</code>.
+     *
+     * @param container The new Container to use
+     */
+    public void setContainer(Container container) {
+        this.container=container;
+        this.logger.setContainer(container);
+
+        if (Constants.DEBUG) {
+            if (container==null) logger.debug("Setting null container");
+            else logger.debug("Setting container "+container.getClass());
+        }
+    }
+
+    /**
+     * Return the "enable DNS lookups" flag.
+     */
+    public boolean getEnableLookups() {
+        return(this.enableLookups);
+    }
+
+    /**
+     * Set the "enable DNS lookups" flag.
+     *
+     * @param enableLookups The new "enable DNS lookups" flag value
+     */
+    public void setEnableLookups(boolean enableLookups) {
+        this.enableLookups=enableLookups;
+
+        if (Constants.DEBUG) logger.debug("Setting lookup to "+enableLookups);
+    }
+
+    /**
+     * Return the <code>ServerSocketFactory</code> used by this
+     * <code>Connector</code> to generate <code>ServerSocket</code> instances.
+     */
+    public ServerSocketFactory getFactory() {
+        if (this.factory==null) {
+            synchronized(this) {
+                if (Constants.DEBUG) logger.debug("Creating factory");
+                this.factory=new DefaultServerSocketFactory();
+            }
+        }
+        return(this.factory);
+    }
+
+    /**
+     * Set the <code>ServerSocketFactory</code> used by this
+     * <code>Connector</code> to generate <code>ServerSocket</code> instances.
+     *
+     * @param factory The new server socket factory
+     */
+    public void setFactory(ServerSocketFactory factory) {
+        if (factory==null) throw new NullPointerException();
+        this.factory=factory;
+
+        if (Constants.DEBUG)
+            logger.debug("Setting factory "+factory.getClass().getName());
+    }
+
+    /**
+     * Return the port number to which a request should be redirected if
+     * it comes in on a non-SSL port and is subject to a security constraint
+     * with a transport guarantee that requires SSL.
+     */
+    public int getRedirectPort() {
+        return(this.redirectPort);
+    }
+
+    /**
+     * Set the redirect port number.
+     *
+     * @param redirectPort The redirect port number (non-SSL to SSL)
+     */
+    public void setRedirectPort(int redirectPort) {
+        if ((redirectPort<1) || (redirectPort>65535))
+            throw new IllegalArgumentException("Invalid port "+redirectPort);
+        this.redirectPort=redirectPort;
+
+        if (Constants.DEBUG)
+            logger.debug("Setting redirection port to "+redirectPort);
+    }
+
+    /**
+     * Return the scheme that will be assigned to requests received
+     * through this connector.  Default value is "warp".
+     */
+    public String getScheme() {
+        return(this.scheme);
+    }
+
+    /**
+     * Set the scheme that will be assigned to requests received through
+     * this connector.
+     *
+     * @param scheme The new scheme
+     */
+    public void setScheme(String scheme) {
+        if (scheme==null) throw new NullPointerException();
+        this.scheme=scheme;
+
+        if (Constants.DEBUG) logger.debug("Setting scheme to "+scheme);
+    }
+
+    /**
+     * Return the secure connection flag that will be assigned to requests
+     * received through this connector.  Default value is "false".
+     */
+    public boolean getSecure() {
+        return(this.secure);
+    }
+
+    /**
+     * Set the secure connection flag that will be assigned to requests
+     * received through this connector.
+     *
+     * @param secure The new secure connection flag
+     */
+    public void setSecure(boolean secure) {
+        this.secure=secure;
+
+        if (Constants.DEBUG) logger.debug("Setting secure to "+secure);
+    }
+
+    /**
+     * Return the <code>Service</code> with which we are associated (if any).
+     */
+    public Service getService() {
+
+        return (this.service);
+
+    }
+
+
+    /**
+     * Set the <code>Service</code> with which we are associated (if any).
+     *
+     * @param service The service that owns this Engine
+     */
+    public void setService(Service service) {
+
+        this.service = service;
+
+    }
+
+
+    /**
+     * Return descriptive information about this <code>Connector</code>.
+     */
+    public String getInfo() {
+        if (this.info==null) {
+            synchronized(this) {
+                this.info=this.getClass().getName()+"/"+
+                          Constants.VERS_MINOR+Constants.VERS_MAJOR;
+            }
+        }
+        return(this.info);
+    }
+
+    /**
+     * Set descriptive information about this <code>Connector</code>.
+     */
+    public void setInfo(String info) {
+        if (info==null) throw new NullPointerException();
+        this.info=info;
+
+        if (Constants.DEBUG) logger.debug("Setting info to "+info);
+    }
+
+    /**
+     * Return the IP address to which this <code>Connector</code> will bind to.
+     */
+    public String getAddress() {
+        return(this.address);
+    }
+
+    /**
+     * Set the IP address to which this <code>Connector</code> will bind to.
+     *
+     * @param address The bind IP address
+     */
+    public void setAddress(String address) {
+        this.address=address;
+
+        if (Constants.DEBUG) logger.debug("Setting address to "+address);
+    }
+
+    /**
+     * Return the port to which this <code>Connector</code> will bind to.
+     */
+    public int getPort() {
+        return(this.port);
+    }
+
+    /**
+     * Set the port to which this <code>Connector</code> will bind to.
+     * 
+     * @param port The bind port
+     */
+    public void setPort(int port) {
+        this.port=port;
+    }
+
+    /**
+     * Set the IP address to which this <code>Connector</code> will bind to.
+     *
+     * @param address The bind IP address
+     */
+    public void setAddress(int port) {
+        if ((port<1) || (port>65535))
+            throw new IllegalArgumentException("Invalid port "+port);
+        this.port=port;
+
+        if (Constants.DEBUG) logger.debug("Setting port to "+port);
+    }
+
+    /**
+     * Return the accept count for this Connector.
+     */
+    public int getAcceptCount() {
+        return (this.acceptCount);
+    }
+
+
+    /**
+     * Set the accept count for this Connector.
+     *
+     * @param count The new accept count
+     */
+    public void setAcceptCount(int count) {
+        this.acceptCount = count;
+
+        if (Constants.DEBUG) logger.debug("Setting acceptCount to "+count);
+    }
+
+    /**
+     * Get the applications base directory for hosts created via WARP.
+     */
+    public String getAppBase() {
+        return (this.appBase);
+    }
+
+
+    /**
+     * Set the applications base directory for hosts created via WARP.
+     *
+     * @param appBase The appbase property.
+     */
+    public void setAppBase(String appBase) {
+        this.appBase = appBase;
+
+        if (Constants.DEBUG) logger.debug("Setting appBase to "+appBase);
+    }
+
+    /**
+     * Return the debug level.
+     */
+    public int getDebug() {
+        return(this.debug);
+    }
+
+    /**
+     * Set the debug level.
+     */
+    public void setDebug(int debug) {
+        this.debug=debug;
+    }
+
+    /* ==================================================================== */
+    /* Lifecycle methods                                                    */
+    /* ==================================================================== */
+
+    /**
+     * Add a <code>LifecycleEvent</code> listener to this
+     * <code>Connector</code>.
+     *
+     * @param listener The listener to add
+     */
+    public void addLifecycleListener(LifecycleListener listener) {
+        lifecycle.addLifecycleListener(listener);
+    }
+
+    /**
+     * Get the lifecycle listeners associated with this lifecycle. If this 
+     * Lifecycle has no listeners registered, a zero-length array is returned.
+     */
+    public LifecycleListener[] findLifecycleListeners() {
+        return null; // FIXME: lifecycle.findLifecycleListeners();
+    }
+
+    /**
+     * Remove a <code>LifecycleEvent</code> listener from this
+     * <code>Connector</code>.
+     *
+     * @param listener The listener to remove
+     */
+    public void removeLifecycleListener(LifecycleListener listener) {
+        lifecycle.removeLifecycleListener(listener);
+    }
+
+    /**
+     * Initialize this connector (create ServerSocket here!)
+     */
+    public void initialize()
+    throws LifecycleException {
+        if (initialized)
+            throw new LifecycleException("Already initialized");
+        this.initialized=true;
+
+        // Get a hold on a server socket
+        try {
+            ServerSocketFactory fact=this.getFactory();
+            int port=this.getPort();
+            int accc=this.getAcceptCount();
+
+            if (this.getAddress()==null) {
+                this.server=fact.createSocket(port,accc);
+            } else {
+                InetAddress addr=InetAddress.getByName(this.getAddress());
+                this.server=fact.createSocket(port,accc,addr);
+            }
+        } catch (Exception e) {
+            throw new LifecycleException("Error creating server socket ("+
+                e.getClass().getName()+")",e);
+        }
+    }
+
+    /**
+     * Start accepting connections by this <code>Connector</code>.
+     */
+    public void start() throws LifecycleException {
+        if (!initialized) this.initialize();
+        if (started) throw new LifecycleException("Already started");
+
+        // Can't get a hold of a server socket
+        if (this.server==null)
+            throw new LifecycleException("Server socket not created");
+
+        lifecycle.fireLifecycleEvent(START_EVENT, null);
+
+        this.started = true;
+
+        this.thread=new Thread(this);
+        this.thread.setDaemon(true);
+        this.thread.start();
+    }
+
+    /**
+     * Stop accepting connections by this <code>Connector</code>.
+     */
+    public void stop() throws LifecycleException {
+        if (!started) throw new LifecycleException("Not started");
+
+        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+
+        this.started = false;
+
+        if (this.server!=null) try {
+            this.server.close();
+        } catch (IOException e) {
+            logger.log("Cannot close ServerSocket",e);
+        }
+    }
+
+    /**
+     * Check whether this service was started or not.
+     */
+    public boolean isStarted() {
+        return(this.started);
+    }
+
+    /* ==================================================================== */
+    /* Public methods                                                       */
+    /* ==================================================================== */
+
+    /**
+     * Return the application ID for a given <code>Context</code>.
+     */
+    protected int applicationId(Context context) {
+        int id=this.applications.indexOf(context);
+        if (id==-1) {
+            this.applications.add(context);
+            id=this.applications.indexOf(context);
+        }
+        return(id);
+    }
+
+    /**
+     * Return the application for a given ID.
+     */
+    protected Context applicationContext(int id) {
+        try {
+            return((Context)this.applications.elementAt(id));
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return(null);
+        }
+    }
+
+    /**
+     * Create (or allocate) and return a Request object suitable for
+     * specifying the contents of a Request to the responsible Container.
+     */
+    public Request createRequest() {
+        return(null);
+    }
+
+    /**
+     * Create (or allocate) and return a Response object suitable for
+     * receiving the contents of a Response from the responsible Container.
+     */
+    public Response createResponse() {
+        return(null);
+    }
+
+    /**
+     * Start accepting WARP requests from the network.
+     */
+    public void run() {
+        // Start accepting connections
+        try {
+            while (this.isStarted()) {
+                Socket sock=this.server.accept();
+                InetAddress raddr=sock.getInetAddress();
+                InetAddress laddr=sock.getLocalAddress();
+                int rport=sock.getPort();
+                int lport=sock.getLocalPort();
+                logger.log("Connection from "+raddr+":"+rport+" to "+laddr+
+                           ":"+lport);
+                WarpConnection conn=new WarpConnection();
+                conn.setConnector(this);
+                conn.setSocket(sock);
+                this.addLifecycleListener(conn);
+                conn.start();
+            }
+        } catch (IOException e) {
+            logger.log("Error accepting requests",e);
+        }
+    }
+}
diff --git a/connectors/webapp/java/org/apache/catalina/connector/warp/WarpEngine.java b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpEngine.java
new file mode 100644
index 0000000..3b0f396
--- /dev/null
+++ b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpEngine.java
@@ -0,0 +1,43 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.catalina.connector.warp;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Host;
+import org.apache.catalina.Request;
+import org.apache.catalina.core.StandardEngine;
+
+public class WarpEngine extends StandardEngine {
+    public Container map(Request request, boolean update) {
+        if (Constants.DEBUG) this.log("Mapping request");
+        if (request instanceof WarpRequest) {
+            WarpRequest wreq = (WarpRequest)request;
+            Host host = wreq.getHost();
+            if (update) {
+                if (request.getRequest().getServerName() == null) {
+                    request.setServerName(host.getName());
+                } else {
+                    request.setServerName
+                        (request.getRequest().getServerName());
+                }
+            }
+            return (host);
+        } else {
+            return (super.map(request,update));
+        }
+    }
+}
diff --git a/connectors/webapp/java/org/apache/catalina/connector/warp/WarpHost.java b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpHost.java
new file mode 100644
index 0000000..2094b82
--- /dev/null
+++ b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpHost.java
@@ -0,0 +1,35 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.catalina.connector.warp;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Request;
+import org.apache.catalina.core.StandardHost;
+
+public class WarpHost extends StandardHost {
+    public Container map(Request request, boolean update) {
+        Context context=null;
+        if (Constants.DEBUG) this.log("Mapping request for Host");
+        if (request instanceof WarpRequest)
+            context=((WarpRequest)request).getContext();
+        else
+            context=(Context)super.map(request,update);
+
+        return(context);
+    }
+}
diff --git a/connectors/webapp/java/org/apache/catalina/connector/warp/WarpLogger.java b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpLogger.java
new file mode 100644
index 0000000..048a144
--- /dev/null
+++ b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpLogger.java
@@ -0,0 +1,172 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.catalina.connector.warp;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Logger;
+
+public class WarpLogger {
+
+    /* ==================================================================== */
+    /* Variables                                                            */
+    /* ==================================================================== */
+
+    /* -------------------------------------------------------------------- */
+    /* Bean variables */
+
+    /** The <code>Container</code> instance processing requests. */
+    private Container container=null;
+    /** The source of log messages for this logger. */
+    private Object source=null;
+
+    /* ==================================================================== */
+    /* Constructor                                                          */
+    /* ==================================================================== */
+
+    /** Deny empty construction. */
+    private WarpLogger() {
+        super();
+    }
+
+    /**
+     * Construct a new instance of a <code>WarpConnector</code>.
+     */
+    public WarpLogger(Object source) {
+        super();
+        this.source=source;
+    }
+
+    /* ==================================================================== */
+    /* Bean methods                                                         */
+    /* ==================================================================== */
+
+    /**
+     * Return the <code>Container</code> instance which will process all
+     * requests received by this <code>Connector</code>.
+     */
+    public Container getContainer() {
+        return(this.container);
+    }
+
+    /**
+     * Set the <code>Container</code> instance which will process all requests
+     * received by this <code>Connector</code>.
+     *
+     * @param container The new Container to use
+     */
+    public void setContainer(Container container) {
+        this.container=container;
+    }
+
+    /* ==================================================================== */
+    /* Logging and debugging methods                                        */
+    /* ==================================================================== */
+
+    /** Log to the container logger with the specified level or to stderr */
+    private void log(String msg, Exception exc, int lev) {
+        if (this.container==null) {
+            if (Constants.DEBUG) dump(msg,exc);
+            return;
+        }
+
+        Logger logg=this.container.getLogger();
+        if (logg==null) {
+            if (Constants.DEBUG) dump(msg,exc);
+            return;
+        }
+
+        String cls="["+this.source.getClass().getName()+"] ";
+        if (msg==null) msg=cls;
+        else msg=cls.concat(msg);
+
+        if (exc==null) logg.log(msg,lev);
+        else logg.log(msg,exc,lev);
+    }
+
+    /** Invoked when we can't get a hold on the logger, dump to stderr */
+    private void dump(String message, Exception exception) {
+        String cls="["+this.source.getClass().getName()+"] ";
+
+        if (message!=null) {
+            System.err.print(cls);
+            System.err.println(message);
+        }
+        if (exception!=null) {
+            System.err.print(cls);
+            exception.printStackTrace(System.err);
+        }
+    }
+
+    /**
+     * If Constants.DEBUG was set true at compilation time, dump a debug
+     * message to Standard Error.
+     *
+     * @param message The message to dump.
+     */
+    protected void debug(String message) {
+        if (Constants.DEBUG) this.log(message,null,Logger.DEBUG);
+    }
+
+    /**
+     * If Constants.DEBUG was set true at compilation time, dump an exception
+     * stack trace to Standard Error.
+     *
+     * @param exception The exception to dump.
+     */
+    protected void debug(Exception exception) {
+        if (Constants.DEBUG) this.log(null,exception,Logger.DEBUG);
+    }
+
+    /**
+     * If Constants.DEBUG was set true at compilation time, dump a debug
+     * message and a related exception stack trace to Standard Error.
+     *
+     * @param exception The exception to dump.
+     * @param message The message to dump.
+     */
+    protected void debug(String message, Exception exception) {
+        if (Constants.DEBUG) this.log(message,exception,Logger.DEBUG);
+    }
+
+    /**
+     * Log a message.
+     *
+     * @param message The message to log.
+     */
+    protected void log(String message) {
+        this.log(message,null,Logger.ERROR);
+    }
+
+    /**
+     * Log an exception.
+     *
+     * @param exception The exception to log.
+     */
+    protected void log(Exception exception) {
+        this.log(null,exception,Logger.ERROR);
+    }
+
+    /**
+     * Log an exception and related message.
+     *
+     * @param exception The exception to log.
+     * @param message The message to log.
+     */
+    protected void log(String message, Exception exception) {
+        this.log(message,exception,Logger.ERROR);
+    }
+}
diff --git a/connectors/webapp/java/org/apache/catalina/connector/warp/WarpPacket.java b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpPacket.java
new file mode 100644
index 0000000..b830511
--- /dev/null
+++ b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpPacket.java
@@ -0,0 +1,204 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.catalina.connector.warp;
+
+import java.io.UnsupportedEncodingException;
+
+public class WarpPacket {
+
+    /** This packet's data buffer */
+    protected byte buffer[]=null;
+    /** Number of bytes stored in the buffer */
+    protected int size=0;
+
+    /* Pointer to the last byte read in the buffer */
+    protected int pointer=0;
+    /* Type of this packet */
+    private int type=-1;
+    /* Maximum value for a 16 bit unsigned value (0x0ffff +1) */
+    private static final int MAX_LENGTH=65535;
+
+    /**
+     * Construct a new WarpPacket instance.
+     */
+    public WarpPacket() {
+        super();
+        this.buffer=new byte[MAX_LENGTH];
+        this.reset();
+    }
+
+    /**
+     * Reset this packet.
+     */
+    public void reset() {
+        this.pointer=0;
+        this.size=0;
+        this.type=Constants.TYPE_INVALID;
+    }
+
+    /**
+     * Set this packet type.
+     *
+     * @param type The type of this packet.
+     */
+    public void setType(int type) {
+        this.type=type;
+    }
+
+    /**
+     * Return the type of this packet.
+     *
+     * @return The type of this packet.
+     */
+    public int getType() {
+        return(this.type);
+    }
+
+    /**
+     * Write an unsigned short value (16 bit) in the packet buffer.
+     *
+     * @param value The unsigned short value to write.
+     * @exception IllegalArgumentException If the value is negative or greater
+     *                than 65535.
+     * @exception ArrayIndexOutOfBoundsException If the packet buffer cannot
+     *                contain the new value.
+     */
+    public void writeUnsignedShort(int value) {
+        if (value<0)
+            throw new IllegalArgumentException("Negative unsigned short");
+        if (value>65535)
+            throw new IllegalArgumentException("Unsigned short is too big");
+
+        if ((this.size+2)>=MAX_LENGTH)
+            throw new ArrayIndexOutOfBoundsException("Too much data");
+
+        this.buffer[this.size++]=(byte) ((value>>8)&0x0ff);
+        this.buffer[this.size++]=(byte) ((value>>0)&0x0ff);
+    }
+
+    /**
+     * Write a signed integer value (32 bit) in the packet buffer.
+     *
+     * @param value The signed integer value to write.
+     * @exception ArrayIndexOutOfBoundsException If the packet buffer cannot
+     *                contain the new value.
+     */
+    public void writeInteger(int value) {
+        if ((this.size+4)>=MAX_LENGTH)
+            throw new ArrayIndexOutOfBoundsException("Too much data");
+
+        this.buffer[this.size++]=(byte) ((value>>24)&0x0ff);
+        this.buffer[this.size++]=(byte) ((value>>16)&0x0ff);
+        this.buffer[this.size++]=(byte) ((value>>8)&0x0ff);
+        this.buffer[this.size++]=(byte) ((value>>0)&0x0ff);
+    }
+
+    /**
+     * Write a string into the packet buffer.
+     *
+     * @param string The string to write into the packet buffer.
+     * @exception ArrayIndexOutOfBoundsException If the packet buffer cannot
+     *                contain the new value.
+     * @exception RuntimeException If the platform doesn't support UTF-8
+     *                encoding.
+     */
+    public void writeString(String string) {
+        try {
+            if (string==null) string="";
+            byte temp[]=string.getBytes("UTF-8");
+            if ((this.size+temp.length+2)>MAX_LENGTH)
+                throw new ArrayIndexOutOfBoundsException("Too much data");
+
+            this.writeUnsignedShort(temp.length);
+            System.arraycopy(temp,0,this.buffer,this.size,temp.length);
+            this.size+=temp.length;
+        } catch (UnsupportedEncodingException s) {
+            throw new RuntimeException("Unsupported encoding UTF-8");
+        }
+    }
+
+    /**
+     * Read an unsigned short value (16 bit) from the packet buffer.
+     *
+     * @return The unsigned short value as an integer.
+     * @exception ArrayIndexOutOfBoundsException If no data is left in the
+     *                packet buffer to be read.
+     */
+    public int readUnsignedShort() {
+        if ((this.pointer+2)>this.size)
+            throw new ArrayIndexOutOfBoundsException("No data available");
+
+        int k=(this.buffer[this.pointer++])&0xff;
+        k=(k<<8)+((this.buffer[this.pointer++])&0xff);
+
+        return(k);
+    }
+
+    /**
+     * Read a signed integer value (32 bit) from the packet buffer.
+     *
+     * @return The signed integer value.
+     * @exception ArrayIndexOutOfBoundsException If no data is left in the
+     *                packet buffer to be read.
+     */
+    public int readInteger() {
+        if ((this.pointer+4)>this.size)
+            throw new ArrayIndexOutOfBoundsException("No data available");
+
+        int k=(this.buffer[this.pointer++])&0xff;
+        k=(k<<8)+((this.buffer[this.pointer++])&0xff);
+        k=(k<<8)+((this.buffer[this.pointer++])&0xff);
+        k=(k<<8)+((this.buffer[this.pointer++])&0xff);
+
+        return(k);
+    }
+
+    /**
+     * Read a string from the packet buffer.
+     *
+     * @return The string red from the packet buffer.
+     * @exception ArrayIndexOutOfBoundsException If no data is left in the
+     *                packet buffer to be read.
+     */
+    public String readString() {
+        int length=this.readUnsignedShort();
+        try {
+            String ret=new String(this.buffer,this.pointer,length,"UTF-8");
+            this.pointer+=length;
+            return(ret);
+        } catch (UnsupportedEncodingException s) {
+            throw new RuntimeException("Unsupported encoding UTF-8");
+        }
+    }
+
+    public String dump() {
+        StringBuffer buf=new StringBuffer("DATA=");
+        for (int x=0; x<this.size; x++) {
+            if ((this.buffer[x]>32)&&(this.buffer[x]<127)) {
+                buf.append((char)this.buffer[x]);
+            } else {
+                buf.append("0x");
+                String digit=Integer.toHexString((int)this.buffer[x]);
+                if (digit.length()<2) buf.append('0');
+                if (digit.length()>2) digit=digit.substring(digit.length()-2);
+                buf.append(digit);
+            }
+            buf.append(" ");
+        }
+        return(buf.toString());
+    }
+}
diff --git a/connectors/webapp/java/org/apache/catalina/connector/warp/WarpRequest.java b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpRequest.java
new file mode 100644
index 0000000..eb45951
--- /dev/null
+++ b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpRequest.java
@@ -0,0 +1,217 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.catalina.connector.warp;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.catalina.Host;
+import org.apache.catalina.connector.HttpRequestBase;
+
+public class WarpRequest extends HttpRequestBase {
+    /** The local stream */
+    private Stream localstream;
+
+    /** The connection to which we are associated */
+    private WarpConnection connection;
+
+    private Host host=null;
+
+    public WarpRequest() {
+        super();
+        this.localstream=new Stream(this);
+        this.setStream(this.localstream);
+    }
+
+    /** Process the SSL attributes */
+    public Object getAttribute(String name) {
+
+        /* Use cached values */
+        Object object = super.getAttribute(name);
+	if (object != null)
+            return object;
+
+	/* Fill the cache and return value if possible */
+        if (!localstream.request.isSecure()) return null;
+
+        /* Client Certificate */
+        if (name.equals("javax.servlet.request.X509Certificate")) {
+            WarpCertificates cert = null;
+            try {
+                cert = new WarpCertificates(localstream.getX509Certificates());
+            } catch (IOException e) {
+                return null;
+            }
+            super.setAttribute("javax.servlet.request.X509Certificate",
+                cert.getCertificates());
+        }
+
+        /* other ssl parameters */
+        if (name.equals("javax.servlet.request.cipher_suite") ||
+            name.equals("javax.servlet.request.key_size") ||
+            name.equals("javax.servlet.request.ssl_session")) {
+            WarpSSLData ssldata = null;
+            try {
+                ssldata = localstream.getSSL();
+            } catch (IOException e) {
+                return null;
+            }
+            if (ssldata == null) return null;
+
+            super.setAttribute("javax.servlet.request.cipher_suite",
+                ssldata.ciph);
+            if (ssldata.size!=0)
+                super.setAttribute("javax.servlet.request.key_size",
+                    new Integer (ssldata.size));
+            super.setAttribute("javax.servlet.request.ssl_session",
+                ssldata.sess);
+        }
+        return(super.getAttribute(name));
+    }
+
+    public void setHost(Host host) {
+        this.host=host;
+    }
+
+    public Host getHost() {
+        return(this.host);
+    }
+
+    /**
+     * Recycle this <code>WarpResponse</code> instance.
+     */
+    public void recycle() {
+        // Recycle our parent
+        super.recycle();
+        // Recycle the stream
+        this.localstream.recycle();
+        // Tell the parent that a stream is already in use.
+        this.setStream(localstream);
+    }
+
+    /**
+     * Associate this <code>WarpResponse</code> instance with a specific
+     * <code>WarpConnection</code> instance.
+     */
+    public void setConnection(WarpConnection connection) {
+        this.connection=connection;
+    }
+
+    /**
+     * Return the <code>WarpConnection</code> associated this instance of
+     * <code>WarpResponse</code>.
+     */
+    public WarpConnection getConnection() {
+        return(this.connection);
+    }
+
+    protected class Stream extends InputStream {
+
+        /** The response associated with this stream instance. */
+        private WarpRequest request=null;
+        /** The packet used by this stream instance. */
+        private WarpPacket packet=null;
+        /** Wether <code>close()</code> was called or not. */
+        private boolean closed=false;
+
+        protected Stream(WarpRequest request) {
+            super();
+            this.request=request;
+            this.packet=new WarpPacket();
+            this.packet.setType(Constants.TYPE_CBK_DATA);
+        }
+
+        
+        public int read()
+        throws IOException {
+            if (closed) throw new IOException("Stream closed");
+
+            if (this.packet.pointer<this.packet.size)
+                return(((int)this.packet.buffer[this.packet.pointer++])&0x0ff);
+
+            this.packet.reset();
+            this.packet.setType(Constants.TYPE_CBK_READ);
+            this.packet.writeUnsignedShort(65535);
+            this.request.getConnection().send(packet);
+            packet.reset();
+
+            this.request.getConnection().recv(packet);
+
+            if (packet.getType()==Constants.TYPE_CBK_DONE) return(-1);
+
+            if (packet.getType()!=Constants.TYPE_CBK_DATA)
+                throw new IOException("Invalid WARP packet type for body");
+
+            return(this.read());
+        }
+
+        public String getX509Certificates()
+        throws IOException {
+            if (closed) throw new IOException("Stream closed");
+            this.packet.reset();
+            this.packet.setType(Constants.TYPE_ASK_SSL_CLIENT);
+            this.request.getConnection().send(packet);
+            packet.reset();
+
+            this.request.getConnection().recv(packet);
+            if (closed) throw new IOException("Stream closed");
+            if (packet.getType()==Constants.TYPE_REP_SSL_NO) return(null);
+            if (packet.getType()!=Constants.TYPE_REP_SSL_CERT)
+               throw new IOException("Invalid WARP packet type for CC");
+            return(this.packet.readString());
+        }
+
+        /** Read the data from the SSL environment. */
+        public WarpSSLData getSSL()
+        throws IOException {
+          
+            if (closed) throw new IOException("Stream closed");
+            this.packet.reset();
+            this.packet.setType(Constants.TYPE_ASK_SSL);
+            this.request.getConnection().send(packet);
+            packet.reset();
+
+            this.request.getConnection().recv(packet);
+            if (closed) throw new IOException("Stream closed");
+            if (packet.getType()==Constants.TYPE_REP_SSL_NO) return(null);
+            if (packet.getType()!=Constants.TYPE_REP_SSL)
+               throw new IOException("Invalid WARP packet type for SSL data");
+            WarpSSLData ssldata  = new WarpSSLData();
+            ssldata.ciph = this.packet.readString();
+            ssldata.sess = this.packet.readString();
+            ssldata.size = this.packet.readInteger();
+            return(ssldata);
+        }
+
+
+        
+        public void close()
+        throws IOException {
+            if (closed) throw new IOException("Stream closed");
+            this.packet.reset();
+            this.packet.setType(Constants.TYPE_CBK_DONE);
+            this.closed=true;
+        }
+
+        public void recycle() {
+            this.packet.reset();
+            this.packet.setType(Constants.TYPE_CBK_DATA);
+            this.closed=false;
+        }
+
+    }
+}
diff --git a/connectors/webapp/java/org/apache/catalina/connector/warp/WarpRequestHandler.java b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpRequestHandler.java
new file mode 100644
index 0000000..d3ff956
--- /dev/null
+++ b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpRequestHandler.java
@@ -0,0 +1,389 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.catalina.connector.warp;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.TreeMap;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Host;
+import org.apache.catalina.util.RequestUtil;
+import org.apache.catalina.util.StringParser;
+
+public class WarpRequestHandler {
+
+    private StringParser parser = new StringParser();
+    private static final String match=";"+Globals.SESSION_PARAMETER_NAME+"=";
+
+    /* ==================================================================== */
+    /* Constructor                                                          */
+    /* ==================================================================== */
+
+    public WarpRequestHandler() {
+        super();
+    }
+
+    public boolean handle(WarpConnection connection, WarpPacket packet)
+    throws IOException {
+        WarpLogger logger=new WarpLogger(this);
+        WarpConnector connector=connection.getConnector();
+        logger.setContainer(connector.getContainer());
+        WarpRequest request=new WarpRequest();
+        WarpResponse response=new WarpResponse();
+        response.setRequest(request);
+        response.setConnection(connection);
+        response.setPacket(packet);
+        request.setConnection(connection);
+        request.setConnector(connector);
+
+        // Prepare the Proceed packet
+        packet.reset();
+        packet.setType(Constants.TYPE_CONF_PROCEED);
+        connection.send(packet);
+
+        // Loop for configuration packets
+        while (true) {
+            connection.recv(packet);
+
+            switch (packet.getType()) {
+                case Constants.TYPE_REQ_INIT: {
+                    int id=packet.readInteger();
+                    String meth=packet.readString();
+                    String ruri=packet.readString();
+                    String args=packet.readString();
+                    String prot=packet.readString();
+                    if (Constants.DEBUG)
+                        logger.debug("Request ID="+id+" \""+meth+" "+ruri+
+                                     "?"+args+" "+prot+"\"");
+
+                    request.recycle();
+                    response.recycle();
+                    response.setRequest(request);
+                    response.setConnection(connection);
+                    response.setPacket(packet);
+
+                    request.setMethod(meth);
+                    this.processUri(logger,request,ruri);
+                    if (args.length()>0) request.setQueryString(args);
+                    else request.setQueryString(null);
+                    request.setProtocol(prot);
+                    request.setConnection(connection);
+                    Context ctx=connector.applicationContext(id);
+                    if (ctx!=null) {
+                        request.setContext(ctx);
+                        request.setContextPath(ctx.getPath());
+                        request.setHost((Host)ctx.getParent());
+                    }
+                    break;
+                }
+
+                case Constants.TYPE_REQ_CONTENT: {
+                    String ctyp=packet.readString();
+                    int clen=packet.readInteger();
+                    if (Constants.DEBUG)
+                        logger.debug("Request content type="+ctyp+" length="+
+                                     clen);
+                    if (ctyp.length()>0) request.setContentType(ctyp);
+                    if (clen>0) request.setContentLength(clen);
+                    break;
+                }
+
+                case Constants.TYPE_REQ_SCHEME: {
+                    String schm=packet.readString();
+                    if (Constants.DEBUG)
+                        logger.debug("Request scheme="+schm);
+                    request.setScheme(schm);
+                    if (schm.equals("https"))
+                       request.setSecure(true);
+                    break;
+                }
+
+                case Constants.TYPE_REQ_AUTH: {
+                    String user=packet.readString();
+                    String auth=packet.readString();
+                    if (Constants.DEBUG)
+                        logger.debug("Request user="+user+" auth="+auth);
+                    request.setAuthType(auth);
+                    // What to do for user name?
+                    if(user != null && auth != null && auth.equals("Basic")) {
+                        Principal prin = new BasicPrincipal(user);
+                        request.setUserPrincipal(prin);
+                    }
+
+                    break;
+                }
+
+                case Constants.TYPE_REQ_HEADER: {
+                    String hnam=packet.readString();
+                    String hval=packet.readString();
+                    this.processHeader(logger,request,hnam,hval);
+                    break;
+                }
+
+                case Constants.TYPE_REQ_SERVER: {
+                    String host=packet.readString();
+                    String addr=packet.readString();
+                    int port=packet.readUnsignedShort();
+                    if (Constants.DEBUG)
+                        logger.debug("Server detail "+host+":"+port+
+                                     " ("+addr+")");
+                    request.setServerName(host);
+                    request.setServerPort(port);
+                    break;
+                }
+
+                case Constants.TYPE_REQ_CLIENT: {
+                    String host=packet.readString();
+                    String addr=packet.readString();
+                    int port=packet.readUnsignedShort();
+                    if (Constants.DEBUG)
+                        logger.debug("Client detail "+host+":"+port+
+                                     " ("+addr+")");
+                    request.setRemoteHost(host);
+                    request.setRemoteAddr(addr);
+                    break;
+                }
+
+                case Constants.TYPE_REQ_PROCEED: {
+                    if (Constants.DEBUG)
+                        logger.debug("Request is about to be processed");
+                    try {
+                        connector.getContainer().invoke(request,response);
+                    } catch (Exception e) {
+                        logger.log(e);
+                    }
+                    request.finishRequest();
+                    response.finishResponse();
+                    if (Constants.DEBUG)
+                        logger.debug("Request has been processed");
+                    break;
+                }
+
+                default: {
+                    String msg="Invalid packet "+packet.getType();
+                    logger.log(msg);
+                    packet.reset();
+                    packet.setType(Constants.TYPE_FATAL);
+                    packet.writeString(msg);
+                    connection.send(packet);
+                    return(false);
+                }
+            }
+        }
+    }
+
+    private void processUri(WarpLogger logger, WarpRequest req, String uri) {
+
+        // Parse any requested session ID out of the request URI
+        int semicolon = uri.indexOf(match);
+        if (semicolon >= 0) {
+            String rest = uri.substring(semicolon + match.length());
+            int semicolon2 = rest.indexOf(';');
+            if (semicolon2 >= 0) {
+                req.setRequestedSessionId(rest.substring(0, semicolon2));
+                rest = rest.substring(semicolon2);
+            } else {
+                req.setRequestedSessionId(rest);
+                rest = "";
+            }
+            req.setRequestedSessionURL(true);
+            uri = uri.substring(0, semicolon) + rest;
+            if (Constants.DEBUG) {
+                logger.log("Requested URL session id is " +
+                    ((HttpServletRequest) req.getRequest())
+                    .getRequestedSessionId());
+            }
+        } else {
+            req.setRequestedSessionId(null);
+            req.setRequestedSessionURL(false);
+        }
+
+        req.setRequestURI(uri);
+    }
+
+    private void processHeader(WarpLogger logger, WarpRequest req,
+                 String name, String value) {
+
+        if (Constants.DEBUG)
+            logger.debug("Request header "+name+": "+value);
+
+        if ("cookie".equalsIgnoreCase(name)) {
+            Cookie cookies[] = RequestUtil.parseCookieHeader(value);
+            for (int i = 0; i < cookies.length; i++) {
+                if (cookies[i].getName().equals
+                    (Globals.SESSION_COOKIE_NAME)) {
+                    // Override anything requested in the URL
+                    if (!req.isRequestedSessionIdFromCookie()) {
+                        // Accept only the first session id cookie
+                        req.setRequestedSessionId
+                            (cookies[i].getValue());
+                        req.setRequestedSessionCookie(true);
+                        req.setRequestedSessionURL(false);
+                        if (Constants.DEBUG) {
+                            logger.debug("Requested cookie session id is " +
+                                ((HttpServletRequest) req.getRequest())
+                                .getRequestedSessionId());
+                        }
+                    }
+                }
+                if (Constants.DEBUG) {
+                    logger.debug("Adding cookie "+cookies[i].getName()+"="+
+                        cookies[i].getValue());
+                }
+                req.addCookie(cookies[i]);
+            }
+        }
+        if (name.equalsIgnoreCase("Accept-Language"))
+            parseAcceptLanguage(logger,req,value);
+
+        if (name.equalsIgnoreCase("Authorization"))
+            req.setAuthorization(value);
+
+        req.addHeader(name,value);
+    }
+
+    /**
+     * Parse the value of an <code>Accept-Language</code> header, and add
+     * the corresponding Locales to the current request.
+     *
+     * @param value The value of the <code>Accept-Language</code> header.
+     */
+    private void parseAcceptLanguage(WarpLogger logger, WarpRequest request, 
+                                     String value) {
+
+        // Store the accumulated languages that have been requested in
+        // a local collection, sorted by the quality value (so we can
+        // add Locales in descending order).  The values will be ArrayLists
+        // containing the corresponding Locales to be added
+        TreeMap locales = new TreeMap();
+
+        // Preprocess the value to remove all whitespace
+        int white = value.indexOf(' ');
+        if (white < 0)
+            white = value.indexOf('\t');
+        if (white >= 0) {
+            StringBuffer sb = new StringBuffer();
+            int len = value.length();
+            for (int i = 0; i < len; i++) {
+                char ch = value.charAt(i);
+                if ((ch != ' ') && (ch != '\t'))
+                    sb.append(ch);
+            }
+            value = sb.toString();
+        }
+
+        // Process each comma-delimited language specification
+        parser.setString(value);        // ASSERT: parser is available to us
+        int length = parser.getLength();
+        while (true) {
+
+            // Extract the next comma-delimited entry
+            int start = parser.getIndex();
+            if (start >= length)
+                break;
+            int end = parser.findChar(',');
+            String entry = parser.extract(start, end).trim();
+            parser.advance();   // For the following entry
+
+            // Extract the quality factor for this entry
+            double quality = 1.0;
+            int semi = entry.indexOf(";q=");
+            if (semi >= 0) {
+                try {
+                    quality = Double.parseDouble(entry.substring(semi + 3));
+                } catch (NumberFormatException e) {
+                    quality = 0.0;
+                }
+                entry = entry.substring(0, semi);
+            }
+
+            // Skip entries we are not going to keep track of
+            if (quality < 0.00005)
+                continue;       // Zero (or effectively zero) quality factors
+            if ("*".equals(entry))
+                continue;       // FIXME - "*" entries are not handled
+
+            // Extract the language and country for this entry
+            String language = null;
+            String country = null;
+            int dash = entry.indexOf('-');
+            if (dash < 0) {
+                language = entry;
+                country = "";
+            } else {
+                language = entry.substring(0, dash);
+                country = entry.substring(dash + 1);
+            }
+
+            // Add a new Locale to the list of Locales for this quality level
+            Locale locale = new Locale(language, country);
+            Double key = new Double(-quality);  // Reverse the order
+            ArrayList values = (ArrayList) locales.get(key);
+            if (values == null) {
+                values = new ArrayList();
+                locales.put(key, values);
+            }
+            values.add(locale);
+
+        }
+
+        // Process the quality values in highest->lowest order (due to
+        // negating the Double value when creating the key)
+        Iterator keys = locales.keySet().iterator();
+        while (keys.hasNext()) {
+            Double key = (Double) keys.next();
+            ArrayList list = (ArrayList) locales.get(key);
+            Iterator values = list.iterator();
+            while (values.hasNext()) {
+                Locale locale = (Locale) values.next();
+                if (Constants.DEBUG) {
+                    logger.debug("Adding locale '" + locale + "'");
+                }
+                request.addLocale(locale);
+            }
+        }
+    }
+
+    class BasicPrincipal implements Principal {
+        private String user;
+
+        BasicPrincipal(String user) {
+            this.user = user;
+        }
+
+        public boolean equals(Object another) {
+            return (another instanceof Principal &&
+                ((Principal)another).getName().equals(user));
+        }
+
+        public String getName() {
+            return user;
+        }
+
+        public String toString() {
+            return getName();
+        }
+    }
+}
diff --git a/connectors/webapp/java/org/apache/catalina/connector/warp/WarpResponse.java b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpResponse.java
new file mode 100644
index 0000000..73e43e7
--- /dev/null
+++ b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpResponse.java
@@ -0,0 +1,268 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.catalina.connector.warp;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.catalina.Globals;
+import org.apache.catalina.connector.HttpResponseBase;
+import org.apache.catalina.util.CookieTools;
+
+public class WarpResponse extends HttpResponseBase {
+    /** The local stream */
+    private Stream localstream;
+    /** The packet used for processing headers */
+    private WarpPacket packet;
+    /** The connection to which we are associated */
+    private WarpConnection connection;
+
+    /**
+     * Create a new instance of a <code>WarpResponse</code>.
+     */
+    public WarpResponse() {
+        super();
+        // A WarpResponse is _always_ associated with a Stream
+        this.localstream=new Stream(this);
+        this.setStream(localstream);
+    }
+
+    /**
+     * Recycle this <code>WarpResponse</code> instance.
+     */
+    public void recycle() {
+        // Recycle our parent
+        super.recycle();
+        // Recycle the stream
+        this.localstream.recycle();
+        // Tell the parent that a stream is already in use.
+        this.setStream(localstream);
+    }
+
+    /**
+     * Set the <code>WarpPacket</code> instance used to process headers.
+     */
+    public void setPacket(WarpPacket packet) {
+        this.packet=packet;
+    }
+
+    /**
+     * Return the <code>WarpPacket</code> instance used to process headers.
+     */
+    public WarpPacket getPacket() {
+        return(this.packet);
+    }
+
+    /**
+     * Associate this <code>WarpResponse</code> instance with a specific
+     * <code>WarpConnection</code> instance.
+     */
+    public void setConnection(WarpConnection connection) {
+        this.connection=connection;
+    }
+
+    /**
+     * Return the <code>WarpConnection</code> associated this instance of
+     * <code>WarpResponse</code>.
+     */
+    public WarpConnection getConnection() {
+        return(this.connection);
+    }
+
+    /**
+     * Flush output and finish.
+     */
+    public void finishResponse()
+    throws IOException {
+        super.finishResponse();
+        this.localstream.finish();
+    }
+
+    /**
+     * Send the HTTP response headers, if this has not already occurred.
+     */
+    protected void sendHeaders() throws IOException {
+        if (isCommitted()) return;
+        if ("HTTP/0.9".equals(request.getRequest().getProtocol())) {
+            committed = true;
+            return;
+        }
+
+        this.packet.reset();
+        this.packet.setType(Constants.TYPE_RES_STATUS);
+        this.packet.writeUnsignedShort(status);
+        this.packet.writeString(message);
+        this.connection.send(this.packet);
+
+        if (getContentType() != null) {
+            this.packet.reset();
+            this.packet.setType(Constants.TYPE_RES_HEADER);
+            this.packet.writeString("Content-Type");
+            this.packet.writeString(getContentType());
+            this.connection.send(this.packet);
+        }
+        if (getContentLength() >= 0) {
+            this.packet.reset();
+            this.packet.setType(Constants.TYPE_RES_HEADER);
+            this.packet.writeString("Content-Length");
+            this.packet.writeString(Integer.toString(getContentLength()));
+            this.connection.send(this.packet);
+        }
+
+        synchronized (headers) {
+                Iterator names = headers.keySet().iterator();
+            while (names.hasNext()) {
+                String name = (String) names.next();
+                ArrayList values = (ArrayList) headers.get(name);
+                Iterator items = values.iterator();
+                while (items.hasNext()) {
+                        String value = (String) items.next();
+                    this.packet.reset();
+                    this.packet.setType(Constants.TYPE_RES_HEADER);
+                    this.packet.writeString(name);
+                    this.packet.writeString(value);
+                    this.connection.send(this.packet);
+                    }
+            }
+        }
+
+        // Add the session ID cookie if necessary
+        HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
+        HttpSession session = hreq.getSession(false);
+
+        if ((session != null) && session.isNew() && (getContext() != null)
+                && getContext().getCookies()) {
+            Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,
+                                       session.getId());
+            cookie.setMaxAge(-1);
+            String contextPath = null;
+                if (context != null)
+                    contextPath = context.getPath();
+            if ((contextPath != null) && (contextPath.length() > 0))
+                cookie.setPath(contextPath);
+            else
+                cookie.setPath("/");
+            if (hreq.isSecure())
+                cookie.setSecure(true);
+            addCookie(cookie);
+        }
+
+        // Send all specified cookies (if any)
+        synchronized (cookies) {
+            Iterator items = cookies.iterator();
+            while (items.hasNext()) {
+                Cookie cookie = (Cookie) items.next();
+                String name=CookieTools.getCookieHeaderName(cookie);
+                StringBuffer value=new StringBuffer();
+                CookieTools.getCookieHeaderValue(cookie,value);
+                    this.packet.reset();
+                this.packet.setType(Constants.TYPE_RES_HEADER);
+                this.packet.writeString(name);
+                this.packet.writeString(value.toString());
+                this.connection.send(this.packet);
+            }
+        }
+
+            this.packet.reset();
+        this.packet.setType(Constants.TYPE_RES_COMMIT);
+        this.connection.send(this.packet);
+
+        committed = true;
+    }
+
+    /**
+     * The <code>OutputStream</code> that will handle all response body
+     * transmission.
+     */
+    protected class Stream extends OutputStream {
+        /** The response associated with this stream instance. */
+        private WarpResponse response=null;
+        /** The packet used by this stream instance. */
+        private WarpPacket packet=null;
+        /** Wether <code>close()</code> was called or not. */
+        private boolean closed=false;
+
+        /**
+         * Construct a new instance of a <code>WarpResponse.Stream</code>
+         * associated with a parent <code>WarpResponse</code>.
+         */
+        protected Stream(WarpResponse response) {
+            super();
+            this.response=response;
+            this.packet=new WarpPacket();
+        }
+
+        /**
+         * Write one byte of data to the <code>WarpPacket</code> nested
+         * within this <code>WarpResponse.Stream</code>. All data is buffered
+         * until the <code>flush()</code> or <code>close()</code> method is
+         * not called.
+         */
+        public void write(int b)
+        throws IOException {
+            if (closed) throw new IOException("Stream closed");
+            if (packet.size>=packet.buffer.length) this.flush();
+            packet.buffer[packet.size++]=(byte)b;
+        }
+
+        /**
+         * Flush the current packet to the WARP client.
+         */
+        public void flush()
+        throws IOException {
+            if (closed) throw new IOException("Stream closed");
+            packet.setType(Constants.TYPE_RES_BODY);
+            response.getConnection().send(packet);
+            packet.reset();
+        }
+
+        /**
+         * Flush this <code>WarpResponse.Stream</code> and close it.
+         */
+        public void close()
+        throws IOException {
+            if (closed) throw new IOException("Stream closed");
+            flush();
+            packet.setType(Constants.TYPE_RES_DONE);
+            response.getConnection().send(packet);
+            packet.reset();
+        }
+
+        /**
+         * Flush this <code>WarpResponse.Stream</code> and close it.
+         */
+        public void finish()
+        throws IOException {
+            if (closed) return;
+            else this.close();
+        }
+
+        /**
+         * Recycle this <code>WarpResponse.Stream</code> instance.
+         */
+        public void recycle() {
+            this.packet.reset();
+            this.closed=false;
+        }
+    }
+}
diff --git a/connectors/webapp/java/org/apache/catalina/connector/warp/WarpSSLData.java b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpSSLData.java
new file mode 100644
index 0000000..1a904ba
--- /dev/null
+++ b/connectors/webapp/java/org/apache/catalina/connector/warp/WarpSSLData.java
@@ -0,0 +1,36 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.catalina.connector.warp;
+ 
+/*
+ * SSL message handling.
+ */
+ 
+public class WarpSSLData {
+    /**
+     * Cipher use by the SSL.
+     */
+    String ciph;
+    /**
+     * SSL session.
+     */
+    String sess;
+    /**
+     * Size of the algorithm. (56-128)
+     */
+    int size;
+}
diff --git a/connectors/webapp/lib/.cvsignore b/connectors/webapp/lib/.cvsignore
new file mode 100644
index 0000000..d162809
--- /dev/null
+++ b/connectors/webapp/lib/.cvsignore
@@ -0,0 +1,33 @@
+.DS_Store
+.libs
+Makefile
+libwebapp.a
+libwebapp.la
+pr_info.lo
+pr_info.o
+pr_info.obj
+pr_warp.lo
+pr_warp.o
+pr_warp.obj
+pr_warp_config.lo
+pr_warp_config.o
+pr_warp_config.obj
+pr_warp_defs.h
+pr_warp_network.lo
+pr_warp_network.o
+pr_warp_network.obj
+pr_warp_packet.lo
+pr_warp_packet.o
+pr_warp_packet.obj
+wa_config.lo
+wa_config.o
+wa_config.obj
+wa_main.lo
+wa_main.o
+wa_main.obj
+wa_request.lo
+wa_request.o
+wa_request.obj
+webapp.idb
+webapp.lib            
+webapp.pdb
diff --git a/connectors/webapp/lib/Makefile.in b/connectors/webapp/lib/Makefile.in
new file mode 100644
index 0000000..32d4b1d
--- /dev/null
+++ b/connectors/webapp/lib/Makefile.in
@@ -0,0 +1,58 @@
+#
+# Copyright 1999-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# @author  Pier Fumagalli <mailto:pier@betaversion.org>
+# @version $Id$
+
+LOCAL_TGT_DIR = @TGT_DIR@/lib
+LOCAL_SRC_DIR = @SRC_DIR@/lib
+include @TGT_DIR@/Makedefs
+
+OBJS =	wa_main.lo \
+	wa_config.lo \
+	wa_request.lo \
+	pr_info.lo \
+	pr_warp.lo \
+	pr_warp_packet.lo \
+	pr_warp_network.lo \
+	pr_warp_config.lo \
+	pr_warp_socketpool.lo
+DEFH =	pr_warp_defs.h
+
+.PHONY: build clean
+
+build: $(DEFH) $(OBJS)
+
+clean:
+	@for FILE in $(OBJS) ; do \
+	  FILE="`basename $$FILE .lo`" ; \
+	  echo rm -f $$FILE.lo ; \
+	  rm -f $$FILE.lo ; \
+	  echo rm -f $$FILE.o ; \
+	  rm -f $$FILE.o ; \
+	done
+	rm -f $(DEFH)
+	rm -rf .libs
+
+$(DEFH): $(SRC_DIR)/java/org/apache/catalina/connector/warp/Constants.java
+	cat "$<" | \
+	    grep 'VERS_' | \
+	    sed 's/.*public static final int/#define/g' | \
+	    sed 'y/=;/  /' > $@
+	cat "$<" | \
+	    grep 'TYPE_' | \
+	    sed 's/.*public static final int/#define/g' | \
+	    sed 'y/=;/  /' > $@
diff --git a/connectors/webapp/lib/Makefile.win b/connectors/webapp/lib/Makefile.win
new file mode 100644
index 0000000..64adcfb
--- /dev/null
+++ b/connectors/webapp/lib/Makefile.win
@@ -0,0 +1,85 @@
+#
+# Copyright 1999-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# @author  Pier Fumagalli <mailto:pier@betaversion.org>
+# @version $Id$
+
+# All our object files
+OBJS =   wa_config.obj \
+         wa_main.obj \
+         wa_request.obj \
+         pr_info.obj \
+         pr_warp.obj \
+         pr_warp_config.obj \
+         pr_warp_network.obj \
+         pr_warp_packet.obj \
+         pr_warp_socketpool.obj
+
+# Files created by this script (for removal only)
+GENS =   webapp.idb \
+         webapp.pdb \
+         pr_warp_defs.h
+
+# The target library
+LIBS =   webapp.lib
+
+# Microsoft Visual C/C++ 6.0 compilation and linking programs
+CC =     cl.exe
+LINK =   link.exe
+
+# Flags for the C compiler
+CFLAGS = /nologo \
+         /W3 \
+!IF "$(DEBUG)" == "true"
+         /MDd \
+         /GX \
+         /Zi \
+         /Od \
+         /Yd \
+         /D"DEBUG" \
+!ELSE
+	     /MD \
+	     /O2 \
+!ENDIF
+	     /I "." \
+	     /I "..\include" \
+	     /I "..\apr\include" \
+	     /D"WIN32" \
+	     /Fd".\webapp" \
+	     /FD
+
+# Flags for the library linker
+LFLAGS = -lib \
+         /nologo
+
+# Makefile rules
+all: $(LIBS)
+
+webapp.lib: pr_warp_defs.h $(OBJS)
+    $(LINK) $(LFLAGS) /out:$@ $(OBJS)
+
+pr_warp_defs.h:
+    type ..\java\Constants.java.in | \
+        ..\support\grep TYPE_ | \
+        ..\support\sed "s/public static final int/#define/g" | \
+        ..\support\sed "y/=;/  /" > pr_warp_defs.h
+
+clean:
+    -@for %%i in ($(OBJS) $(GENS) $(LIBS)) do \
+        @erase "%%i"
+
+.c.obj:
+	$(CC) $(CFLAGS) /Fo"$@" /c "$<"
diff --git a/connectors/webapp/lib/pr_info.c b/connectors/webapp/lib/pr_info.c
new file mode 100644
index 0000000..12a3899
--- /dev/null
+++ b/connectors/webapp/lib/pr_info.c
@@ -0,0 +1,412 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/* @version $Id$ */
+#include <wa.h>
+
+/* Counter for requests */
+static int k=0;
+
+/* Initialize this provider. */
+static const char *info_init(void) {
+    wa_debug(WA_MARK,"INFO provider initialized");
+    return(NULL);
+}
+
+/* Notify this provider of its imminent startup. */
+static void info_startup(void) {
+    wa_debug(WA_MARK,"INFO provider started");
+}
+
+/* Cleans up all resources allocated by this provider. */
+static void info_shutdown(void) {
+    wa_debug(WA_MARK,"INFO provider shut down");
+}
+
+/* Configure a connection with the parameter from the web server
+   configuration file. */
+static const char *info_connect(wa_connection *conn, const char *param) {
+    wa_debug(WA_MARK,"Provider is configuring \"%s\" with parameter \"%s\"",
+             conn->name,param);
+    conn->conf=NULL;
+    return(NULL);
+}
+
+/* Receive notification of the deployment of an application. */
+static const char *info_deploy(wa_application *appl) {
+    wa_debug(WA_MARK,"Provider is deploying %s for http://%s:%d%s (Conn: %s)",
+             appl->name,appl->host->name,appl->host->port,appl->rpth,
+             appl->conn->name);
+    appl->conf=NULL;
+    appl->depl=wa_true;
+    return(NULL);
+}
+
+/* Describe the configuration member found in a connection. */
+static char *info_conninfo(wa_connection *conn, apr_pool_t *pool) {
+    return(NULL);
+}
+
+/* Describe the configuration member found in a web application. */
+static char *info_applinfo(wa_application *appl, apr_pool_t *pool) {
+    return(NULL);
+}
+
+/* Display informations regarding an application */
+static void info_handle_application(wa_request *r, wa_application *a) {
+    char *desc;
+
+    wa_rprintf(r,"   <tr>\n");
+    wa_rprintf(r,"    <td width=\"10%%\" valign=\"top\" align=\"right\">\n");
+    wa_rprintf(r,"     <font size=\"-1\">\n");
+    wa_rprintf(r,"      Application&nbsp;Name<br>\n");
+    wa_rprintf(r,"      Root&nbsp;URL&nbsp;Path<br>\n");
+    wa_rprintf(r,"      Local&nbsp;Deployment&nbsp;Path<br>\n");
+    wa_rprintf(r,"      Configuration&nbsp;Details<br>\n");
+    wa_rprintf(r,"      Connection<br>\n");
+    wa_rprintf(r,"      Deployed\n");
+    wa_rprintf(r,"     </font>\n");
+    wa_rprintf(r,"    </td>\n");
+    wa_rprintf(r,"    <td width=\"90%%\" valign=\"top\" align=\"left\">\n");
+    wa_rprintf(r,"     <font size=\"-1\">");
+    wa_rprintf(r,"      <b>&quot;%s&quot;</b><br>\n",a->name);
+    wa_rprintf(r,"      <b>&quot;%s&quot;</b><br>\n",a->rpth);
+
+    if (a->lpth==NULL) wa_rprintf(r,"      <i>No local deployment path</i>");
+    else               wa_rprintf(r,"      <b>&quot;%s&quot;</b>",a->lpth);
+    wa_rprintf(r,"<br>\n");
+
+    desc=a->conn->prov->applinfo(a,r->pool);
+    if (desc==NULL) wa_rprintf(r,"      <i>No configuration information</i>");
+    else            wa_rprintf(r,"      <b>&quot;%s&quot;</b>",desc);
+    wa_rprintf(r,"<br>\n");
+
+    wa_rprintf(r,"      <b>&quot;%s&quot;</b>",a->conn->name);
+    wa_rprintf(r," <i><a href=\"#%s\">(details)</a></i><br>\n",a->conn->name);
+    wa_rprintf(r,"      <b>%s</b><br>\n",a->depl?"TRUE":"FALSE");
+    wa_rprintf(r,"     </font>\n");
+    wa_rprintf(r,"    </td>\n");
+    wa_rprintf(r,"   </tr>\n");
+    wa_rflush(r);
+}
+
+/* Display informations regarding a virtual host and its children applications.
+   At the same time add all connections to the 'c' chain for later */
+static void info_handle_host(wa_request *r, wa_virtualhost *h, wa_chain *c) {
+    wa_chain *elem=NULL;
+
+    wa_rprintf(r,"  <table width=\"80%%\" border=\"1\" cellspacing=\"0\">\n");
+    wa_rprintf(r,"   <tr>\n");
+    wa_rprintf(r,"    <td bgcolor=\"#ccccff\" colspan=\"2\">\n");
+    wa_rprintf(r,"     <b>Host %s:%d</b>\n",h->name,h->port);
+    wa_rprintf(r,"    </td>\n");
+    wa_rprintf(r,"   </tr>\n");
+    wa_rflush(r);
+
+    elem=h->apps;
+    while(elem!=NULL) {
+        wa_application *curr=(wa_application *)elem->curr;
+        wa_chain *orig=c;
+
+        info_handle_application(r,curr);
+
+        /* Check out if this application connection is already stored in the
+           connections chain */
+        c=orig;
+        while(c->next!=NULL) {
+            wa_connection *conn=(wa_connection *)c->next->curr;
+            if (strcmp(conn->name,curr->conn->name)==0) break;
+            c=c->next;
+        }
+        /* If we didn't find the connection, add it to the chain */
+        if (c->next==NULL) {
+            c->next=(wa_chain *)apr_palloc(r->pool,sizeof(wa_chain));
+            c->next->curr=curr->conn;
+            c->next->next=NULL;
+        }
+
+        /* Process next application in host */
+        elem=elem->next;
+    }
+
+    wa_rprintf(r,"  </table>\n");
+    wa_rprintf(r,"  <br>\n");
+    wa_rflush(r);
+}
+
+/* Display informations regarding a connection */
+static void info_handle_connection(wa_request *r, wa_connection *c) {
+    char *desc;
+
+    wa_rprintf(r,"   <tr>\n");
+    wa_rprintf(r,"    <td width=\"10%%\" valign=\"top\" align=\"right\">\n");
+    wa_rprintf(r,"     <a name=\"%s\">\n",c->name);
+    wa_rprintf(r,"     <font size=\"-1\">\n");
+    wa_rprintf(r,"      Connection&nbsp;Name<br>\n");
+    wa_rprintf(r,"      Connection&nbsp;Parameters<br>\n");
+    wa_rprintf(r,"      Provider<br>\n");
+    wa_rprintf(r,"      Configuration&nbsp;Details\n");
+    wa_rprintf(r,"     </font>\n");
+    wa_rprintf(r,"    </td>\n");
+    wa_rprintf(r,"    <td width=\"90%%\" valign=\"top\" align=\"left\">\n");
+    wa_rprintf(r,"     <font size=\"-1\">");
+    wa_rprintf(r,"      <b>&quot;%s&quot;</b><br>\n",c->name);
+    wa_rprintf(r,"      <b>&quot;%s&quot;</b><br>\n",c->parm);
+    wa_rprintf(r,"      <b>&quot;%s&quot;</b><br>\n",c->prov->name);
+
+    desc=c->prov->conninfo(c,r->pool);
+    if (desc==NULL) wa_rprintf(r,"      <i>No configuration information</i>\n");
+    else            wa_rprintf(r,"      <b>&quot;%s&quot;</b>\n",desc);
+
+    wa_rprintf(r,"     </font>\n");
+    wa_rprintf(r,"    </td>\n");
+    wa_rprintf(r,"   </tr>\n");
+    wa_rflush(r);
+}
+
+/* Display informations for a header name */
+static int info_handle_hdrname(void *d, const char *n, const char *v) {
+    wa_request *r=(wa_request *)d;
+
+    wa_rprintf(r,"       <nobr>%s</nobr><br>\n",n);
+    return(TRUE);
+}
+
+/* Display informations for a header velue */
+static int info_handle_hdrvalue(void *d, const char *n, const char *v) {
+    wa_request *r=(wa_request *)d;
+    char *b=(char *)v;
+
+    if (strlen(b)>64) {
+        b=apr_pstrndup(r->pool,b,64);
+        b=apr_pstrcat(r->pool,b," ....",NULL);
+    }
+    wa_rprintf(r,"      <b><nobr>&quot;%s&quot;</nobr></b><br>\n",b);
+    return(TRUE);
+}
+
+/* Handle a connection from the web server. */
+static int info_handle(wa_request *r, wa_application *a) {
+    wa_chain *conn=(wa_chain *)apr_palloc(r->pool,sizeof(wa_chain));
+    wa_chain *elem=NULL;
+
+    wa_rsetstatus(r,200,NULL);
+    wa_rsetctype(r,"text/html");
+    wa_rcommit(r);
+
+    wa_rprintf(r,"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">");
+    wa_rprintf(r,"\n\n");
+    wa_rprintf(r,"<html>\n");
+    wa_rprintf(r," <head>\n");
+    wa_rprintf(r,"  <title>WebApp Library Configuration</title>");
+    wa_rprintf(r," </head>\n");
+    wa_rprintf(r," <body>\n");
+    wa_rprintf(r,"  <div align=\"center\">\n");
+    wa_rprintf(r,"  <table width=\"90%%\" border=\"1\" cellspacing=\"0\">\n");
+    wa_rprintf(r,"   <tr>\n");
+    wa_rprintf(r,"    <td align=\"center\" bgcolor=\"#ffcccc\">\n");
+    wa_rprintf(r,"     <font size=\"+1\">\n");
+    wa_rprintf(r,"      <b>WebApp Library Configuration</b>\n");
+    wa_rprintf(r,"     </font>\n");
+    wa_rprintf(r,"    </td>\n");
+    wa_rprintf(r,"   </tr>\n");
+    wa_rprintf(r,"  </table>\n");
+    wa_rprintf(r,"  <br>\n");
+    wa_rflush(r);
+
+    /* Process all virtual hosts and related applications (this will also
+       add connections to the conn chain) */
+    elem=wa_configuration;
+    conn->curr=NULL;
+    conn->next=NULL;
+    while(elem!=NULL) {
+        wa_virtualhost *curr=(wa_virtualhost *)elem->curr;
+        info_handle_host(r,curr,conn);
+        elem=elem->next;
+    }
+
+    /* Process all connections */
+    wa_rprintf(r,"  <table width=\"80%%\" border=\"1\" cellspacing=\"0\">\n");
+    wa_rprintf(r,"   <tr>\n");
+    wa_rprintf(r,"    <td bgcolor=\"#ccffcc\" colspan=\"2\">\n");
+    wa_rprintf(r,"     <b>Connections</b>");
+    wa_rprintf(r,"    </td>\n");
+    wa_rprintf(r,"   </tr>\n");
+
+    elem=conn->next;
+    while(elem!=NULL) {
+        wa_connection *curr=(wa_connection *)elem->curr;
+        info_handle_connection(r,curr);
+        elem=elem->next;
+    }
+
+    wa_rprintf(r,"  </table>\n");
+    wa_rprintf(r,"  <br>\n");
+
+#ifdef DEBUG
+    /* See the request */
+    wa_rprintf(r,"  <table width=\"80%%\" border=\"1\" cellspacing=\"0\">\n");
+    wa_rprintf(r,"   <tr>\n");
+    wa_rprintf(r,"    <td bgcolor=\"#cccccc\" colspan=\"2\">\n");
+    wa_rprintf(r,"     <b>Your request</b>");
+    wa_rprintf(r,"    </td>\n");
+    wa_rprintf(r,"   </tr>\n");
+
+    /* A couple of forms for testing */
+    k++;
+    wa_rprintf(r,"   <tr>\n");
+    wa_rprintf(r,"    <td width=\"100%%\" colspan=\"2\">\n");
+    wa_rprintf(r,"     <form action=\"%s\" method=\"GET\">\n",r->ruri);
+    wa_rprintf(r,"      <input type=\"hidden\" name=\"a\" value=\"%d.%d\">\n",
+               getpid(),k);
+    wa_rprintf(r,"      <input type=\"text\" name=\"b\" value=\"%s\">\n",
+               "some random data...");
+    wa_rprintf(r,"      <input type=\"submit\" value=\"Submit\">\n");
+    wa_rprintf(r,"      Resubmit via GET\n");
+    wa_rprintf(r,"     </form>\n");
+    wa_rprintf(r,"     <form action=\"%s\" method=\"POST\">\n",r->ruri);
+    wa_rprintf(r,"      <input type=\"hidden\" name=\"a\" value=\"%d.%d\">\n",
+               getpid(),k);
+    wa_rprintf(r,"      <input type=\"text\" name=\"b\" value=\"%s\">\n",
+               "some random data...");
+    wa_rprintf(r,"      <input type=\"submit\" value=\"Submit\">\n");
+    wa_rprintf(r,"      Resubmit via POST\n");
+    wa_rprintf(r,"     </form>\n");
+    wa_rprintf(r,"    </td>\n");
+    wa_rprintf(r,"   </tr>\n");
+
+    /* The request parameters */
+    wa_rprintf(r,"   <tr>\n");
+    wa_rprintf(r,"    <td width=\"10%%\" valign=\"top\" align=\"right\">\n");
+    wa_rprintf(r,"     <font size=\"-1\">\n");
+    wa_rprintf(r,"       Server&nbsp;Host<br>\n",r->serv->host);
+    wa_rprintf(r,"       Server&nbsp;Address<br>\n",r->serv->addr);
+    wa_rprintf(r,"       Server&nbsp;Port<br>\n",r->serv->port);
+    wa_rprintf(r,"       Client&nbsp;Host<br>\n",r->clnt->host);
+    wa_rprintf(r,"       Client&nbsp;Address<br>\n",r->clnt->addr);
+    wa_rprintf(r,"       Client&nbsp;Port<br>\n",r->clnt->port);
+    wa_rprintf(r,"       Request&nbsp;Method<br>\n",r->meth);
+    wa_rprintf(r,"       Request&nbsp;URI<br>\n",r->ruri);
+    wa_rprintf(r,"       Request&nbsp;Arguments<br>\n",r->args);
+    wa_rprintf(r,"       Request&nbsp;Protocol<br>\n",r->prot);
+    wa_rprintf(r,"       Request&nbsp;Scheme<br>\n",r->schm);
+    wa_rprintf(r,"       Authenticated&nbsp;User<br>\n",r->user);
+    wa_rprintf(r,"       Authentication&nbsp;Mechanism<br>\n",r->auth);
+    wa_rprintf(r,"       Request&nbsp;Content&nbsp;Length<br>\n",r->clen);
+    wa_rprintf(r,"     </font>\n");
+    wa_rprintf(r,"    </td>\n");
+    wa_rprintf(r,"    <td width=\"90%%\" valign=\"top\" align=\"left\">\n");
+    wa_rprintf(r,"     <font size=\"-1\"><nobr>\n");
+    wa_rprintf(r,"      <b><nobr>&quot;%s&quot;</nobr></b><br>\n",r->serv->host);
+    wa_rprintf(r,"      <b><nobr>&quot;%s&quot;</nobr></b><br>\n",r->serv->addr);
+    wa_rprintf(r,"      <b><nobr>&quot;%d&quot;</nobr></b><br>\n",r->serv->port);
+    wa_rprintf(r,"      <b><nobr>&quot;%s&quot;</nobr></b><br>\n",r->clnt->host);
+    wa_rprintf(r,"      <b><nobr>&quot;%s&quot;</nobr></b><br>\n",r->clnt->addr);
+    wa_rprintf(r,"      <b><nobr>&quot;%d&quot;</nobr></b><br>\n",r->clnt->port);
+    wa_rprintf(r,"      <b><nobr>&quot;%s&quot;</nobr></b><br>\n",r->meth);
+    wa_rprintf(r,"      <b><nobr>&quot;%s&quot;</nobr></b><br>\n",r->ruri);
+    wa_rprintf(r,"      <b><nobr>&quot;%s&quot;</nobr></b><br>\n",r->args);
+    wa_rprintf(r,"      <b><nobr>&quot;%s&quot;</nobr></b><br>\n",r->prot);
+    wa_rprintf(r,"      <b><nobr>&quot;%s&quot;</nobr></b><br>\n",r->schm);
+    wa_rprintf(r,"      <b><nobr>&quot;%s&quot;</nobr></b><br>\n",r->user);
+    wa_rprintf(r,"      <b><nobr>&quot;%s&quot;</nobr></b><br>\n",r->auth);
+    wa_rprintf(r,"      <b><nobr>&quot;%d&quot;</nobr></b>\n",r->clen);
+    wa_rprintf(r,"     </font>\n");
+    wa_rprintf(r,"    </td>\n");
+
+    /* See the request headers */
+    wa_rprintf(r,"   <tr>\n");
+    wa_rprintf(r,"    <td width=\"100%%\" colspan=\"2\">\n");
+    wa_rprintf(r,"     <font size=\"-1\">Headers</font>\n");
+    wa_rprintf(r,"    </td>\n");
+    wa_rprintf(r,"   </tr>\n");
+    wa_rprintf(r,"   <tr>\n");
+    wa_rprintf(r,"    <td width=\"10%%\" valign=\"top\" align=\"right\">\n");
+    wa_rprintf(r,"     <font size=\"-1\">\n");
+    apr_table_do(info_handle_hdrname,r,r->hdrs,NULL);
+    wa_rprintf(r,"     </font>\n");
+    wa_rprintf(r,"    </td>\n");
+    wa_rprintf(r,"    <td width=\"90%%\" valign=\"top\" align=\"left\">\n");
+    wa_rprintf(r,"     <font size=\"-1\">\n");
+    apr_table_do(info_handle_hdrvalue,r,r->hdrs,NULL);
+    wa_rprintf(r,"     </font>\n");
+    wa_rprintf(r,"    </td>\n");
+    wa_rprintf(r,"   </tr>\n");
+    wa_rflush(r);
+
+    /* Dump the request body */
+    if (r->clen>0) {
+        char *buf=(char *)apr_palloc(r->pool,1024*sizeof(char));
+        int ret=1;
+
+        wa_rprintf(r,"   <tr>\n");
+        wa_rprintf(r,"    <td width=\"100%%\" colspan=\"2\">\n");
+        wa_rprintf(r,"     <font size=\"-1\">Request Body</font>\n");
+        wa_rprintf(r,"    </td>\n");
+        wa_rprintf(r,"   </tr>\n");
+        wa_rprintf(r,"   <tr>\n");
+        wa_rprintf(r,"    <td width=\"100%%\" colspan=\"2\">\n");
+        wa_rprintf(r,"     <font size=\"-1\">\n");
+        wa_rprintf(r,"      <pre>\n");
+
+        while (ret>0) {
+            ret=wa_rread(r,buf,1024);
+            if (ret>0) {
+                wa_rwrite(r,buf,ret);
+                wa_rflush(r);
+            } else if (ret<0) {
+                wa_rprintf(r,">\n<b>TRANSFER INTERRUPTED</b>\n");
+            }
+        }
+
+        wa_rprintf(r,"      </pre>\n");
+        wa_rprintf(r,"     </font>\n");
+        wa_rprintf(r,"    </td>\n");
+        wa_rprintf(r,"   </tr>\n");
+    } else {
+        wa_rprintf(r,"   <tr>\n");
+        wa_rprintf(r,"    <td width=\"100%%\" colspan=\"2\">\n");
+        wa_rprintf(r,"     <font size=\"-1\"><i>No Request Body</i></font>\n");
+        wa_rprintf(r,"    </td>\n");
+        wa_rprintf(r,"   </tr>\n");
+    }
+
+    wa_rprintf(r,"  </table>\n");
+#endif /* ifdef DEBUG */
+    wa_rprintf(r,"  <br>\n");
+
+    wa_rprintf(r,"  </div>\n");
+    wa_rprintf(r,"  <br>\n");
+
+    wa_rprintf(r," </body>\n");
+    wa_rprintf(r,"</html>\n");
+    wa_rflush(r);
+
+    return(200);
+}
+
+/* The INFO provider structure */
+wa_provider wa_provider_info = {
+    "info",
+    info_init,
+    info_startup,
+    info_shutdown,
+    info_connect,
+    info_deploy,
+    info_conninfo,
+    info_applinfo,
+    info_handle,
+};
diff --git a/connectors/webapp/lib/pr_warp.c b/connectors/webapp/lib/pr_warp.c
new file mode 100644
index 0000000..bcd91c4
--- /dev/null
+++ b/connectors/webapp/lib/pr_warp.c
@@ -0,0 +1,497 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/* @version $Id$ */
+#include "pr_warp.h"
+
+/* Initialize this provider. */
+static const char *warp_init(void) {
+    wa_debug(WA_MARK,"WARP provider initialized");
+    return(NULL);
+}
+
+/* Notify this provider of its imminent startup. */
+static void warp_startup(void) {
+    wa_chain *elem=warp_connections;
+
+    /* Open all connections having deployed applications */
+    while (elem!=NULL) {
+        wa_connection *curr=(wa_connection *)elem->curr;
+        warp_config *conf=(warp_config *)curr->conf;
+        apr_socket_t * sock = 0;
+        wa_debug(WA_MARK,"Opening connection \"%s\"",curr->name);
+
+        sock=n_connect(curr);
+        if (sock!=NULL) {
+            wa_debug(WA_MARK,"Connection \"%s\" opened",curr->name);
+            if (c_configure(curr,sock)==wa_true) {
+                wa_debug(WA_MARK,"Connection \"%s\" configured",curr->name);
+
+                warp_sockpool_release(conf->socket_pool, curr, sock);
+
+            } else {
+                wa_log(WA_MARK,"Cannot configure connection \"%s\"",curr->name);
+            }
+        } else wa_log(WA_MARK,"Cannot open connection \"%s\"",curr->name);
+        elem=elem->next;
+    }
+
+    wa_debug(WA_MARK,"WARP provider started");
+}
+
+/* Cleans up all resources allocated by this provider. */
+static void warp_shutdown(void) {
+    wa_debug(WA_MARK,"WARP provider shut down");
+}
+
+/* Configure a connection with the parameter from the web server
+   configuration file. */
+static const char *warp_connect(wa_connection *conn, const char *param) {
+    apr_status_t r=APR_SUCCESS;
+    warp_config *conf=NULL;
+    apr_port_t port=0;
+    char *addr=NULL;
+    char *scop=NULL;
+
+    /* Allocation and checking */
+    conf=(warp_config *)apr_palloc(wa_pool,sizeof(warp_config));
+    if (conf==NULL) return("Cannot allocate connection configuration");
+
+    /* Check and parse parameter */
+    if (param==NULL) return("Parameter for connection not specified");
+    if (apr_parse_addr_port(&addr,&scop,&port,param,wa_pool)!=APR_SUCCESS)
+        return("Invalid format for parameter");
+    if (addr==NULL) return("Host name not specified in parameter");
+    if (scop!=NULL) return("Invalid format for parameter (scope)");
+    if (port==0) return("Port number not specified in parameter");
+
+    /* Create and APR sockaddr structure */
+    r=apr_sockaddr_info_get(&(conf->addr),addr,APR_INET,port,0,wa_pool);
+    if (r!=APR_SUCCESS) return("Cannot get socket address information");
+
+    /* Done */
+#if APR_HAS_THREADS
+    apr_atomic_set(&conf->serv, (unsigned) 0);
+    apr_atomic_set(&conf->open_socket_count, (unsigned) 0);
+#else
+    conf->serv = 0;
+    conf->open_socket_count = 0;
+#endif
+    conn->conf=conf;
+
+    /* Create the socket pool */
+    conf->socket_pool = warp_sockpool_create();
+    if (conf->socket_pool == NULL) return("Cannot create socket pool");
+
+    return(NULL);
+}
+
+/* Receive notification of the deployment of an application. */
+static const char *warp_deploy(wa_application *appl) {
+    wa_chain *elem=warp_connections;
+    wa_connection *conn=appl->conn;
+
+    /* Integer configuration -1 equals application not deployed */
+    appl->conf=(void *)-1;
+
+    /* Check if the connection specified in this application has already
+       been stored in our local array of connections */
+    while (elem!=NULL) {
+        if (conn==elem->curr) break;
+        elem=elem->next;
+    }
+    if (elem==NULL) {
+        elem=(wa_chain *)apr_palloc(wa_pool,sizeof(wa_chain));
+        elem->curr=conn;
+        elem->next=warp_connections;
+        warp_connections=elem;
+    }
+
+    /* Check if this application has already been stored in our local array of
+       applications */
+    elem=warp_applications;
+    while (elem!=NULL) {
+        if (appl==elem->curr) break;
+        elem=elem->next;
+    }
+    if (elem==NULL) {
+        elem=(wa_chain *)apr_palloc(wa_pool,sizeof(wa_chain));
+        elem->curr=appl;
+        elem->next=warp_applications;
+        warp_applications=elem;
+    }
+
+    return(NULL);
+}
+
+/* Describe the configuration member found in a connection. */
+static char *warp_conninfo(wa_connection *conn, apr_pool_t *pool) {
+    warp_config *conf=(warp_config *)conn->conf;
+    apr_port_t port=0;
+    char *addr=NULL;
+    char *name=NULL;
+    char *buff=NULL;
+
+#if APR_HAS_THREADS
+    apr_uint32_t socket_count = apr_atomic_read(&conf->open_socket_count);
+    apr_uint32_t server_id = apr_atomic_read(&conf->serv);
+#else
+    apr_uint32_t socket_count = conf->open_socket_count;
+    apr_uint32_t server_id = conf->serv;
+#endif
+    if (conf==NULL) return("Invalid configuration member");
+
+    apr_sockaddr_port_get(&port,conf->addr);
+    apr_sockaddr_ip_get(&addr,conf->addr);
+    apr_getnameinfo(&name,conf->addr,0);
+
+    buff=apr_psprintf(pool,"Host: %s Port:%d Address:%s Socket Count: %d Server ID: %d",
+                      name,port,addr,socket_count,server_id);
+    return(buff);
+}
+
+/* Describe the configuration member found in a web application. */
+static char *warp_applinfo(wa_application *appl, apr_pool_t *pool) {
+    return(apr_psprintf(pool,"Application ID: %d",(int)(appl->conf)));
+}
+
+/* Transmit headers */
+static int headers(void *d, const char *n, const char *v) {
+    warp_header *data=(warp_header *)d;
+    wa_connection *conn=data->conn;
+    warp_config *conf=(warp_config *)conn->conf;
+    warp_packet *pack=data->pack;
+
+    pack->type=TYPE_REQ_HEADER;
+    p_write_string(pack,(char *)n);
+    p_write_string(pack,(char *)v);
+    if (n_send(data->sock,pack)!=wa_true) {
+        data->fail=wa_true;
+        return(FALSE);
+    }
+    wa_debug(WA_MARK,"Req. header %s: %s",n,v);
+    return(TRUE);
+}
+
+/* Handle a connection from the web server. */
+static int warp_handle(wa_request *r, wa_application *appl) {
+    warp_header *h=(warp_header *)apr_palloc(r->pool,sizeof(warp_header));
+    wa_connection *conn=appl->conn;
+    warp_config *conf=(warp_config *)conn->conf;
+    warp_packet *pack=p_create(r->pool);
+
+    apr_socket_t * sock = NULL;
+
+    int status=0;
+
+    /* Check packet */
+    if (pack==NULL)
+        return(wa_rerror(WA_MARK,r,500,"Cannot create WARP packet"));
+
+    /* Check application */
+    if (((int)(appl->conf))==-1)
+        return(wa_rerror(WA_MARK,r,404,"Application not deployed"));
+
+    sock = warp_sockpool_acquire(conf->socket_pool);
+
+    /* Attempt to reconnect if disconnected */
+    if (sock==NULL) {
+        sock=n_connect(conn);
+        if (sock!=NULL) {
+            wa_debug(WA_MARK,"Connection \"%s\" opened",conn->name);
+            if (c_configure(conn,sock)==wa_true) {
+                wa_debug(WA_MARK,"Connection \"%s\" configured",conn->name);
+            } else {
+                wa_log(WA_MARK,"Cannot configure connection %s",conn->name);
+                return(wa_rerror(WA_MARK,r,500,
+                                 "Cannot configure connection \"%s\"",
+                                 conn->name));
+            }
+        } else {
+            wa_log(WA_MARK,"Cannot open connection %s",conn->name);
+            return(wa_rerror(WA_MARK,r,500,"Cannot open connection %s",
+                             conn->name));
+        }
+    }
+
+    /* Let's do it */
+    pack->type=TYPE_REQ_INIT;
+    p_write_int(pack,(int)(appl->conf));
+    p_write_string(pack,r->meth);
+    p_write_string(pack,r->ruri);
+    p_write_string(pack,r->args);
+    p_write_string(pack,r->prot);
+    if (n_send(sock,pack)!=wa_true) {
+        n_disconnect(conn, sock);
+        sock=n_connect(conn);
+        if (sock!=NULL) {
+            wa_debug(WA_MARK,"Connection \"%s\" reopened",conn->name);
+            if (c_configure(conn,sock)==wa_true) {
+                wa_debug(WA_MARK,"Connection \"%s\" reconfigured",conn->name);
+            } else {
+                wa_log(WA_MARK,"Cannot reconfigure connection %s",conn->name);
+                return(wa_rerror(WA_MARK,r,500,
+                                 "Cannot reconfigure connection \"%s\"",
+                                 conn->name));
+            }
+            if (n_send(sock,pack)!=wa_true) {
+              n_disconnect(conn, sock);
+              return(wa_rerror(WA_MARK,r,500,
+                     "Communication broken while reconnecting"));
+            } else {
+                wa_debug(WA_MARK,"Re-Req. %s %s %s",r->meth,r->ruri,r->prot);
+            }
+        } else {
+            wa_log(WA_MARK,"Cannot open connection %s",conn->name);
+            return(wa_rerror(WA_MARK,r,500,"Cannot open connection %s",
+                             conn->name));
+        }
+    } else {
+        wa_debug(WA_MARK,"Req. %s %s %s",r->meth,r->ruri,r->prot);
+    }
+    
+    p_reset(pack);
+    pack->type=TYPE_REQ_CONTENT;
+    p_write_string(pack,r->ctyp);
+    p_write_int(pack,r->clen);
+    if (n_send(sock,pack)!=wa_true) {
+        n_disconnect(conn, sock);
+        return(wa_rerror(WA_MARK,r,500,"Communication interrupted"));
+    } else {
+        wa_debug(WA_MARK,"Req. content typ=%s len=%d",r->ctyp,r->clen);
+    }
+
+    if (r->schm!=NULL) {
+        p_reset(pack);
+        pack->type=TYPE_REQ_SCHEME;
+        p_write_string(pack,r->schm);
+        if (n_send(sock,pack)!=wa_true) {
+            n_disconnect(conn,sock);
+            return(wa_rerror(WA_MARK,r,500,"Communication interrupted"));
+        } else {
+            wa_debug(WA_MARK,"Req. scheme %s",r->schm);
+        }
+    }
+
+    if ((r->user!=NULL)||(r->auth!=NULL)) {
+        p_reset(pack);
+        pack->type=TYPE_REQ_AUTH;
+        if (r->user==NULL) r->user="\0";
+        if (r->auth==NULL) r->auth="\0";
+        p_write_string(pack,r->user); 
+        p_write_string(pack,r->auth); 
+        if (n_send(sock,pack)!=wa_true) {
+            n_disconnect(conn,sock);
+            return(wa_rerror(WA_MARK,r,500,"Communication interrupted"));
+        } else {
+            wa_debug(WA_MARK,"Req. user %s auth %s",r->user,r->auth);
+        }
+    }
+
+    /* The request headers */
+    h->conn=conn;
+    h->pack=pack;
+    h->fail=wa_false;
+    h->sock=sock;
+    apr_table_do(headers,h,r->hdrs,NULL);
+    if (h->fail==wa_true) {
+        n_disconnect(conn,sock);
+        return(wa_rerror(WA_MARK,r,500,"Communication interrupted"));
+    }
+
+    /* The request client data */
+    if (r->clnt!=NULL) {
+        p_reset(pack);
+        pack->type=TYPE_REQ_CLIENT;
+        p_write_string(pack,r->clnt->host);
+        p_write_string(pack,r->clnt->addr);
+        p_write_ushort(pack,r->clnt->port);
+        if (n_send(sock,pack)!=wa_true) {
+            n_disconnect(conn,sock);
+            return(wa_rerror(WA_MARK,r,500,"Communication interrupted"));
+        } else {
+            wa_debug(WA_MARK,"Req. server %s:%d (%s)",r->clnt->host,
+                     r->clnt->port,r->clnt->addr);
+        }
+    }
+
+    /* The request server data */
+    if (r->serv!=NULL) {
+        p_reset(pack);
+        pack->type=TYPE_REQ_SERVER;
+        p_write_string(pack,r->serv->host);
+        p_write_string(pack,r->serv->addr);
+        p_write_ushort(pack,r->serv->port);
+        if (n_send(sock,pack)!=wa_true) {
+            n_disconnect(conn,sock);
+            return(wa_rerror(WA_MARK,r,500,"Communication interrupted"));
+        } else {
+            wa_debug(WA_MARK,"Req. client %s:%d (%s)",r->serv->host,
+                     r->serv->port,r->serv->addr);
+        }
+    }
+
+    p_reset(pack);
+    pack->type=TYPE_REQ_PROCEED;
+    if (n_send(sock,pack)!=wa_true) {
+        n_disconnect(conn,sock);
+        return(wa_rerror(WA_MARK,r,500,"Communication interrupted"));
+    }
+
+    
+    while (1) {
+        if (n_recv(sock,pack)!=wa_true) {
+            n_disconnect(conn,sock);
+            return(wa_rerror(WA_MARK,r,500,"Communication interrupted"));
+        }
+        switch (pack->type) {
+            case TYPE_RES_STATUS: {
+                char *mesg=NULL;
+                p_read_ushort(pack,&status);
+                p_read_string(pack,&mesg);
+                wa_debug(WA_MARK,"=== %d %s",status,mesg);
+                wa_rsetstatus(r,status,mesg);
+                break;
+            }
+            case TYPE_RES_HEADER: {
+                char *name=NULL;
+                char *valu=NULL;
+                p_read_string(pack,&name);
+                p_read_string(pack,&valu);
+                if (strcasecmp("content-type",name)==0)
+                    wa_rsetctype(r,valu);
+                else wa_rsetheader(r,name,valu);
+                wa_debug(WA_MARK,"=== %s: %s",name,valu);
+                break;
+            }
+            case TYPE_RES_COMMIT: {
+                wa_rcommit(r);
+                wa_debug(WA_MARK,"=== ");
+                break;
+            }
+            case TYPE_RES_BODY: {
+                wa_rwrite(r,pack->buff,pack->size);
+                wa_rflush(r);
+                pack->buff[pack->size]='\0';
+                wa_debug(WA_MARK,"Response body bytes: %d",pack->size);
+                break;
+            }
+            case TYPE_RES_DONE: {
+                wa_debug(WA_MARK,"=== DONE ===");
+                if (sock != NULL) {
+                    warp_sockpool_release(conf->socket_pool, conn, sock);
+                }
+                return(status);
+                break;
+            }
+            case TYPE_CBK_READ: {
+                int size=-1;
+                p_read_ushort(pack,&size);
+                p_reset(pack);
+                wa_debug(WA_MARK,"Request body bytes: (Req=%d)",size);
+                size=wa_rread(r,pack->buff,size);
+                wa_debug(WA_MARK,"Request body bytes: (Got=%d)",size);
+                if (size==0) {
+                    pack->type=TYPE_CBK_DONE;
+                } else if (size>0) {
+                    pack->type=TYPE_CBK_DATA;
+                    pack->size=size;
+                } else {
+                    pack->type=TYPE_ERROR;
+                    p_write_string(pack,"Transfer interrupted");
+                }
+                wa_debug(WA_MARK,"Request body bytes: (Sent=%d)",pack->size);
+                if (n_send(sock,pack)!=wa_true) {
+                    n_disconnect(conn,sock);
+                    return(wa_rerror(WA_MARK,r,500,"Communication interrupted"));
+                }
+                break;
+            }
+            case TYPE_ASK_SSL: {
+                wa_debug(WA_MARK,"TYPE_ASK_SSL");
+                /* Request for client certificate */
+                if (r->ssld==NULL) {
+                    pack->type=TYPE_REP_SSL_NO;
+                    pack->size=0;
+                } else {
+                    pack->type=TYPE_REP_SSL;
+                    p_write_string(pack,r->ssld->ciph);
+                    p_write_string(pack,r->ssld->sess);
+                    p_write_int(pack,r->ssld->size);
+                }
+                wa_debug(WA_MARK,"CC bytes: (Sent=%d)",pack->size);
+                if (n_send(sock,pack)!=wa_true) {
+                    n_disconnect(conn,sock);
+                    return(wa_rerror(WA_MARK,r,500,"Communication interrupted"));
+                }
+                break;
+            }
+            case TYPE_ASK_SSL_CLIENT: {
+                wa_debug(WA_MARK,"TYPE_ASK_SSL_CLIENT");
+                /* Request for client certificate */
+                if (r->ssld==NULL || r->ssld->cert==NULL) {
+                    pack->type=TYPE_REP_SSL_NO;
+                    pack->size=0;
+                } else {
+                    pack->type=TYPE_REP_SSL_CERT;
+                    p_write_string(pack,r->ssld->cert);
+                }
+                wa_debug(WA_MARK,"CC bytes: (Sent=%d)",pack->size);
+                if (n_send(sock,pack)!=wa_true) {
+                    n_disconnect(conn,sock);
+                    return(wa_rerror(WA_MARK,r,500,"Communication interrupted"));
+                }
+                break;
+            }
+            case TYPE_ERROR: {
+                char *mesg=NULL;
+                p_read_string(pack,&mesg);
+                if (sock != NULL) {
+                    warp_sockpool_release(conf->socket_pool, 
+                                                   conn, sock);
+                }
+                return(wa_rerror(WA_MARK,r,500,"%s",mesg));
+            }
+            default: {
+                n_disconnect(conn,sock);
+                return(wa_rerror(WA_MARK,r,500,"Invalid packet %d",pack->type));
+            }
+        }           
+    }
+
+    if (sock != NULL) {
+        warp_sockpool_release(conf->socket_pool, conn, sock);
+    }
+
+    return status;
+}
+
+/* The list of all configured connections */
+wa_chain *warp_connections=NULL;
+/* The list of all deployed connections */
+wa_chain *warp_applications=NULL;
+/* The warp provider structure */
+wa_provider wa_provider_warp = {
+    "warp",
+    warp_init,
+    warp_startup,
+    warp_shutdown,
+    warp_connect,
+    warp_deploy,
+    warp_conninfo,
+    warp_applinfo,
+    warp_handle,
+};
diff --git a/connectors/webapp/lib/pr_warp.h b/connectors/webapp/lib/pr_warp.h
new file mode 100644
index 0000000..545f00b
--- /dev/null
+++ b/connectors/webapp/lib/pr_warp.h
@@ -0,0 +1,128 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/* @version $Id$ */
+#ifndef _PR_WARP_H_
+#define _PR_WARP_H_
+
+#include <wa.h>
+
+/* ************************************************************************* */
+/* INSTANCE VARIABLES                                                        */
+/* ************************************************************************* */
+/* The list of all configured connections */
+extern wa_chain *warp_connections;
+/* The list of all deployed connections */
+extern wa_chain *warp_applications;
+/* The warp provider structure */
+extern wa_provider wa_provider_warp;
+
+/* ************************************************************************* */
+/* STRUCTURES                                                                */
+/* ************************************************************************* */
+
+/* Structure to maintain a pool of sockets to be used for a given
+   WARP connection */
+typedef struct warp_socket_pool {
+    wa_chain * available_socket_list;
+#if APR_HAS_THREADS 
+    apr_thread_mutex_t * pool_mutex;
+#endif
+    int available_socket_list_size;
+    wa_chain * available_elem_blocks;
+} warp_socket_pool;
+
+
+/* The WARP connection configuration structure */
+typedef struct warp_config {
+    warp_socket_pool * socket_pool;
+    apr_sockaddr_t *addr;
+
+#if APR_HAS_THREADS 
+    apr_atomic_t open_socket_count;
+    apr_atomic_t serv;
+#else
+    unsigned int open_socket_count;
+    unsigned int serv;
+#endif
+
+} warp_config;
+
+/* The WARP packet structure */
+typedef struct warp_packet {
+    apr_pool_t *pool;
+    int type;
+    int size;
+    int curr;
+    char buff[65536];
+} warp_packet;
+
+/* Structure for processin headers in WARP */
+typedef struct warp_header {
+    wa_connection *conn;
+    warp_packet *pack;
+    wa_boolean fail;
+    apr_socket_t *sock;
+} warp_header;
+
+/* ************************************************************************* */
+/* DEFINITIONS                                                               */
+/* ************************************************************************* */
+
+/* WARP definitions */
+#define VERS_MAJOR 0
+#define VERS_MINOR 10
+#include "pr_warp_defs.h"
+
+/* ************************************************************************* */
+/* PACKET FUNCTIONS FROM PR_WARP_PACKET.C                                    */
+/* ************************************************************************* */
+void p_reset(warp_packet *pack);
+warp_packet *p_create(apr_pool_t *pool);
+wa_boolean p_read_ushort(warp_packet *pack, int *x);
+wa_boolean p_read_int(warp_packet *pack, int *x);
+wa_boolean p_read_string(warp_packet *pack, char **x);
+wa_boolean p_write_ushort(warp_packet *pack, int x);
+wa_boolean p_write_int(warp_packet *pack, int x);
+wa_boolean p_write_string(warp_packet *pack, char *x);
+
+/* ************************************************************************* */
+/* NETWORK FUNCTIONS FROM PR_WARP_NETWORK.C                                  */
+/* ************************************************************************* */
+wa_boolean n_recv(apr_socket_t *sock, warp_packet *pack);
+wa_boolean n_send(apr_socket_t *sock, warp_packet *pack);
+apr_socket_t *n_connect(wa_connection *conn);
+void n_disconnect(wa_connection *conn, apr_socket_t * sock);
+
+/* ************************************************************************* */
+/* CONFIGURATION FUNCTIONS FROM PR_WARP_CONFIG.C                             */
+/* ************************************************************************* */
+wa_boolean c_check(wa_connection *conn, warp_packet *pack, apr_socket_t * sock);
+wa_boolean c_configure(wa_connection *conn, apr_socket_t *sock);
+
+
+/* ************************************************************************* */
+/* SOCKET POOL FUNCTIONS FROM PR_WARP_SOCKETPOOL.C                           */
+/* ************************************************************************* */
+
+warp_socket_pool * warp_sockpool_create();
+void warp_sockpool_destroy(warp_socket_pool * pool);
+apr_socket_t * warp_sockpool_acquire(warp_socket_pool * pool);
+void warp_sockpool_release(warp_socket_pool * pool, 
+                           wa_connection *conn, 
+                           apr_socket_t * sock);
+
+#endif /* ifndef _PR_WARP_H_ */
diff --git a/connectors/webapp/lib/pr_warp_config.c b/connectors/webapp/lib/pr_warp_config.c
new file mode 100644
index 0000000..7d410b4
--- /dev/null
+++ b/connectors/webapp/lib/pr_warp_config.c
@@ -0,0 +1,206 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/* @version $Id$ */
+#include "pr_warp.h"
+
+wa_boolean c_check(wa_connection *conn, warp_packet *pack, apr_socket_t * sock) {
+    warp_config *conf=(warp_config *)conn->conf;
+    int maj=-1;
+    int min=-1;
+    int sid=-1;
+
+    if (n_recv(sock,pack)!=wa_true) {
+        wa_log(WA_MARK,"Cannot receive handshake WARP packet");
+        return(wa_false);
+    }
+
+    if (pack->type!=TYPE_CONF_WELCOME) {
+        wa_log(WA_MARK,"Invalid WARP packet %d (WELCOME)",pack->type);
+        return(wa_false);
+    }
+
+    if (p_read_ushort(pack,&maj)!=wa_true) {
+        wa_log(WA_MARK,"Cannot read major version");
+        return(wa_false);
+    }
+
+    if (p_read_ushort(pack,&min)!=wa_true) {
+        wa_log(WA_MARK,"Cannot read minor version");
+        return(wa_false);
+    }
+
+    if ((maj!=VERS_MAJOR)||(min!=VERS_MINOR)) {
+        wa_log(WA_MARK,"Invalid WARP protocol version %d.%d",maj,min);
+        return(wa_false);
+    }
+
+    if (p_read_int(pack,&sid)!=wa_true) {
+        wa_log(WA_MARK,"Cannot read server id");
+        return(wa_false);
+    }
+
+#if APR_HAS_THREADS
+    apr_atomic_set(&conf->serv, (unsigned) sid);
+#else
+    conf->serv = (unsigned) sid;
+#endif
+    
+    wa_debug(WA_MARK,"Connection \"%s\" checked WARP/%d.%d (SERVER ID=%d)",
+        conn->name,maj,min,conf->serv);
+    return(wa_true);
+}
+
+wa_boolean c_configure(wa_connection *conn, apr_socket_t * sock) {
+    wa_chain *elem=warp_applications;
+    apr_pool_t *pool=NULL;
+    wa_boolean ret=wa_false;
+    warp_packet *pack=NULL;
+    char *temp=NULL;
+
+    if (apr_pool_create(&pool,wa_pool)!=APR_SUCCESS) {
+        wa_log(WA_MARK,"Cannot create WARP temporary configuration pool");
+        n_disconnect(conn, sock);
+        return(wa_false);
+    }
+
+    if ((pack=p_create(wa_pool))==NULL) {
+        wa_log(WA_MARK,"Cannot create WARP configuration packet");
+        n_disconnect(conn, sock);
+        apr_pool_destroy(pool);
+        return(wa_false);
+    }
+
+    if ((ret=c_check(conn,pack, sock))==wa_false) n_disconnect(conn, sock);
+
+    while (elem!=NULL) {
+        wa_application *appl=(wa_application *)elem->curr;
+
+        /* Check that the application really belongs to that connection */
+        if (strcmp(appl->conn->name,conn->name)!=0) {
+            elem=elem->next;
+            continue;
+        }
+
+        wa_debug(WA_MARK,"Deploying \"%s\" via \"%s\" in \"http://%s:%d%s\"",
+                appl->name,conn->name,appl->host->name,appl->host->port,
+                appl->rpth);
+        p_reset(pack);
+        pack->type=TYPE_CONF_DEPLOY;
+        p_write_string(pack,appl->name);
+        p_write_string(pack,appl->host->name);
+        p_write_ushort(pack,appl->host->port);
+        p_write_string(pack,appl->rpth);
+        n_send(sock,pack);
+
+        if (n_recv(sock,pack)!=wa_true) {
+            wa_log(WA_MARK,"Cannot read packet (%s:%d)",WA_MARK);
+            n_disconnect(conn, sock);
+            return(wa_false);
+        }
+        if (pack->type==TYPE_ERROR) {
+            wa_log(WA_MARK,"Cannot deploy application %s",appl->name);
+            elem=elem->next;
+            continue;
+        }
+        if (pack->type!=TYPE_CONF_APPLIC) {
+            wa_log(WA_MARK,"Unknown packet received (%d)",pack->type);
+            p_reset(pack);
+            pack->type=TYPE_FATAL;
+            p_write_string(pack,"Invalid packet received");
+            n_send(sock,pack);
+            n_disconnect(conn, sock);
+        }
+        p_read_int(pack,(int *)&appl->conf);
+        p_read_string(pack,&temp);
+        appl->lpth=apr_pstrdup(wa_pool,temp);
+        
+        /* Check if this web-application is local or not by checking if its
+           WEB-INF directory can be opened. */
+        if (appl->lpth!=NULL) {
+            apr_dir_t *dir=NULL;
+            char *webinf=apr_pstrcat(wa_pool,appl->lpth,"/WEB-INF",NULL);
+            if (apr_dir_open(&dir,webinf,wa_pool)==APR_SUCCESS) {
+                if (dir!=NULL) apr_dir_close(dir);
+                else appl->lpth=NULL;
+            } else {
+                appl->lpth=NULL;
+            }
+        }
+        
+        /* If this application is local, we want to retrieve the allowed and
+           denied mapping list. */
+        if (appl->lpth!=NULL) {
+            p_reset(pack);
+            pack->type=TYPE_CONF_MAP;
+            p_write_int(pack,(int)appl->conf);
+            n_send(sock,pack);
+            
+            while(1) {
+                if (n_recv(sock,pack)!=wa_true) {
+                    wa_log(WA_MARK,"Cannot read packet (%s:%d)",WA_MARK);
+                    n_disconnect(conn,sock);
+                    return(wa_false);
+                }
+                if (pack->type==TYPE_CONF_MAP_DONE) {
+                    wa_debug(WA_MARK,"Done mapping URLs");
+                    break;
+                } else if (pack->type==TYPE_CONF_MAP_ALLOW) {
+                    char *map=NULL;
+                    p_read_string(pack,&map);
+                    wa_debug(WA_MARK,"Allow URL mapping \"%s\"",map);
+                } else if (pack->type==TYPE_CONF_MAP_DENY) {
+                    char *map=NULL;
+                    p_read_string(pack,&map);
+                    wa_debug(WA_MARK,"Deny URL mapping \"%s\"",map);
+                }
+            }
+        }
+
+        if (appl->lpth==NULL) {
+            wa_debug(WA_MARK,"Application \"%s\" deployed with id=%d (%s)",
+                appl->name,appl->conf,"remote");
+        } else {
+            wa_debug(WA_MARK,"Application \"%s\" deployed with id=%d (%s)",
+                appl->name,appl->conf,appl->lpth);
+        }
+
+        appl->depl=wa_true;
+        elem=elem->next;
+    }
+
+    p_reset(pack);
+    pack->type=TYPE_CONF_DONE;
+    n_send(sock,pack);
+
+    if (n_recv(sock,pack)!=wa_true) {
+        wa_log(WA_MARK,"Cannot read packet (%s:%d)",WA_MARK);
+        n_disconnect(conn,sock);
+        return(wa_false);
+    }
+    if (pack->type!=TYPE_CONF_PROCEED) {
+        wa_log(WA_MARK,"Cannot proceed on this connection");
+        p_reset(pack);
+        pack->type=TYPE_FATAL;
+        p_write_string(pack,"Expected PROCEED packet not received");
+        n_send(sock,pack);
+        n_disconnect(conn,sock);
+        return(wa_false);
+    }
+
+    apr_pool_destroy(pool);
+    return(ret);
+}
diff --git a/connectors/webapp/lib/pr_warp_network.c b/connectors/webapp/lib/pr_warp_network.c
new file mode 100644
index 0000000..2667a57
--- /dev/null
+++ b/connectors/webapp/lib/pr_warp_network.c
@@ -0,0 +1,154 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/* @version $Id$ */
+#include "pr_warp.h"
+
+wa_boolean n_recv(apr_socket_t *sock, warp_packet *pack) {
+    apr_size_t len=0;
+    char hdr[3];
+    int ptr=0;
+
+    if (sock==NULL) return(wa_false);
+    if (pack==NULL) return(wa_false);
+
+    p_reset(pack);
+    len=3;
+    while(1) {
+        if (apr_recv(sock,&hdr[ptr],&len)!=APR_SUCCESS) {
+            wa_debug(WA_MARK,"Cannot receive header");
+            return(wa_false);
+        }
+        ptr+=len;
+        len=3-ptr;
+        if (len==0) break;
+    }
+    pack->type=((int)hdr[0])&0x0ff;
+    pack->size=(hdr[1]&0x0ff)<<8;
+    pack->size=pack->size|(hdr[2]&0x0ff);
+
+    if (pack->size>0) {
+        len=pack->size;
+        ptr=0;
+        while(1) {
+            if (apr_recv(sock,&pack->buff[ptr],&len)!=APR_SUCCESS) {
+                wa_debug(WA_MARK,"Cannot receive payload");
+                return(wa_false);
+            }
+            ptr+=len;
+            len=pack->size-ptr;
+            if (len==0) break;
+        }
+    }
+    
+    wa_debug(WA_MARK,"WARP <<< TYP=%02X LEN=%d",pack->type,pack->size);
+
+    return(wa_true);
+}
+
+wa_boolean n_send(apr_socket_t *sock, warp_packet *pack) {
+    apr_size_t len=0;
+    char hdr[3];
+    int ptr=0;
+
+    if (sock==NULL) return(wa_false);
+    if (pack==NULL) return(wa_false);
+
+    hdr[0]=(char)(pack->type&0x0ff);
+    hdr[1]=(char)((pack->size>>8)&0x0ff);
+    hdr[2]=(char)(pack->size&0x0ff);
+
+    len=3;
+    while(1) {
+        if (apr_send(sock,&hdr[ptr],&len)!=APR_SUCCESS) return(wa_false);
+        ptr+=len;
+        len=3-ptr;
+        if (len==0) break;
+    }
+
+    len=pack->size;
+    ptr=0;
+    while(1) {
+        if (apr_send(sock,&pack->buff[ptr],&len)!=APR_SUCCESS)
+            return(wa_false);
+        ptr+=len;
+        len=pack->size-ptr;
+        if (len==0) break;
+    }
+
+    wa_debug(WA_MARK,"WARP >>> TYP=%2X LEN=%d",pack->type,pack->size);
+
+    p_reset(pack);
+    return(wa_true);
+}
+
+/* Attempt to connect to the remote endpoint of the WARP connection (if not
+   done already). */
+apr_socket_t *n_connect(wa_connection *conn) {
+    warp_config *conf=(warp_config *)conn->conf;
+    apr_status_t ret=APR_SUCCESS;
+    apr_socket_t *sock=NULL;
+
+    ret=apr_socket_create(&sock,AF_INET,SOCK_STREAM,wa_pool);
+    if (ret!=APR_SUCCESS) {
+        sock=NULL;
+        wa_log(WA_MARK,"Cannot create socket for conn. \"%s\"",conn->name);
+        return(sock);
+    }
+
+    /* Attempt to connect to the remote endpoint */
+    ret=apr_connect(sock, conf->addr);
+    if (ret!=APR_SUCCESS) {
+        apr_shutdown(sock,APR_SHUTDOWN_READWRITE);
+        sock=NULL;
+        wa_log(WA_MARK,"Connection \"%s\" cannot connect",conn->name);
+        return(sock);
+    }
+
+#if APR_HAS_THREADS
+    apr_atomic_inc(&conf->open_socket_count);
+#else
+    conf->open_socket_count++;
+#endif
+
+    return(sock);
+}
+
+/* Attempt to disconnect a connection if connected. */
+void n_disconnect(wa_connection *conn, apr_socket_t * sock) {
+    warp_config *conf=(warp_config *)conn->conf;
+    apr_status_t ret=APR_SUCCESS;
+
+    wa_debug(WA_MARK,"Disconnecting \"%s\"",conn->name);
+
+    /* Create the APR socket if that has not been yet created */
+    if (sock==NULL) return;
+
+    /* Shutdown and close the socket (ignoring errors) */
+    ret=apr_shutdown(sock,APR_SHUTDOWN_READWRITE);
+    if (ret!=APR_SUCCESS)
+        wa_log(WA_MARK,"Cannot shutdown \"%s\"",conn->name);
+    ret=apr_socket_close(sock);
+    if (ret!=APR_SUCCESS)
+        wa_log(WA_MARK,"Cannot close \"%s\"",conn->name);
+
+#if APR_HAS_THREADS
+    apr_atomic_dec(&conf->open_socket_count);
+#else
+    conf->open_socket_count--;
+#endif
+}
+
diff --git a/connectors/webapp/lib/pr_warp_packet.c b/connectors/webapp/lib/pr_warp_packet.c
new file mode 100644
index 0000000..1cc3daf
--- /dev/null
+++ b/connectors/webapp/lib/pr_warp_packet.c
@@ -0,0 +1,117 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/* @version $Id$ */
+#include "pr_warp.h"
+
+void p_reset(warp_packet *pack) {
+    pack->type=TYPE_INVALID;
+    pack->type=TYPE_INVALID;
+    pack->size=0;
+    pack->curr=0;
+    pack->buff[0]='\0';
+}
+
+warp_packet *p_create(apr_pool_t *pool) {
+    warp_packet *pack=NULL;
+
+    if (pool==NULL) return(NULL);
+    pack=(warp_packet *)apr_palloc(pool,sizeof(warp_packet));
+    pack->pool=pool;
+    p_reset(pack);
+    return(pack);
+}
+
+wa_boolean p_read_ushort(warp_packet *pack, int *x) {
+    int k=0;
+
+    if ((pack->curr+2)>pack->size) return(wa_false);
+    k=(pack->buff[pack->curr++]&0x0ff)<<8;
+    k=k|(pack->buff[pack->curr++]&0x0ff);
+    *x=k;
+    return(wa_true);
+}
+
+wa_boolean p_read_int(warp_packet *pack, int *x) {
+    int k=0;
+
+    if ((pack->curr+2)>pack->size) return(wa_false);
+    k=(pack->buff[pack->curr++]&0x0ff)<<24;
+    k=k|((pack->buff[pack->curr++]&0x0ff)<<16);
+    k=k|((pack->buff[pack->curr++]&0x0ff)<<8);
+    k=k|(pack->buff[pack->curr++]&0x0ff);
+    *x=k;
+    return(wa_true);
+}
+
+wa_boolean p_read_string(warp_packet *pack, char **x) {
+    int len=0;
+
+    if (p_read_ushort(pack,&len)==wa_false) {
+        *x=NULL;
+        wa_debug(WA_MARK,"Cannot read string length");
+        return(wa_false);
+    }
+    if ((pack->curr+len)>pack->size) {
+        *x=NULL;
+        wa_debug(WA_MARK,"String too long (len=%d curr=%d size=%d)",
+                 len,pack->curr,pack->size);
+        return(wa_false);
+    }
+
+    *x=(char *)apr_palloc(pack->pool,(len+2)*sizeof(char));
+    if (*x==NULL) return(wa_false);
+
+    apr_cpystrn(*x,&pack->buff[pack->curr],len+1);
+    pack->curr+=len;
+    return(wa_true);
+}
+
+wa_boolean p_write_ushort(warp_packet *pack, int x) {
+    if (pack->size>65533) return(wa_false);
+    pack->buff[pack->size++]=(x>>8)&0x0ff;
+    pack->buff[pack->size++]=x&0x0ff;
+    return(wa_true);
+}
+
+wa_boolean p_write_int(warp_packet *pack, int x) {
+    if (pack->size>65531) return(wa_false);
+    pack->buff[pack->size++]=(x>>24)&0x0ff;
+    pack->buff[pack->size++]=(x>>16)&0x0ff;
+    pack->buff[pack->size++]=(x>>8)&0x0ff;
+    pack->buff[pack->size++]=x&0x0ff;
+    return(wa_true);
+}
+
+wa_boolean p_write_string(warp_packet *pack, char *x) {
+    int len=0;
+    char *k=NULL;
+    int q=0;
+
+    if (x==NULL) return(p_write_ushort(pack,0));
+    for (k=x; k[0]!='\0'; k++);
+    len=k-x;
+    if (p_write_ushort(pack,len)==wa_false) {
+        pack->size-=2;
+        return(wa_false);
+    }
+    if ((pack->size+len)>65535) {
+        pack->size-=2;
+        return(wa_false);
+    }
+    for (q=0;q<len;q++) pack->buff[pack->size++]=x[q];
+    return(wa_true);
+}
diff --git a/connectors/webapp/lib/pr_warp_socketpool.c b/connectors/webapp/lib/pr_warp_socketpool.c
new file mode 100644
index 0000000..d273c79
--- /dev/null
+++ b/connectors/webapp/lib/pr_warp_socketpool.c
@@ -0,0 +1,195 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+
+#include "pr_warp.h"
+
+
+
+/* Define the maximum number of idle sockets to leave aside for a connection */
+
+#if APR_HAS_THREADS
+#define MAX_SOCKET_POOL_SIZE 25
+#else
+#define MAX_SOCKET_POOL_SIZE 1
+#endif
+
+
+/* Create a warp socket pool.  A warp socket pool is a pool of connected, free
+   sockets for a given warp connection. */
+warp_socket_pool * warp_sockpool_create()
+{
+    int idx;
+
+    warp_socket_pool * pool =
+        (warp_socket_pool*)apr_palloc(wa_pool,sizeof(warp_socket_pool));
+
+    /* Start the pool with no available sockets. */
+    pool->available_socket_list = NULL;
+    pool->available_socket_list_size = 0;
+
+    /* Create mutex to provide thread-safe access to socket pool. */
+#if APR_HAS_THREADS
+    if (apr_thread_mutex_create(&pool->pool_mutex,
+                                APR_THREAD_MUTEX_DEFAULT, 
+                                wa_pool) !=APR_SUCCESS)
+        return 0;
+#endif
+
+    /* Set up array of wa_chain elements to be used later for elements in the
+       available socket list.  By allocating them up front we avoid the use
+       of malloc and free during normal operation. */
+    pool->available_elem_blocks = 
+        (wa_chain*)apr_palloc(wa_pool,sizeof(wa_chain)*MAX_SOCKET_POOL_SIZE);
+
+    /* From the array create a linked-list. */
+    for (idx = 0; idx < MAX_SOCKET_POOL_SIZE - 1; idx++) {
+        pool->available_elem_blocks[idx].next = 
+            & pool->available_elem_blocks[idx+1];
+    }
+    pool->available_elem_blocks[idx-1].next = NULL;
+
+    return pool;
+}
+
+
+/* Destroy the socket pool specified. */
+void warp_sockpool_destroy(warp_socket_pool * pool)
+{
+    apr_status_t ret = APR_SUCCESS;
+    apr_socket_t * sock = NULL;
+
+    wa_chain *elem = pool->available_socket_list;
+
+    /* Destroy mutex associated with the pool */
+#if APR_HAS_THREADS
+    ret = apr_thread_mutex_destroy(pool->pool_mutex);
+    pool->pool_mutex = NULL;
+#endif
+
+    if (ret!=APR_SUCCESS)
+        wa_log(WA_MARK,"Cannot destroy socket pool mutex");
+
+    /* Destroy all the sockets in the pool */
+    while (elem != NULL) {
+        sock = (apr_socket_t*) elem->curr;
+
+        if (sock != NULL) {
+            ret = apr_shutdown(sock, APR_SHUTDOWN_READWRITE);
+            ret = apr_socket_close(sock);
+        }
+
+        elem->curr = NULL;
+        elem = elem->next;
+    }
+
+    pool->available_socket_list_size = 0;
+    pool->available_socket_list = NULL;
+}
+
+
+
+/* Acquire a socket from the socket pool specified.  If there
+   are no available sockets in the pool then NULL is returned. */
+apr_socket_t * warp_sockpool_acquire(warp_socket_pool * pool)
+{
+    apr_socket_t * sock = NULL;
+    wa_chain * first_elem = 0;
+
+    if (pool->available_socket_list_size > 0)
+    {
+        /* Preliminary check of available_socket_list_size suggests
+           that there is an available socket in the pool.  Lock the
+           pool and then check again. */
+
+#if APR_HAS_THREADS 
+        if (apr_thread_mutex_lock(pool->pool_mutex) != APR_SUCCESS)
+            return NULL;
+#endif
+
+        if (pool->available_socket_list_size > 0)
+        {
+            /* There is an available socket in the pool.  Get
+               one from the pool. */
+            first_elem = pool->available_socket_list;
+
+            /* Update the available socket list. */
+            pool->available_socket_list_size--;
+            pool->available_socket_list = first_elem->next;
+
+            /* Extract the socket from first_elem. */
+            sock = (apr_socket_t*) first_elem->curr;
+
+            /* Put the now unused first_elem block in the
+               available elem list so it can be later reused. */
+            first_elem->curr = 0;
+            first_elem->next = pool->available_elem_blocks;
+            pool->available_elem_blocks = first_elem;
+        }
+
+#if APR_HAS_THREADS 
+        apr_thread_mutex_unlock(pool->pool_mutex);
+#endif
+    }
+
+    return sock;
+}
+
+
+
+/* Returns the socket specified to socket pool.  If the pool already contains
+   the maximum allowable number of sockets for a pool (MAX_SOCKET_POOL_SIZE)
+   then the socket is simply disconnected. */
+void warp_sockpool_release(warp_socket_pool * pool, 
+                           wa_connection *conn, 
+                           apr_socket_t * sock)
+{
+    wa_chain * new_elem = NULL;
+
+#if APR_HAS_THREADS 
+    if (apr_thread_mutex_lock(pool->pool_mutex) != APR_SUCCESS)
+        return;
+#endif
+
+    /* If we already have the maximum number of available sockets in
+       the pool then just disconnect the socket and return */
+    if (pool->available_socket_list_size==MAX_SOCKET_POOL_SIZE) {
+#if APR_HAS_THREADS
+        apr_thread_mutex_unlock(pool->pool_mutex);
+#endif
+        n_disconnect(conn, sock);
+        return;
+    }
+
+    /* Add the socket given to the pool...*/
+    pool->available_socket_list_size++;
+
+    /* Get a new pool element from the available list. */
+    new_elem = pool->available_elem_blocks;
+    pool->available_elem_blocks = new_elem->next;
+    
+    /* Initialise the new pool element. */
+    new_elem->curr = sock;
+    new_elem->next = pool->available_socket_list;
+
+    /* Add the element to the head of the available
+       socket list. */
+    pool->available_socket_list = new_elem;
+
+#if APR_HAS_THREADS 
+    apr_thread_mutex_unlock(pool->pool_mutex);
+#endif
+}
diff --git a/connectors/webapp/lib/wa_config.c b/connectors/webapp/lib/wa_config.c
new file mode 100644
index 0000000..05fa0e1
--- /dev/null
+++ b/connectors/webapp/lib/wa_config.c
@@ -0,0 +1,130 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/* @version $Id$ */
+#include <wa.h>
+
+/* Allocate and set up a <code>wa_application</code> member. */
+const char *wa_capplication(wa_application **a, const char *n,
+                            const char *p) {
+    wa_application *appl=NULL;
+    char buf[1024];
+    int l=0;
+
+    /* Check parameters */
+    if (a==NULL) return("Invalid application storage location");
+    if (n==NULL) return("Invalid application name");
+    if (p==NULL) return("Invalid application path");
+
+    /* Allocate some memory */
+    appl=(wa_application *)apr_palloc(wa_pool,sizeof(wa_application));
+    if (appl==NULL) return("Cannot allocate memory");
+
+    /* Set up application name */
+    appl->name=apr_pstrdup(wa_pool,n);
+
+    /* Normalize the application root URL path */
+    strncpy(buf,p,1024);
+    l=strlen(buf)-1;
+    if (buf[l]=='/') buf[l]='\0';
+    if (buf[0]=='/' || l==0) {
+      appl->rpth=apr_pstrcat(wa_pool,buf,"/",NULL);
+    } else {
+      appl->rpth=apr_pstrcat(wa_pool,"/",buf,"/",NULL);
+    }
+
+    /* Zero all other parameters */
+    appl->host=NULL;
+    appl->conn=NULL;
+    appl->conf=NULL;
+    appl->lpth=NULL;
+    appl->allw=NULL;
+    appl->deny=NULL;
+    appl->depl=wa_false;
+
+    /* Done */
+    wa_debug(WA_MARK,"Created application \"%s\" in path \"%s\"",
+             appl->name,appl->rpth);
+    *a=appl;
+    return(NULL);
+}
+
+/* Allocate and set up a virtual host */
+const char *wa_cvirtualhost(wa_virtualhost **h, const char *n, int p) {
+    wa_virtualhost *host=NULL;
+
+    /* Check parameters */
+    if (h==NULL) return("Invalid virtual host storage location");
+    if (n==NULL) return("Invalid virtual host name");
+    if (p<1) return("Invalid port number (p<1) No \"Port\" statement found");
+    if (p>65535) return("Invalid port number (p>65535)");
+
+    /* Allocate some memory */
+    host=(wa_virtualhost *)apr_palloc(wa_pool,sizeof(wa_virtualhost));
+    if (host==NULL) return("Cannot allocate memory");
+
+    /* Set up parameters */
+    host->name=apr_pstrdup(wa_pool,n);
+    host->port=p;
+    host->apps=NULL;
+
+    /* Done! :) */
+    wa_debug(WA_MARK,"Created virtual host \"%s:%d\"",host->name,host->port);
+    *h=host;
+    return(NULL);
+}
+
+/* Allocate and setup a connection */
+const char *wa_cconnection(wa_connection **c, const char *n,
+                           const char *p, const char *a) {
+    wa_connection *conn=NULL;
+    const char *ret=NULL;
+    int x=0;
+
+    /* Check parameters */
+    if (c==NULL) return("Invalid connection storage location");
+    if (n==NULL) return("Invalid connection name");
+    if (p==NULL) return("Invalid connection provider");
+
+    /* Allocate some memory */
+    conn=(wa_connection *)apr_palloc(wa_pool,sizeof(wa_connection));
+    if (conn==NULL) return("Cannot allocate memory");
+
+    /* Set up the parameter string */
+    conn->name=apr_pstrdup(wa_pool,n);
+    if (a==NULL) {
+        conn->parm=apr_pstrdup(wa_pool,"\0");
+    } else {
+        conn->parm=apr_pstrdup(wa_pool,a);
+    }
+
+    /* Retrieve the provider and set up the conection */
+    conn->conf=NULL;
+    while(wa_providers[x]!=NULL) {
+        if(strcasecmp(wa_providers[x]->name,p)==0) {
+             conn->prov=wa_providers[x];
+             break;
+        } else x++;
+    }
+    if (conn->prov==NULL) return("Invalid provider name specified");
+    if ((ret=conn->prov->connect(conn,a))!=NULL) return(ret);
+
+    /* Done */
+    wa_debug(WA_MARK,"Created connection \"%s\" (Prov: \"%s\" Param: \"%s\")",
+             n,p,a);
+    *c=conn;
+    return(NULL);
+}
diff --git a/connectors/webapp/lib/wa_main.c b/connectors/webapp/lib/wa_main.c
new file mode 100644
index 0000000..4c0610d
--- /dev/null
+++ b/connectors/webapp/lib/wa_main.c
@@ -0,0 +1,175 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/* @version $Id$ */
+#include <wa.h>
+
+/* The current APR memory pool. */
+apr_pool_t *wa_pool=NULL;
+/* The list of all deployed applications. */
+wa_chain *wa_configuration=NULL;
+/* The list of all compiled in providers */
+wa_provider *wa_providers[] = {
+    &wa_provider_info,
+    &wa_provider_warp,
+    /*&wa_provider_jni,*/
+    NULL,
+};
+
+/* Initialize the WebApp Library. */
+const char *wa_init(void) {
+    int x=0;
+
+    wa_debug(WA_MARK,"WebApp Library initializing");
+
+    /* Check the main APR pool. */
+    if (wa_pool==NULL) {
+        wa_debug(WA_MARK,"Initializing APR");
+        if (apr_initialize()!=APR_SUCCESS)
+            return("Cannot initialize APR");
+        if (apr_pool_create(&wa_pool,NULL)!=APR_SUCCESS)
+            return("Cannot create WebApp Library memory pool");
+        if (wa_pool==NULL)
+            return("Invalid WebApp Library memory pool created");
+#if APR_HAS_THREADS
+        if (apr_atomic_init(wa_pool)!=APR_SUCCESS)
+            return("Cannot initialize atomic integer library");
+#endif
+    }
+
+    /* Initialize providers */
+    while(wa_providers[x]!=NULL) {
+        const char *ret=wa_providers[x]->init();
+        if (ret!=NULL) {
+            wa_shutdown();
+            return(ret);
+        }
+        x++;
+    }
+
+    /* Done */
+    wa_debug(WA_MARK,"WebApp Library initialized");
+    return(NULL);
+}
+
+/* Startup all providers. */
+void wa_startup(void) {
+    int x=0;
+
+    while(wa_providers[x]!=NULL) wa_providers[x++]->startup();
+
+    wa_debug(WA_MARK,"WebApp Library started");
+}
+
+/* Clean up the WebApp Library. */
+void wa_shutdown(void) {
+    int x=0;
+
+    /* Initialization check */
+    if (wa_pool==NULL) return;
+
+    /* Shutdown providers */
+    while(wa_providers[x]!=NULL) wa_providers[x++]->shutdown();
+
+    /* Clean up this library and APR */
+    apr_pool_destroy(wa_pool);
+    wa_pool=NULL;
+    wa_configuration=NULL;
+    apr_terminate();
+
+    wa_debug(WA_MARK,"WebApp Library shut down");
+}
+
+/* Deploy a web-application. */
+const char *wa_deploy(wa_application *a, wa_virtualhost *h, wa_connection *c) {
+    wa_chain *elem=NULL;
+    const char *ret=NULL;
+
+    /* Check parameters */
+    if (a==NULL) return("Invalid application for deployment");
+    if (h==NULL) return("Invalid virtual host for deployment");
+    if (c==NULL) return("Invalid connection for deployment");
+
+    /* Check if another application was deployed under the same URI path in
+       the same virtual host */
+    elem=h->apps;
+    while (elem!=NULL) {
+        wa_application *curr=(wa_application *)elem->curr;
+        if (strcasecmp(curr->rpth,a->rpth)==0)
+            return("Duplicate application specified for the same URL path");
+        elem=elem->next;
+    }
+
+    /* Deploy */
+    a->host=h;
+    a->conn=c;
+
+    /* Give the opportunity to the provider to be notified of the deployment of
+       this application */
+    if ((ret=c->prov->deploy(a))!=NULL) return(ret);
+
+    /* Append this application to the list of deployed applications in the
+       virtual host */
+    elem=(wa_chain *)apr_palloc(wa_pool,sizeof(wa_chain));
+    elem->curr=a;
+    elem->next=h->apps;
+    h->apps=elem;
+
+    /* Check if this virtual host is already present in the configuration */
+    if (wa_configuration!=NULL) {
+        elem=wa_configuration;
+        while (elem!=NULL) {
+            /* Compare the pointers to structures, we *MIGHT* allow two
+               virtual hosts to have the same name and port. The selection
+               of the host is done at the web server level */
+            if (elem->curr==h) return(NULL);
+            elem=elem->next;
+        }
+    }
+
+    /* We didn't find this host in our list, we need to add it to the conf. */
+    elem=(wa_chain *)apr_palloc(wa_pool,sizeof(wa_chain));
+    elem->curr=h;
+    elem->next=wa_configuration;
+    wa_configuration=elem;
+
+    /* Done */
+    wa_debug(WA_MARK,"Application %s deployed for http://%s:%d%s (Conn: %s)",
+             a->name,h->name,h->port,a->rpth,c->name);
+    return(NULL);
+}
+
+/* Dump some debugging information. */
+void wa_debug(const char *f, const int l, const char *fmt, ...) {
+#ifdef DEBUG
+    char hdr[128];
+    char dta[640];
+    char buf[768];
+    apr_time_t at;
+    char st[128];
+    va_list ap;
+
+    at=apr_time_now();
+    apr_ctime(st, at);
+    va_start(ap,fmt);
+    apr_snprintf(hdr,128,"[%s] %d (%s:%d)",st,getpid(),f,l);
+    apr_vsnprintf(dta,640,fmt,ap);
+    apr_snprintf(buf,728,"%s %s\n",hdr,dta);
+    fprintf(stderr,"%s",buf);
+    fflush(stderr);
+    va_end(ap);
+#endif /* ifdef DEBUG */
+}
diff --git a/connectors/webapp/lib/wa_request.c b/connectors/webapp/lib/wa_request.c
new file mode 100644
index 0000000..9558189
--- /dev/null
+++ b/connectors/webapp/lib/wa_request.c
@@ -0,0 +1,218 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/* @version $Id$ */
+#include <wa.h>
+
+/* Allocate a new request structure. */
+const char *wa_ralloc(wa_request **r, wa_handler *h, void *d) {
+    apr_pool_t *pool=NULL;
+    wa_request *req=NULL;
+
+    if(apr_pool_create(&pool,wa_pool)!=APR_SUCCESS)
+        return("Cannot create request memory pool");
+    if((req=apr_palloc(pool,sizeof(wa_request)))==NULL) {
+        apr_pool_destroy(pool);
+        return("Cannot allocate memory for the request structure");
+    }
+
+    if (h==NULL) return("Invalid request handler specified");
+
+    /* Set up the server host data record */
+    if((req->serv=apr_palloc(pool,sizeof(wa_hostdata)))==NULL) {
+        apr_pool_destroy(pool);
+        return("Cannot allocate memory for server host data structure");
+    } else {
+        req->serv->host=NULL;
+        req->serv->addr=NULL;
+        req->serv->port=-1;
+    }
+
+    /* Set up the server host data record */
+    if((req->clnt=apr_palloc(pool,sizeof(wa_hostdata)))==NULL) {
+        apr_pool_destroy(pool);
+        return("Cannot allocate memory for client host data structure");
+    } else {
+        req->clnt->host=NULL;
+        req->clnt->addr=NULL;
+        req->clnt->port=-1;
+    }
+
+    /* Set up the headers table */
+    req->hdrs=apr_table_make(pool,0);
+
+    /* Set up all other request members */
+    req->pool=pool;
+    req->hand=h;
+    req->data=d;
+    req->meth=NULL;
+    req->ruri=NULL;
+    req->args=NULL;
+    req->prot=NULL;
+    req->schm=NULL;
+    req->user=NULL;
+    req->auth=NULL;
+    req->ssld=NULL;
+    req->clen=0;
+    req->ctyp="\0";
+    req->rlen=0;
+
+    /* All done */
+    *r=req;
+    return(NULL);
+}
+
+/* Clean up and free the memory used by a request structure. */
+const char *wa_rfree(wa_request *r) {
+    if (r==NULL) return("Invalid request member");
+    apr_pool_destroy(r->pool);
+    return(NULL);
+}
+
+
+/* Dump headers for wa_rerror */
+static int headers(void *d, const char *key, const char *val) {
+    wa_request *r=(wa_request *)d;
+
+    wa_rprintf(r,"   <dd>%s: %s</dd>\n",key,val);
+    return(TRUE);
+}
+
+/* Dump an error response */
+int wa_rerror(const char *file, const int line, wa_request *r, int s,
+              const char *fmt, ...) {
+    va_list ap;
+    char buf[1024];
+
+    va_start(ap,fmt);
+    apr_vsnprintf(buf,1024,fmt,ap);
+    va_end(ap);
+
+    r->hand->log(r,WA_MARK,buf);
+
+    wa_rsetstatus(r,s,NULL);
+    wa_rsetctype(r,"text/html");
+    wa_rcommit(r);
+
+    wa_rprintf(r,"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">");
+    wa_rprintf(r,"\n\n");
+    wa_rprintf(r,"<html>\n");
+    wa_rprintf(r," <head>\n");
+    wa_rprintf(r,"  <title>WebApp: Error %d (File: %s Line: %d)</title>",s,
+               file,line);
+    wa_rprintf(r," </head>\n");
+    wa_rprintf(r," <body>\n");
+    wa_rprintf(r,"  <div align=\"center\">\n");
+    wa_rprintf(r,"    <h1>WebApp: Error %d</h1>\n",s);
+    wa_rprintf(r,"    <i>(File: %s Line: %d)</i>\n",file,line);
+    wa_rprintf(r,"  </div>\n");
+    wa_rprintf(r,"  <hr>\n");
+    wa_rprintf(r,"  %s\n",buf);
+    wa_rprintf(r,"  <hr>\n");
+#ifdef DEBUG
+    wa_rprintf(r,"  <dl>\n");
+    wa_rprintf(r,"   <dt>Your Request:</dt>\n");
+    wa_rprintf(r,"   <dd>Server Host: \"%s\"</dd>\n",r->serv->host);
+    wa_rprintf(r,"   <dd>Server Address: \"%s\"</dd>\n",r->serv->addr);
+    wa_rprintf(r,"   <dd>Server Port: \"%d\"</dd>\n",r->serv->port);
+    wa_rprintf(r,"   <dd>Client Host: \"%s\"</dd>\n",r->clnt->host);
+    wa_rprintf(r,"   <dd>Client Address: \"%s\"</dd>\n",r->clnt->addr);
+    wa_rprintf(r,"   <dd>Client Port: \"%d\"</dd>\n",r->clnt->port);
+    wa_rprintf(r,"   <dd>Request Method: \"%s\"</dd>\n",r->meth);
+    wa_rprintf(r,"   <dd>Request URI: \"%s\"</dd>\n",r->ruri);
+    wa_rprintf(r,"   <dd>Request Arguments: \"%s\"</dd>\n",r->args);
+    wa_rprintf(r,"   <dd>Request Protocol: \"%s\"</dd>\n",r->prot);
+    wa_rprintf(r,"   <dd>Request Scheme: \"%s\"</dd>\n",r->schm);
+    wa_rprintf(r,"   <dd>Request User: \"%s\"</dd>\n",r->user);
+    wa_rprintf(r,"   <dd>Request Authentication Mech.: \"%s\"</dd>\n",r->auth);
+    wa_rprintf(r,"   <dd>Request Content-Length: \"%d\"</dd>\n",r->clen);
+    wa_rprintf(r,"   <dt>Your Headers:</dt>\n");
+    apr_table_do(headers,r,r->hdrs,NULL);
+    wa_rprintf(r,"  </dl>\n");
+    wa_rprintf(r,"  <hr>\n");
+#endif /* ifdef DEBUG */
+    wa_rprintf(r," </body>\n");
+    wa_rprintf(r,"</html>\n");
+    wa_rflush(r);
+
+    return(s);
+}
+
+/* Invoke a request in a web application. */
+int wa_rinvoke(wa_request *r, wa_application *a) {
+    /*
+     * If the application is not yet deployed that could be because
+     * Apache was started before Tomcat.
+     */
+    if (a->depl!=wa_true) {
+        wa_log(WA_MARK, "Re-Trying to deploy connections");
+        wa_startup();
+        if (a->depl!=wa_true)
+            return(wa_rerror(WA_MARK,r,404,"Web-application not yet deployed"));
+    }
+    return(a->conn->prov->handle(r,a));
+}
+
+void wa_rlog(wa_request *r, const char *f, const int l, const char *fmt, ...) {
+    va_list ap;
+    char buf[1024];
+
+    va_start(ap,fmt);
+    apr_vsnprintf(buf,1024,fmt,ap);
+    va_end(ap);
+
+    r->hand->log(r,f,l,buf);
+}
+
+void wa_rsetstatus(wa_request *r, int status, char *message) {
+    r->hand->setstatus(r,status,message);
+}
+
+void wa_rsetctype(wa_request *r, char *type) {
+    r->hand->setctype(r,type);
+}
+
+void wa_rsetheader(wa_request *r, char *name, char *value) {
+    r->hand->setheader(r,name,value);
+}
+
+void wa_rcommit(wa_request *r) {
+    r->hand->commit(r);
+}
+
+void wa_rflush(wa_request *r) {
+    r->hand->flush(r);
+}
+
+int wa_rread(wa_request *r, char *buf, int len) {
+    return(r->hand->read(r,buf,len));
+}
+
+int wa_rwrite(wa_request *r, char *buf, int len) {
+    return(r->hand->write(r,buf,len));
+}
+
+int wa_rprintf(wa_request *r, const char *fmt, ...) {
+    va_list ap;
+    char buf[1024];
+    int ret=0;
+
+    va_start(ap,fmt);
+    ret=apr_vsnprintf(buf,1024,fmt,ap);
+    va_end(ap);
+
+    return(r->hand->write(r,buf,ret));
+}
diff --git a/connectors/webapp/support/.cvsignore b/connectors/webapp/support/.cvsignore
new file mode 100644
index 0000000..e43b0f9
--- /dev/null
+++ b/connectors/webapp/support/.cvsignore
@@ -0,0 +1 @@
+.DS_Store
diff --git a/connectors/webapp/support/buildconf.sh b/connectors/webapp/support/buildconf.sh
new file mode 100755
index 0000000..59c6ac4
--- /dev/null
+++ b/connectors/webapp/support/buildconf.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+#
+# Copyright 1999-2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# ------------------------------------------------------------------------- #
+# Author Pier Fumagalli <mailto:pier@betaversion.org>
+# Version $Id$
+# ------------------------------------------------------------------------- #
+
+# ------------------------------------------------------------------------- #
+# Check if we are in the right directory to run this script                 # 
+# ------------------------------------------------------------------------- #
+if test ! -f ./configure.in
+then
+    echo "cannot find \"configure.in\" file."
+    exit 1
+fi
+
+# ------------------------------------------------------------------------- #
+# Check if we have the correct autoconf                                     # 
+# ------------------------------------------------------------------------- #
+echo "--- Checking \"autoconf\" version"
+VERSION=`autoconf --version 2> /dev/null | \
+    head -1 | \
+    sed -e 's/^[^0-9]*//' -e 's/[a-z]* *$//'`
+
+if test -z "${VERSION}"; then
+    echo "autoconf not found."
+    echo "autoconf version 2.52 or newer required to build from CVS."
+    exit 1
+fi
+
+IFS=.
+set $VERSION
+IFS=' '
+if test "$1" = "2" -a "$2" -lt "52" || test "$1" -lt "2"; then
+    echo "autoconf version $VERSION found."
+    echo "autoconf version 2.52 or newer required to build from CVS."
+    exit 1
+fi
+
+echo "autoconf version $VERSION detected."
+
+# ------------------------------------------------------------------------- #
+# Try to run the APR buildconf script if we have the sources locally        # 
+# ------------------------------------------------------------------------- #
+echo ""
+if [ -f "./apr/buildconf" ]
+then
+    echo "--- Running APR \"buildconf\" script"
+    (
+        cd ./apr
+        sh ./buildconf
+    )
+else
+    echo "--- Cannot run APR \"buildconf\" script"
+    echo "If you need to build the WebApp module for Apache 1.3"
+    echo "you will have to download the APR library sources from"
+    echo "http://apr.apache.org/ and run its \"buildconf\" script"
+    echo "  # cd [path to APR sources]"
+    echo "  # ./buildconf"
+    echo "  # cd [path to WebApp sources]"
+    echo "Then remember to run the WebApp \"configure\" script"
+    echo "specifying the \"--with-apr=[path to APR sources]\" extra"
+    echo "command line option."
+fi
+
+# ------------------------------------------------------------------------- #
+# Run autoconf to create the configure script                               # 
+# ------------------------------------------------------------------------- #
+echo ""
+echo "--- Creating WebApp \"configure\" script"
+echo "Creating configure ..."
+rm -rf autom4te.cache
+autoconf
+
+# ------------------------------------------------------------------------- #
+# Finish up                                                                 # 
+# ------------------------------------------------------------------------- #
+echo ""
+echo "--- All done"
diff --git a/connectors/webapp/support/config.guess b/connectors/webapp/support/config.guess
new file mode 100755
index 0000000..dff9e48
--- /dev/null
+++ b/connectors/webapp/support/config.guess
@@ -0,0 +1,1317 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+#   Free Software Foundation, Inc.
+
+timestamp='2001-09-04'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Written by Per Bothner <bothner@cygnus.com>.
+# Please send patches to <config-patches@gnu.org>.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+
+dummy=dummy-$$
+trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script.
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+set_cc_for_build='case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int dummy(){}" > $dummy.c ;
+	for c in cc gcc c89 ; do
+	  ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1 ;
+	  if test $? = 0 ; then
+	     CC_FOR_BUILD="$c"; break ;
+	  fi ;
+	done ;
+	rm -f $dummy.c $dummy.o $dummy.rel ;
+	if test x"$CC_FOR_BUILD" = x ; then
+	  CC_FOR_BUILD=no_compiler_found ;
+	fi
+	;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+	PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+	# Netbsd (nbsd) targets should (where applicable) match one or
+	# more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+	# switched to ELF, *-*-netbsd* would select the old
+	# object file format.  This provides both forward
+	# compatibility and a consistent mechanism for selecting the
+	# object file format.
+	# Determine the machine/vendor (is the vendor relevant).
+	case "${UNAME_MACHINE}" in
+	    amiga) machine=m68k-unknown ;;
+	    arm32) machine=arm-unknown ;;
+	    atari*) machine=m68k-atari ;;
+	    sun3*) machine=m68k-sun ;;
+	    mac68k) machine=m68k-apple ;;
+	    macppc) machine=powerpc-apple ;;
+	    hp3[0-9][05]) machine=m68k-hp ;;
+	    ibmrt|romp-ibm) machine=romp-ibm ;;
+	    *) machine=${UNAME_MACHINE}-unknown ;;
+	esac
+	# The Operating System including object format, if it has switched
+	# to ELF recently, or will in the future.
+	case "${UNAME_MACHINE}" in
+	    i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k)
+		eval $set_cc_for_build
+		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+			| grep __ELF__ >/dev/null
+		then
+		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+		    # Return netbsd for either.  FIX?
+		    os=netbsd
+		else
+		    os=netbsdelf
+		fi
+		;;
+	    *)
+	        os=netbsd
+		;;
+	esac
+	# The OS release
+	release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+	# contains redundant information, the shorter form:
+	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+	echo "${machine}-${os}${release}"
+	exit 0 ;;
+    alpha:OSF1:*:*)
+	if test $UNAME_RELEASE = "V4.0"; then
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+	fi
+	# A Vn.n version is a released version.
+	# A Tn.n version is a released field test version.
+	# A Xn.n version is an unreleased experimental baselevel.
+	# 1.2 uses "1.2" for uname -r.
+	cat <<EOF >$dummy.s
+	.data
+\$Lformat:
+	.byte 37,100,45,37,120,10,0	# "%d-%x\n"
+
+	.text
+	.globl main
+	.align 4
+	.ent main
+main:
+	.frame \$30,16,\$26,0
+	ldgp \$29,0(\$27)
+	.prologue 1
+	.long 0x47e03d80 # implver \$0
+	lda \$2,-1
+	.long 0x47e20c21 # amask \$2,\$1
+	lda \$16,\$Lformat
+	mov \$0,\$17
+	not \$1,\$18
+	jsr \$26,printf
+	ldgp \$29,0(\$26)
+	mov 0,\$16
+	jsr \$26,exit
+	.end main
+EOF
+	eval $set_cc_for_build
+	$CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+	if test "$?" = 0 ; then
+		case `./$dummy` in
+			0-0)
+				UNAME_MACHINE="alpha"
+				;;
+			1-0)
+				UNAME_MACHINE="alphaev5"
+				;;
+			1-1)
+				UNAME_MACHINE="alphaev56"
+				;;
+			1-101)
+				UNAME_MACHINE="alphapca56"
+				;;
+			2-303)
+				UNAME_MACHINE="alphaev6"
+				;;
+			2-307)
+				UNAME_MACHINE="alphaev67"
+				;;
+			2-1307)
+				UNAME_MACHINE="alphaev68"
+				;;
+		esac
+	fi
+	rm -f $dummy.s $dummy
+	echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+	exit 0 ;;
+    Alpha\ *:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# Should we change UNAME_MACHINE based on the output of uname instead
+	# of the specific Alpha model?
+	echo alpha-pc-interix
+	exit 0 ;;
+    21064:Windows_NT:50:3)
+	echo alpha-dec-winnt3.5
+	exit 0 ;;
+    Amiga*:UNIX_System_V:4.0:*)
+	echo m68k-unknown-sysv4
+	exit 0;;
+    amiga:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+	echo ${UNAME_MACHINE}-unknown-amigaos
+	exit 0 ;;
+    arc64:OpenBSD:*:*)
+	echo mips64el-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    arc:OpenBSD:*:*)
+	echo mipsel-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    hkmips:OpenBSD:*:*)
+	echo mips-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    pmax:OpenBSD:*:*)
+	echo mipsel-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    sgi:OpenBSD:*:*)
+	echo mips-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    wgrisc:OpenBSD:*:*)
+	echo mipsel-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    *:OS/390:*:*)
+	echo i370-ibm-openedition
+	exit 0 ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+	echo arm-acorn-riscix${UNAME_RELEASE}
+	exit 0;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+	echo hppa1.1-hitachi-hiuxmpp
+	exit 0;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+	# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+	if test "`(/bin/universe) 2>/dev/null`" = att ; then
+		echo pyramid-pyramid-sysv3
+	else
+		echo pyramid-pyramid-bsd
+	fi
+	exit 0 ;;
+    NILE*:*:*:dcosx)
+	echo pyramid-pyramid-svr4
+	exit 0 ;;
+    sun4H:SunOS:5.*:*)
+	echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+	echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    i86pc:SunOS:5.*:*)
+	echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:6*:*)
+	# According to config.sub, this is the proper way to canonicalize
+	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+	# it's likely to be more like Solaris than SunOS4.
+	echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:*:*)
+	case "`/usr/bin/arch -k`" in
+	    Series*|S4*)
+		UNAME_RELEASE=`uname -v`
+		;;
+	esac
+	# Japanese Language versions have a version number like `4.1.3-JL'.
+	echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+	exit 0 ;;
+    sun3*:SunOS:*:*)
+	echo m68k-sun-sunos${UNAME_RELEASE}
+	exit 0 ;;
+    sun*:*:4.2BSD:*)
+	UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+	test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+	case "`/bin/arch`" in
+	    sun3)
+		echo m68k-sun-sunos${UNAME_RELEASE}
+		;;
+	    sun4)
+		echo sparc-sun-sunos${UNAME_RELEASE}
+		;;
+	esac
+	exit 0 ;;
+    aushp:SunOS:*:*)
+	echo sparc-auspex-sunos${UNAME_RELEASE}
+	exit 0 ;;
+    sparc*:NetBSD:*)
+	echo `uname -p`-unknown-netbsd${UNAME_RELEASE}
+	exit 0 ;;
+    atari*:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+	exit 0 ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+	echo m68k-atari-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+	exit 0 ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+        echo m68k-milan-mint${UNAME_RELEASE}
+        exit 0 ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+        echo m68k-hades-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+        echo m68k-unknown-mint${UNAME_RELEASE}
+        exit 0 ;;
+    sun3*:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mac68k:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mvme68k:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mvme88k:OpenBSD:*:*)
+	echo m88k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    powerpc:machten:*:*)
+	echo powerpc-apple-machten${UNAME_RELEASE}
+	exit 0 ;;
+    RISC*:Mach:*:*)
+	echo mips-dec-mach_bsd4.3
+	exit 0 ;;
+    RISC*:ULTRIX:*:*)
+	echo mips-dec-ultrix${UNAME_RELEASE}
+	exit 0 ;;
+    VAX*:ULTRIX*:*:*)
+	echo vax-dec-ultrix${UNAME_RELEASE}
+	exit 0 ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+	echo clipper-intergraph-clix${UNAME_RELEASE}
+	exit 0 ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+	#if defined (host_mips) && defined (MIPSEB)
+	#if defined (SYSTYPE_SYSV)
+	  printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_SVR4)
+	  printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+	  printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+	#endif
+	#endif
+	  exit (-1);
+	}
+EOF
+	$CC_FOR_BUILD $dummy.c -o $dummy \
+	  && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+	  && rm -f $dummy.c $dummy && exit 0
+	rm -f $dummy.c $dummy
+	echo mips-mips-riscos${UNAME_RELEASE}
+	exit 0 ;;
+    Motorola:PowerMAX_OS:*:*)
+	echo powerpc-motorola-powermax
+	exit 0 ;;
+    Night_Hawk:Power_UNIX:*:*)
+	echo powerpc-harris-powerunix
+	exit 0 ;;
+    m88k:CX/UX:7*:*)
+	echo m88k-harris-cxux7
+	exit 0 ;;
+    m88k:*:4*:R4*)
+	echo m88k-motorola-sysv4
+	exit 0 ;;
+    m88k:*:3*:R3*)
+	echo m88k-motorola-sysv3
+	exit 0 ;;
+    AViiON:dgux:*:*)
+        # DG/UX returns AViiON for all architectures
+        UNAME_PROCESSOR=`/usr/bin/uname -p`
+	if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+	then
+	    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+	       [ ${TARGET_BINARY_INTERFACE}x = x ]
+	    then
+		echo m88k-dg-dgux${UNAME_RELEASE}
+	    else
+		echo m88k-dg-dguxbcs${UNAME_RELEASE}
+	    fi
+	else
+	    echo i586-dg-dgux${UNAME_RELEASE}
+	fi
+ 	exit 0 ;;
+    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
+	echo m88k-dolphin-sysv3
+	exit 0 ;;
+    M88*:*:R3*:*)
+	# Delta 88k system running SVR3
+	echo m88k-motorola-sysv3
+	exit 0 ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+	echo m88k-tektronix-sysv3
+	exit 0 ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+	echo m68k-tektronix-bsd
+	exit 0 ;;
+    *:IRIX*:*:*)
+	echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+	exit 0 ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+	echo romp-ibm-aix      # uname -m gives an 8 hex-code CPU id
+	exit 0 ;;              # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+	echo i386-ibm-aix
+	exit 0 ;;
+    ia64:AIX:*:*)
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+	exit 0 ;;
+    *:AIX:2:3)
+	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+		eval $set_cc_for_build
+		sed 's/^		//' << EOF >$dummy.c
+		#include <sys/systemcfg.h>
+
+		main()
+			{
+			if (!__power_pc())
+				exit(1);
+			puts("powerpc-ibm-aix3.2.5");
+			exit(0);
+			}
+EOF
+		$CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0
+		rm -f $dummy.c $dummy
+		echo rs6000-ibm-aix3.2.5
+	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+		echo rs6000-ibm-aix3.2.4
+	else
+		echo rs6000-ibm-aix3.2
+	fi
+	exit 0 ;;
+    *:AIX:*:[45])
+	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'`
+	if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+		IBM_ARCH=rs6000
+	else
+		IBM_ARCH=powerpc
+	fi
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+	exit 0 ;;
+    *:AIX:*:*)
+	echo rs6000-ibm-aix
+	exit 0 ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+	echo romp-ibm-bsd4.4
+	exit 0 ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+	echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+	exit 0 ;;                           # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+	echo rs6000-bull-bosx
+	exit 0 ;;
+    DPX/2?00:B.O.S.:*:*)
+	echo m68k-bull-sysv3
+	exit 0 ;;
+    9000/[34]??:4.3bsd:1.*:*)
+	echo m68k-hp-bsd
+	exit 0 ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+	echo m68k-hp-bsd4.4
+	exit 0 ;;
+    9000/[34678]??:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	case "${UNAME_MACHINE}" in
+	    9000/31? )            HP_ARCH=m68000 ;;
+	    9000/[34]?? )         HP_ARCH=m68k ;;
+	    9000/[678][0-9][0-9])
+              case "${HPUX_REV}" in
+                11.[0-9][0-9])
+                  if [ -x /usr/bin/getconf ]; then
+                    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                    case "${sc_cpu_version}" in
+                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+                      532)                      # CPU_PA_RISC2_0
+                        case "${sc_kernel_bits}" in
+                          32) HP_ARCH="hppa2.0n" ;;
+                          64) HP_ARCH="hppa2.0w" ;;
+                        esac ;;
+                    esac
+                  fi ;;
+              esac
+              if [ "${HP_ARCH}" = "" ]; then
+	      eval $set_cc_for_build
+              sed 's/^              //' << EOF >$dummy.c
+
+              #define _HPUX_SOURCE
+              #include <stdlib.h>
+              #include <unistd.h>
+
+              int main ()
+              {
+              #if defined(_SC_KERNEL_BITS)
+                  long bits = sysconf(_SC_KERNEL_BITS);
+              #endif
+                  long cpu  = sysconf (_SC_CPU_VERSION);
+
+                  switch (cpu)
+              	{
+              	case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+              	case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+              	case CPU_PA_RISC2_0:
+              #if defined(_SC_KERNEL_BITS)
+              	    switch (bits)
+              		{
+              		case 64: puts ("hppa2.0w"); break;
+              		case 32: puts ("hppa2.0n"); break;
+              		default: puts ("hppa2.0"); break;
+              		} break;
+              #else  /* !defined(_SC_KERNEL_BITS) */
+              	    puts ("hppa2.0"); break;
+              #endif
+              	default: puts ("hppa1.0"); break;
+              	}
+                  exit (0);
+              }
+EOF
+	    (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy`
+	    if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi
+	    rm -f $dummy.c $dummy
+	fi ;;
+	esac
+	echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+	exit 0 ;;
+    ia64:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	echo ia64-hp-hpux${HPUX_REV}
+	exit 0 ;;
+    3050*:HI-UX:*:*)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#include <unistd.h>
+	int
+	main ()
+	{
+	  long cpu = sysconf (_SC_CPU_VERSION);
+	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+	     results, however.  */
+	  if (CPU_IS_PA_RISC (cpu))
+	    {
+	      switch (cpu)
+		{
+		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+		  default: puts ("hppa-hitachi-hiuxwe2"); break;
+		}
+	    }
+	  else if (CPU_IS_HP_MC68K (cpu))
+	    puts ("m68k-hitachi-hiuxwe2");
+	  else puts ("unknown-hitachi-hiuxwe2");
+	  exit (0);
+	}
+EOF
+	$CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0
+	rm -f $dummy.c $dummy
+	echo unknown-hitachi-hiuxwe2
+	exit 0 ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+	echo hppa1.1-hp-bsd
+	exit 0 ;;
+    9000/8??:4.3bsd:*:*)
+	echo hppa1.0-hp-bsd
+	exit 0 ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+	echo hppa1.0-hp-mpeix
+	exit 0 ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+	echo hppa1.1-hp-osf
+	exit 0 ;;
+    hp8??:OSF1:*:*)
+	echo hppa1.0-hp-osf
+	exit 0 ;;
+    i*86:OSF1:*:*)
+	if [ -x /usr/sbin/sysversion ] ; then
+	    echo ${UNAME_MACHINE}-unknown-osf1mk
+	else
+	    echo ${UNAME_MACHINE}-unknown-osf1
+	fi
+	exit 0 ;;
+    parisc*:Lites*:*:*)
+	echo hppa1.1-hp-lites
+	exit 0 ;;
+    hppa*:OpenBSD:*:*)
+	echo hppa-unknown-openbsd
+	exit 0 ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+	echo c1-convex-bsd
+        exit 0 ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+        exit 0 ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+	echo c34-convex-bsd
+        exit 0 ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+	echo c38-convex-bsd
+        exit 0 ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+	echo c4-convex-bsd
+        exit 0 ;;
+    CRAY*X-MP:*:*:*)
+	echo xmp-cray-unicos
+        exit 0 ;;
+    CRAY*Y-MP:*:*:*)
+	echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*[A-Z]90:*:*:*)
+	echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+	      -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*TS:*:*:*)
+	echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*T3D:*:*:*)
+	echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*T3E:*:*:*)
+	echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*SV1:*:*:*)
+	echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY-2:*:*:*)
+	echo cray2-cray-unicos
+        exit 0 ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+	FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+        exit 0 ;;
+    hp300:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+	echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    sparc*:BSD/OS:*:*)
+	echo sparc-unknown-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    *:BSD/OS:*:*)
+	echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    *:FreeBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+	exit 0 ;;
+    *:OpenBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+	exit 0 ;;
+    i*:CYGWIN*:*)
+	echo ${UNAME_MACHINE}-pc-cygwin
+	exit 0 ;;
+    i*:MINGW*:*)
+	echo ${UNAME_MACHINE}-pc-mingw32
+	exit 0 ;;
+    i*:PW*:*)
+	echo ${UNAME_MACHINE}-pc-pw32
+	exit 0 ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+	# UNAME_MACHINE based on the output of uname instead of i386?
+	echo i386-pc-interix
+	exit 0 ;;
+    i*:UWIN*:*)
+	echo ${UNAME_MACHINE}-pc-uwin
+	exit 0 ;;
+    p*:CYGWIN*:*)
+	echo powerpcle-unknown-cygwin
+	exit 0 ;;
+    prep*:SunOS:5.*:*)
+	echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    *:GNU:*:*)
+	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+	exit 0 ;;
+    i*86:Minix:*:*)
+	echo ${UNAME_MACHINE}-pc-minix
+	exit 0 ;;
+    arm*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    ia64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux
+	exit 0 ;;
+    m68*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    mips:Linux:*:*)
+	case `sed -n '/^byte/s/^.*: \(.*\) endian/\1/p' < /proc/cpuinfo` in
+	  big)    echo mips-unknown-linux-gnu && exit 0 ;;
+	  little) echo mipsel-unknown-linux-gnu && exit 0 ;;
+	esac
+	;;
+    ppc:Linux:*:*)
+	echo powerpc-unknown-linux-gnu
+	exit 0 ;;
+    ppc64:Linux:*:*)
+	echo powerpc64-unknown-linux-gnu
+	exit 0 ;;
+    alpha:Linux:*:*)
+	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+	  EV5)   UNAME_MACHINE=alphaev5 ;;
+	  EV56)  UNAME_MACHINE=alphaev56 ;;
+	  PCA56) UNAME_MACHINE=alphapca56 ;;
+	  PCA57) UNAME_MACHINE=alphapca56 ;;
+	  EV6)   UNAME_MACHINE=alphaev6 ;;
+	  EV67)  UNAME_MACHINE=alphaev67 ;;
+	  EV68*) UNAME_MACHINE=alphaev68 ;;
+        esac
+	objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+	if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+	echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+	exit 0 ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+	# Look for CPU level
+	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+	  PA7*) echo hppa1.1-unknown-linux-gnu ;;
+	  PA8*) echo hppa2.0-unknown-linux-gnu ;;
+	  *)    echo hppa-unknown-linux-gnu ;;
+	esac
+	exit 0 ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+	echo hppa64-unknown-linux-gnu
+	exit 0 ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+	echo ${UNAME_MACHINE}-ibm-linux
+	exit 0 ;;
+    sh*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    x86_64:Linux:*:*)
+	echo x86_64-unknown-linux-gnu
+	exit 0 ;;
+    i*86:Linux:*:*)
+	# The BFD linker knows what the default object file format is, so
+	# first see if it will tell us. cd to the root directory to prevent
+	# problems with other programs or directories called `ld' in the path.
+	ld_supported_targets=`cd /; ld --help 2>&1 \
+			 | sed -ne '/supported targets:/!d
+				    s/[ 	][ 	]*/ /g
+				    s/.*supported targets: *//
+				    s/ .*//
+				    p'`
+        case "$ld_supported_targets" in
+	  elf32-i386)
+		TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+		;;
+	  a.out-i386-linux)
+		echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+		exit 0 ;;		
+	  coff-i386)
+		echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+		exit 0 ;;
+	  "")
+		# Either a pre-BFD a.out linker (linux-gnuoldld) or
+		# one that does not give us useful --help.
+		echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+		exit 0 ;;
+	esac
+	# Determine whether the default compiler is a.out or elf
+	eval $set_cc_for_build
+	cat >$dummy.c <<EOF
+#include <features.h>
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+#ifdef __ELF__
+# ifdef __GLIBC__
+#  if __GLIBC__ >= 2
+    printf ("%s-pc-linux-gnu\n", argv[1]);
+#  else
+    printf ("%s-pc-linux-gnulibc1\n", argv[1]);
+#  endif
+# else
+   printf ("%s-pc-linux-gnulibc1\n", argv[1]);
+# endif
+#else
+  printf ("%s-pc-linux-gnuaout\n", argv[1]);
+#endif
+  return 0;
+}
+EOF
+	$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0
+	rm -f $dummy.c $dummy
+	test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+	;;
+    i*86:DYNIX/ptx:4*:*)
+	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+	# earlier versions are messed up and put the nodename in both
+	# sysname and nodename.
+	echo i386-sequent-sysv4
+	exit 0 ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+        # Unixware is an offshoot of SVR4, but it has its own version
+        # number series starting with 2...
+        # I am not positive that other SVR4 systems won't match this,
+	# I just have to hope.  -- rms.
+        # Use sysv4.2uw... so that sysv4* matches it.
+	echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+	exit 0 ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+	UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+		echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+	else
+		echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+	fi
+	exit 0 ;;
+    i*86:*:5:[78]*)
+	case `/bin/uname -X | grep "^Machine"` in
+	    *486*)	     UNAME_MACHINE=i486 ;;
+	    *Pentium)	     UNAME_MACHINE=i586 ;;
+	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+	esac
+	echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+	exit 0 ;;
+    i*86:*:3.2:*)
+	if test -f /usr/options/cb.name; then
+		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+		echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+	elif /bin/uname -X 2>/dev/null >/dev/null ; then
+		UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
+		(/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
+		(/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+			&& UNAME_MACHINE=i586
+		(/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		(/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+	else
+		echo ${UNAME_MACHINE}-pc-sysv32
+	fi
+	exit 0 ;;
+    i*86:*DOS:*:*)
+	echo ${UNAME_MACHINE}-pc-msdosdjgpp
+	exit 0 ;;
+    pc:*:*:*)
+	# Left here for compatibility:
+        # uname -m prints for DJGPP always 'pc', but it prints nothing about
+        # the processor, so we play safe by assuming i386.
+	echo i386-pc-msdosdjgpp
+        exit 0 ;;
+    Intel:Mach:3*:*)
+	echo i386-pc-mach3
+	exit 0 ;;
+    paragon:*:*:*)
+	echo i860-intel-osf1
+	exit 0 ;;
+    i860:*:4.*:*) # i860-SVR4
+	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+	  echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+	else # Add other i860-SVR4 vendors below as they are discovered.
+	  echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+	fi
+	exit 0 ;;
+    mini*:CTIX:SYS*5:*)
+	# "miniframe"
+	echo m68010-convergent-sysv
+	exit 0 ;;
+    M68*:*:R3V[567]*:*)
+	test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+    3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0)
+	OS_REL=''
+	test -r /etc/.relid \
+	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	  && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+          && echo i486-ncr-sysv4 && exit 0 ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+	echo m68k-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    mc68030:UNIX_System_V:4.*:*)
+	echo m68k-atari-sysv4
+	exit 0 ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+	echo i386-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    TSUNAMI:LynxOS:2.*:*)
+	echo sparc-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    rs6000:LynxOS:2.*:*)
+	echo rs6000-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+	echo powerpc-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    SM[BE]S:UNIX_SV:*:*)
+	echo mips-dde-sysv${UNAME_RELEASE}
+	exit 0 ;;
+    RM*:ReliantUNIX-*:*:*)
+	echo mips-sni-sysv4
+	exit 0 ;;
+    RM*:SINIX-*:*:*)
+	echo mips-sni-sysv4
+	exit 0 ;;
+    *:SINIX-*:*:*)
+	if uname -p 2>/dev/null >/dev/null ; then
+		UNAME_MACHINE=`(uname -p) 2>/dev/null`
+		echo ${UNAME_MACHINE}-sni-sysv4
+	else
+		echo ns32k-sni-sysv
+	fi
+	exit 0 ;;
+    PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                      # says <Richard.M.Bartel@ccMail.Census.GOV>
+        echo i586-unisys-sysv4
+        exit 0 ;;
+    *:UNIX_System_V:4*:FTX*)
+	# From Gerald Hewes <hewes@openmarket.com>.
+	# How about differentiating between stratus architectures? -djm
+	echo hppa1.1-stratus-sysv4
+	exit 0 ;;
+    *:*:*:FTX*)
+	# From seanf@swdc.stratus.com.
+	echo i860-stratus-sysv4
+	exit 0 ;;
+    *:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	echo hppa1.1-stratus-vos
+	exit 0 ;;
+    mc68*:A/UX:*:*)
+	echo m68k-apple-aux${UNAME_RELEASE}
+	exit 0 ;;
+    news*:NEWS-OS:6*:*)
+	echo mips-sony-newsos6
+	exit 0 ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+	if [ -d /usr/nec ]; then
+	        echo mips-nec-sysv${UNAME_RELEASE}
+	else
+	        echo mips-unknown-sysv${UNAME_RELEASE}
+	fi
+        exit 0 ;;
+    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
+	echo powerpc-be-beos
+	exit 0 ;;
+    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
+	echo powerpc-apple-beos
+	exit 0 ;;
+    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
+	echo i586-pc-beos
+	exit 0 ;;
+    SX-4:SUPER-UX:*:*)
+	echo sx4-nec-superux${UNAME_RELEASE}
+	exit 0 ;;
+    SX-5:SUPER-UX:*:*)
+	echo sx5-nec-superux${UNAME_RELEASE}
+	exit 0 ;;
+    Power*:Rhapsody:*:*)
+	echo powerpc-apple-rhapsody${UNAME_RELEASE}
+	exit 0 ;;
+    *:Rhapsody:*:*)
+	echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+	exit 0 ;;
+    *:Darwin:*:*)
+	echo `uname -p`-apple-darwin${UNAME_RELEASE}
+	exit 0 ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+	if test "${UNAME_MACHINE}" = "x86pc"; then
+		UNAME_MACHINE=pc
+	fi
+	echo `uname -p`-${UNAME_MACHINE}-nto-qnx
+	exit 0 ;;
+    *:QNX:*:4*)
+	echo i386-pc-qnx
+	exit 0 ;;
+    NSR-[KW]:NONSTOP_KERNEL:*:*)
+	echo nsr-tandem-nsk${UNAME_RELEASE}
+	exit 0 ;;
+    *:NonStop-UX:*:*)
+	echo mips-compaq-nonstopux
+	exit 0 ;;
+    BS2000:POSIX*:*:*)
+	echo bs2000-siemens-sysv
+	exit 0 ;;
+    DS/*:UNIX_System_V:*:*)
+	echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+	exit 0 ;;
+    *:Plan9:*:*)
+	# "uname -m" is not consistent, so use $cputype instead. 386
+	# is converted to i386 for consistency with other x86
+	# operating systems.
+	if test "$cputype" = "386"; then
+	    UNAME_MACHINE=i386
+	else
+	    UNAME_MACHINE="$cputype"
+	fi
+	echo ${UNAME_MACHINE}-unknown-plan9
+	exit 0 ;;
+    i*86:OS/2:*:*)
+	# If we were able to find `uname', then EMX Unix compatibility
+	# is probably installed.
+	echo ${UNAME_MACHINE}-pc-os2-emx
+	exit 0 ;;
+    *:TOPS-10:*:*)
+	echo pdp10-unknown-tops10
+	exit 0 ;;
+    *:TENEX:*:*)
+	echo pdp10-unknown-tenex
+	exit 0 ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+	echo pdp10-dec-tops20
+	exit 0 ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+	echo pdp10-xkl-tops20
+	exit 0 ;;
+    *:TOPS-20:*:*)
+	echo pdp10-unknown-tops20
+	exit 0 ;;
+    *:ITS:*:*)
+	echo pdp10-unknown-its
+	exit 0 ;;
+    i*86:XTS-300:*:STOP)
+	echo ${UNAME_MACHINE}-unknown-stop
+	exit 0 ;;
+    i*86:atheos:*:*)
+	echo ${UNAME_MACHINE}-unknown-atheos
+	exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+	  ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+	printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+	printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+#  include <sys/param.h>
+#  if defined (BSD)
+#   if BSD == 43
+      printf ("vax-dec-bsd4.3\n"); exit (0);
+#   else
+#    if BSD == 199006
+      printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#    else
+      printf ("vax-dec-bsd\n"); exit (0);
+#    endif
+#   endif
+#  else
+    printf ("vax-dec-bsd\n"); exit (0);
+#  endif
+# else
+    printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0
+rm -f $dummy.c $dummy
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+	echo c1-convex-bsd
+	exit 0 ;;
+    c2*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+	exit 0 ;;
+    c34*)
+	echo c34-convex-bsd
+	exit 0 ;;
+    c38*)
+	echo c38-convex-bsd
+	exit 0 ;;
+    c4*)
+	echo c4-convex-bsd
+	exit 0 ;;
+    esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+    ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/connectors/webapp/support/config.sub b/connectors/webapp/support/config.sub
new file mode 100755
index 0000000..393f13d
--- /dev/null
+++ b/connectors/webapp/support/config.sub
@@ -0,0 +1,1411 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+#   Free Software Foundation, Inc.
+
+timestamp='2001-09-07'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit 0;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | storm-chaos* | os2-emx* | windows32-*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+	-sun*os*)
+		# Prevent following clause from handling this invalid input.
+		;;
+	-dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+	-att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+	-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+	-apple | -axis)
+		os=
+		basic_machine=$1
+		;;
+	-sim | -cisco | -oki | -wec | -winbond)
+		os=
+		basic_machine=$1
+		;;
+	-scout)
+		;;
+	-wrs)
+		os=-vxworks
+		basic_machine=$1
+		;;
+	-chorusos*)
+		os=-chorusos
+		basic_machine=$1
+		;;
+ 	-chorusrdb)
+ 		os=-chorusrdb
+		basic_machine=$1
+ 		;;
+	-hiux*)
+		os=-hiuxwe2
+		;;
+	-sco5)
+		os=-sco3.2v5
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco4)
+		os=-sco3.2v4
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2.[4-9]*)
+		os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2v[4-9]*)
+		# Don't forget version if it is 3.2v4 or newer.
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco*)
+		os=-sco3.2v2
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-udk*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-isc)
+		os=-isc2.2
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-clix*)
+		basic_machine=clipper-intergraph
+		;;
+	-isc*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-lynx*)
+		os=-lynxos
+		;;
+	-ptx*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+		;;
+	-windowsnt*)
+		os=`echo $os | sed -e 's/windowsnt/winnt/'`
+		;;
+	-psos*)
+		os=-psos
+		;;
+	-mint | -mint[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+	# Recognize the basic CPU types without company name.
+	# Some are omitted here because they have special meanings below.
+	1750a | 580 \
+	| a29k \
+	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+	| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+	| c4x | clipper \
+	| d10v | d30v | dsp16xx \
+	| fr30 \
+	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+	| i370 | i860 | i960 | ia64 \
+	| m32r | m68000 | m68k | m88k | mcore \
+	| mips16 | mips64 | mips64el | mips64orion | mips64orionel \
+	| mips64vr4100 | mips64vr4100el | mips64vr4300 \
+	| mips64vr4300el | mips64vr5000 | mips64vr5000el \
+	| mipsbe | mipseb | mipsel | mipsle | mipstx39 | mipstx39el \
+	| mipsisa32 \
+	| mn10200 | mn10300 \
+	| ns16k | ns32k \
+	| openrisc \
+	| pdp10 | pdp11 | pj | pjl \
+	| powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+	| pyramid \
+	| s390 | s390x \
+	| sh | sh[34] | sh[34]eb | shbe | shle \
+	| sparc | sparc64 | sparclet | sparclite | sparcv9 | sparcv9b \
+	| stormy16 | strongarm \
+	| tahoe | thumb | tic80 | tron \
+	| v850 \
+	| we32k \
+	| x86 | xscale \
+	| z8k)
+		basic_machine=$basic_machine-unknown
+		;;
+	m6811 | m68hc11 | m6812 | m68hc12)
+		# Motorola 68HC11/12.
+		basic_machine=$basic_machine-unknown
+		os=-none
+		;;
+	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+		;;
+
+	# We use `pc' rather than `unknown'
+	# because (1) that's what they normally are, and
+	# (2) the word "unknown" tends to confuse beginning users.
+	i*86 | x86_64)
+	  basic_machine=$basic_machine-pc
+	  ;;
+	# Object if more than one company name word.
+	*-*-*)
+		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+		exit 1
+		;;
+	# Recognize the basic CPU types with company name.
+	580-* \
+	| a29k-* \
+	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+	| alphapca5[67]-* | arc-* \
+	| arm-*  | armbe-* | armle-* | armv*-* \
+	| bs2000-* \
+	| c[123]* | c30-* | [cjt]90-* | c54x-* \
+	| clipper-* | cray2-* | cydra-* \
+	| d10v-* | d30v-* \
+	| elxsi-* \
+	| f30[01]-* | f700-* | fr30-* | fx80-* \
+	| h8300-* | h8500-* \
+	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+	| i*86-* | i860-* | i960-* | ia64-* \
+	| m32r-* \
+	| m68000-* | m680[01234]0-* | m68360-* | m683?2-* | m68k-* \
+	| m88110-* | m88k-* | mcore-* \
+	| mips-* | mips16-* | mips64-* | mips64el-* | mips64orion-* \
+	| mips64orionel-* | mips64vr4100-* | mips64vr4100el-* \
+	| mips64vr4300-* | mips64vr4300el-* | mipsbe-* | mipseb-* \
+	| mipsle-* | mipsel-* | mipstx39-* | mipstx39el-* \
+	| none-* | np1-* | ns16k-* | ns32k-* \
+	| orion-* \
+	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+	| pyramid-* \
+	| romp-* | rs6000-* \
+	| s390-* | s390x-* \
+	| sh-* | sh[34]-* | sh[34]eb-* | shbe-* | shle-* \
+	| sparc-* | sparc64-* | sparc86x-* | sparclite-* \
+	| sparcv9-* | sparcv9b-* | stormy16-* | strongarm-* | sv1-* \
+	| t3e-* | tahoe-* | thumb-* | tic30-* | tic54x-* | tic80-* | tron-* \
+	| v850-* | vax-* \
+	| we32k-* \
+	| x86-* | x86_64-* | xmp-* | xps100-* | xscale-* \
+	| ymp-* \
+	| z8k-*)
+		;;
+	# Recognize the various machine names and aliases which stand
+	# for a CPU type and a company and sometimes even an OS.
+	386bsd)
+		basic_machine=i386-unknown
+		os=-bsd
+		;;
+	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+		basic_machine=m68000-att
+		;;
+	3b*)
+		basic_machine=we32k-att
+		;;
+	a29khif)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	adobe68k)
+		basic_machine=m68010-adobe
+		os=-scout
+		;;
+	alliant | fx80)
+		basic_machine=fx80-alliant
+		;;
+	altos | altos3068)
+		basic_machine=m68k-altos
+		;;
+	am29k)
+		basic_machine=a29k-none
+		os=-bsd
+		;;
+	amdahl)
+		basic_machine=580-amdahl
+		os=-sysv
+		;;
+	amiga | amiga-*)
+		basic_machine=m68k-unknown
+		;;
+	amigaos | amigados)
+		basic_machine=m68k-unknown
+		os=-amigaos
+		;;
+	amigaunix | amix)
+		basic_machine=m68k-unknown
+		os=-sysv4
+		;;
+	apollo68)
+		basic_machine=m68k-apollo
+		os=-sysv
+		;;
+	apollo68bsd)
+		basic_machine=m68k-apollo
+		os=-bsd
+		;;
+	aux)
+		basic_machine=m68k-apple
+		os=-aux
+		;;
+	balance)
+		basic_machine=ns32k-sequent
+		os=-dynix
+		;;
+	convex-c1)
+		basic_machine=c1-convex
+		os=-bsd
+		;;
+	convex-c2)
+		basic_machine=c2-convex
+		os=-bsd
+		;;
+	convex-c32)
+		basic_machine=c32-convex
+		os=-bsd
+		;;
+	convex-c34)
+		basic_machine=c34-convex
+		os=-bsd
+		;;
+	convex-c38)
+		basic_machine=c38-convex
+		os=-bsd
+		;;
+	cray | ymp)
+		basic_machine=ymp-cray
+		os=-unicos
+		;;
+	cray2)
+		basic_machine=cray2-cray
+		os=-unicos
+		;;
+	[cjt]90)
+		basic_machine=${basic_machine}-cray
+		os=-unicos
+		;;
+	crds | unos)
+		basic_machine=m68k-crds
+		;;
+	cris | cris-* | etrax*)
+		basic_machine=cris-axis
+		;;
+	da30 | da30-*)
+		basic_machine=m68k-da30
+		;;
+	decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+		basic_machine=mips-dec
+		;;
+	delta | 3300 | motorola-3300 | motorola-delta \
+	      | 3300-motorola | delta-motorola)
+		basic_machine=m68k-motorola
+		;;
+	delta88)
+		basic_machine=m88k-motorola
+		os=-sysv3
+		;;
+	dpx20 | dpx20-*)
+		basic_machine=rs6000-bull
+		os=-bosx
+		;;
+	dpx2* | dpx2*-bull)
+		basic_machine=m68k-bull
+		os=-sysv3
+		;;
+	ebmon29k)
+		basic_machine=a29k-amd
+		os=-ebmon
+		;;
+	elxsi)
+		basic_machine=elxsi-elxsi
+		os=-bsd
+		;;
+	encore | umax | mmax)
+		basic_machine=ns32k-encore
+		;;
+	es1800 | OSE68k | ose68k | ose | OSE)
+		basic_machine=m68k-ericsson
+		os=-ose
+		;;
+	fx2800)
+		basic_machine=i860-alliant
+		;;
+	genix)
+		basic_machine=ns32k-ns
+		;;
+	gmicro)
+		basic_machine=tron-gmicro
+		os=-sysv
+		;;
+	go32)
+		basic_machine=i386-pc
+		os=-go32
+		;;
+	h3050r* | hiux*)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	h8300hms)
+		basic_machine=h8300-hitachi
+		os=-hms
+		;;
+	h8300xray)
+		basic_machine=h8300-hitachi
+		os=-xray
+		;;
+	h8500hms)
+		basic_machine=h8500-hitachi
+		os=-hms
+		;;
+	harris)
+		basic_machine=m88k-harris
+		os=-sysv3
+		;;
+	hp300-*)
+		basic_machine=m68k-hp
+		;;
+	hp300bsd)
+		basic_machine=m68k-hp
+		os=-bsd
+		;;
+	hp300hpux)
+		basic_machine=m68k-hp
+		os=-hpux
+		;;
+	hp3k9[0-9][0-9] | hp9[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k2[0-9][0-9] | hp9k31[0-9])
+		basic_machine=m68000-hp
+		;;
+	hp9k3[2-9][0-9])
+		basic_machine=m68k-hp
+		;;
+	hp9k6[0-9][0-9] | hp6[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k7[0-79][0-9] | hp7[0-79][0-9])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k78[0-9] | hp78[0-9])
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][13679] | hp8[0-9][13679])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][0-9] | hp8[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hppa-next)
+		os=-nextstep3
+		;;
+	hppaosf)
+		basic_machine=hppa1.1-hp
+		os=-osf
+		;;
+	hppro)
+		basic_machine=hppa1.1-hp
+		os=-proelf
+		;;
+	i370-ibm* | ibm*)
+		basic_machine=i370-ibm
+		;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+	i*86v32)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv32
+		;;
+	i*86v4*)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv4
+		;;
+	i*86v)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv
+		;;
+	i*86sol2)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-solaris2
+		;;
+	i386mach)
+		basic_machine=i386-mach
+		os=-mach
+		;;
+	i386-vsta | vsta)
+		basic_machine=i386-unknown
+		os=-vsta
+		;;
+	iris | iris4d)
+		basic_machine=mips-sgi
+		case $os in
+		    -irix*)
+			;;
+		    *)
+			os=-irix4
+			;;
+		esac
+		;;
+	isi68 | isi)
+		basic_machine=m68k-isi
+		os=-sysv
+		;;
+	m88k-omron*)
+		basic_machine=m88k-omron
+		;;
+	magnum | m3230)
+		basic_machine=mips-mips
+		os=-sysv
+		;;
+	merlin)
+		basic_machine=ns32k-utek
+		os=-sysv
+		;;
+	mingw32)
+		basic_machine=i386-pc
+		os=-mingw32
+		;;
+	miniframe)
+		basic_machine=m68000-convergent
+		;;
+	*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+	mipsel*-linux*)
+		basic_machine=mipsel-unknown
+		os=-linux-gnu
+		;;
+	mips*-linux*)
+		basic_machine=mips-unknown
+		os=-linux-gnu
+		;;
+	mips3*-*)
+		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+		;;
+	mips3*)
+		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+		;;
+	mmix*)
+		basic_machine=mmix-knuth
+		os=-mmixware
+		;;
+	monitor)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	msdos)
+		basic_machine=i386-pc
+		os=-msdos
+		;;
+	mvs)
+		basic_machine=i370-ibm
+		os=-mvs
+		;;
+	ncr3000)
+		basic_machine=i486-ncr
+		os=-sysv4
+		;;
+	netbsd386)
+		basic_machine=i386-unknown
+		os=-netbsd
+		;;
+	netwinder)
+		basic_machine=armv4l-rebel
+		os=-linux
+		;;
+	news | news700 | news800 | news900)
+		basic_machine=m68k-sony
+		os=-newsos
+		;;
+	news1000)
+		basic_machine=m68030-sony
+		os=-newsos
+		;;
+	news-3600 | risc-news)
+		basic_machine=mips-sony
+		os=-newsos
+		;;
+	necv70)
+		basic_machine=v70-nec
+		os=-sysv
+		;;
+	next | m*-next )
+		basic_machine=m68k-next
+		case $os in
+		    -nextstep* )
+			;;
+		    -ns2*)
+		      os=-nextstep2
+			;;
+		    *)
+		      os=-nextstep3
+			;;
+		esac
+		;;
+	nh3000)
+		basic_machine=m68k-harris
+		os=-cxux
+		;;
+	nh[45]000)
+		basic_machine=m88k-harris
+		os=-cxux
+		;;
+	nindy960)
+		basic_machine=i960-intel
+		os=-nindy
+		;;
+	mon960)
+		basic_machine=i960-intel
+		os=-mon960
+		;;
+	nonstopux)
+		basic_machine=mips-compaq
+		os=-nonstopux
+		;;
+	np1)
+		basic_machine=np1-gould
+		;;
+	nsr-tandem)
+		basic_machine=nsr-tandem
+		;;
+	op50n-* | op60c-*)
+		basic_machine=hppa1.1-oki
+		os=-proelf
+		;;
+	OSE68000 | ose68000)
+		basic_machine=m68000-ericsson
+		os=-ose
+		;;
+	os68k)
+		basic_machine=m68k-none
+		os=-os68k
+		;;
+	pa-hitachi)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	paragon)
+		basic_machine=i860-intel
+		os=-osf
+		;;
+	pbd)
+		basic_machine=sparc-tti
+		;;
+	pbb)
+		basic_machine=m68k-tti
+		;;
+        pc532 | pc532-*)
+		basic_machine=ns32k-pc532
+		;;
+	pentium | p5 | k5 | k6 | nexgen)
+		basic_machine=i586-pc
+		;;
+	pentiumpro | p6 | 6x86 | athlon)
+		basic_machine=i686-pc
+		;;
+	pentiumii | pentium2)
+		basic_machine=i686-pc
+		;;
+	pentium-* | p5-* | k5-* | k6-* | nexgen-*)
+		basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentiumpro-* | p6-* | 6x86-* | athlon-*)
+		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentiumii-* | pentium2-*)
+		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pn)
+		basic_machine=pn-gould
+		;;
+	power)	basic_machine=power-ibm
+		;;
+	ppc)	basic_machine=powerpc-unknown
+	        ;;
+	ppc-*)	basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppcle | powerpclittle | ppc-le | powerpc-little)
+		basic_machine=powerpcle-unknown
+	        ;;
+	ppcle-* | powerpclittle-*)
+		basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppc64)	basic_machine=powerpc64-unknown
+	        ;;
+	ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+		basic_machine=powerpc64le-unknown
+	        ;;
+	ppc64le-* | powerpc64little-*)
+		basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ps2)
+		basic_machine=i386-ibm
+		;;
+	pw32)
+		basic_machine=i586-unknown
+		os=-pw32
+		;;
+	rom68k)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	rm[46]00)
+		basic_machine=mips-siemens
+		;;
+	rtpc | rtpc-*)
+		basic_machine=romp-ibm
+		;;
+	sa29200)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	sequent)
+		basic_machine=i386-sequent
+		;;
+	sh)
+		basic_machine=sh-hitachi
+		os=-hms
+		;;
+	sparclite-wrs)
+		basic_machine=sparclite-wrs
+		os=-vxworks
+		;;
+	sps7)
+		basic_machine=m68k-bull
+		os=-sysv2
+		;;
+	spur)
+		basic_machine=spur-unknown
+		;;
+	st2000)
+		basic_machine=m68k-tandem
+		;;
+	stratus)
+		basic_machine=i860-stratus
+		os=-sysv4
+		;;
+	sun2)
+		basic_machine=m68000-sun
+		;;
+	sun2os3)
+		basic_machine=m68000-sun
+		os=-sunos3
+		;;
+	sun2os4)
+		basic_machine=m68000-sun
+		os=-sunos4
+		;;
+	sun3os3)
+		basic_machine=m68k-sun
+		os=-sunos3
+		;;
+	sun3os4)
+		basic_machine=m68k-sun
+		os=-sunos4
+		;;
+	sun4os3)
+		basic_machine=sparc-sun
+		os=-sunos3
+		;;
+	sun4os4)
+		basic_machine=sparc-sun
+		os=-sunos4
+		;;
+	sun4sol2)
+		basic_machine=sparc-sun
+		os=-solaris2
+		;;
+	sun3 | sun3-*)
+		basic_machine=m68k-sun
+		;;
+	sun4)
+		basic_machine=sparc-sun
+		;;
+	sun386 | sun386i | roadrunner)
+		basic_machine=i386-sun
+		;;
+	sv1)
+		basic_machine=sv1-cray
+		os=-unicos
+		;;
+	symmetry)
+		basic_machine=i386-sequent
+		os=-dynix
+		;;
+	t3e)
+		basic_machine=t3e-cray
+		os=-unicos
+		;;
+	tic54x | c54x*)
+		basic_machine=tic54x-unknown
+		os=-coff
+		;;
+	tx39)
+		basic_machine=mipstx39-unknown
+		;;
+	tx39el)
+		basic_machine=mipstx39el-unknown
+		;;
+	tower | tower-32)
+		basic_machine=m68k-ncr
+		;;
+	udi29k)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	ultra3)
+		basic_machine=a29k-nyu
+		os=-sym1
+		;;
+	v810 | necv810)
+		basic_machine=v810-nec
+		os=-none
+		;;
+	vaxv)
+		basic_machine=vax-dec
+		os=-sysv
+		;;
+	vms)
+		basic_machine=vax-dec
+		os=-vms
+		;;
+	vpp*|vx|vx-*)
+               basic_machine=f301-fujitsu
+               ;;
+	vxworks960)
+		basic_machine=i960-wrs
+		os=-vxworks
+		;;
+	vxworks68)
+		basic_machine=m68k-wrs
+		os=-vxworks
+		;;
+	vxworks29k)
+		basic_machine=a29k-wrs
+		os=-vxworks
+		;;
+	w65*)
+		basic_machine=w65-wdc
+		os=-none
+		;;
+	w89k-*)
+		basic_machine=hppa1.1-winbond
+		os=-proelf
+		;;
+	windows32)
+		basic_machine=i386-pc
+		os=-windows32-msvcrt
+		;;
+	xmp)
+		basic_machine=xmp-cray
+		os=-unicos
+		;;
+        xps | xps100)
+		basic_machine=xps100-honeywell
+		;;
+	z8k-*-coff)
+		basic_machine=z8k-unknown
+		os=-sim
+		;;
+	none)
+		basic_machine=none-none
+		os=-none
+		;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+	w89k)
+		basic_machine=hppa1.1-winbond
+		;;
+	op50n)
+		basic_machine=hppa1.1-oki
+		;;
+	op60c)
+		basic_machine=hppa1.1-oki
+		;;
+	mips)
+		if [ x$os = x-linux-gnu ]; then
+			basic_machine=mips-unknown
+		else
+			basic_machine=mips-mips
+		fi
+		;;
+	romp)
+		basic_machine=romp-ibm
+		;;
+	rs6000)
+		basic_machine=rs6000-ibm
+		;;
+	vax)
+		basic_machine=vax-dec
+		;;
+	pdp10)
+		# there are many clones, so DEC is not a safe bet
+		basic_machine=pdp10-unknown
+		;;
+	pdp11)
+		basic_machine=pdp11-dec
+		;;
+	we32k)
+		basic_machine=we32k-att
+		;;
+	sh3 | sh4 | sh3eb | sh4eb)
+		basic_machine=sh-unknown
+		;;
+	sparc | sparcv9 | sparcv9b)
+		basic_machine=sparc-sun
+		;;
+        cydra)
+		basic_machine=cydra-cydrome
+		;;
+	orion)
+		basic_machine=orion-highlevel
+		;;
+	orion105)
+		basic_machine=clipper-highlevel
+		;;
+	mac | mpw | mac-mpw)
+		basic_machine=m68k-apple
+		;;
+	pmac | pmac-mpw)
+		basic_machine=powerpc-apple
+		;;
+	c4x*)
+		basic_machine=c4x-none
+		os=-coff
+		;;
+	*-unknown)
+		# Make sure to match an already-canonicalized machine name.
+		;;
+	*)
+		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+		exit 1
+		;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+	*-digital*)
+		basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+		;;
+	*-commodore*)
+		basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+		;;
+	*)
+		;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+        # First match some system type aliases
+        # that might get confused with valid system types.
+	# -solaris* is a basic system type, with this one exception.
+	-solaris1 | -solaris1.*)
+		os=`echo $os | sed -e 's|solaris1|sunos4|'`
+		;;
+	-solaris)
+		os=-solaris2
+		;;
+	-svr4*)
+		os=-sysv4
+		;;
+	-unixware*)
+		os=-sysv4.2uw
+		;;
+	-gnu/linux*)
+		os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+		;;
+	# First accept the basic system types.
+	# The portable systems comes first.
+	# Each alternative MUST END IN A *, to match a version number.
+	# -sysv* is not here because it comes later, after sysvr4.
+	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+	      | -aos* \
+	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+	      | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+	      | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+	      | -chorusos* | -chorusrdb* \
+	      | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+	      | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+	      | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \
+	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+	      | -os2* | -vos*)
+	# Remember, each alternative MUST END IN *, to match a version number.
+		;;
+	-qnx*)
+		case $basic_machine in
+		    x86-* | i*86-*)
+			;;
+		    *)
+			os=-nto$os
+			;;
+		esac
+		;;
+	-nto*)
+		os=-nto-qnx
+		;;
+	-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+	      | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+	      | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+		;;
+	-mac*)
+		os=`echo $os | sed -e 's|mac|macos|'`
+		;;
+	-linux*)
+		os=`echo $os | sed -e 's|linux|linux-gnu|'`
+		;;
+	-sunos5*)
+		os=`echo $os | sed -e 's|sunos5|solaris2|'`
+		;;
+	-sunos6*)
+		os=`echo $os | sed -e 's|sunos6|solaris3|'`
+		;;
+	-opened*)
+		os=-openedition
+		;;
+	-wince*)
+		os=-wince
+		;;
+	-osfrose*)
+		os=-osfrose
+		;;
+	-osf*)
+		os=-osf
+		;;
+	-utek*)
+		os=-bsd
+		;;
+	-dynix*)
+		os=-bsd
+		;;
+	-acis*)
+		os=-aos
+		;;
+	-386bsd)
+		os=-bsd
+		;;
+	-ctix* | -uts*)
+		os=-sysv
+		;;
+	-ns2 )
+	        os=-nextstep2
+		;;
+	-nsk*)
+		os=-nsk
+		;;
+	# Preserve the version number of sinix5.
+	-sinix5.*)
+		os=`echo $os | sed -e 's|sinix|sysv|'`
+		;;
+	-sinix*)
+		os=-sysv4
+		;;
+	-triton*)
+		os=-sysv3
+		;;
+	-oss*)
+		os=-sysv3
+		;;
+	-svr4)
+		os=-sysv4
+		;;
+	-svr3)
+		os=-sysv3
+		;;
+	-sysvr4)
+		os=-sysv4
+		;;
+	# This must come after -sysvr4.
+	-sysv*)
+		;;
+	-ose*)
+		os=-ose
+		;;
+	-es1800*)
+		os=-ose
+		;;
+	-xenix)
+		os=-xenix
+		;;
+        -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+	        os=-mint
+		;;
+	-none)
+		;;
+	*)
+		# Get rid of the `-' at the beginning of $os.
+		os=`echo $os | sed 's/[^-]*-//'`
+		echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+		exit 1
+		;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+	*-acorn)
+		os=-riscix1.2
+		;;
+	arm*-rebel)
+		os=-linux
+		;;
+	arm*-semi)
+		os=-aout
+		;;
+	pdp10-*)
+		os=-tops20
+		;;
+        pdp11-*)
+		os=-none
+		;;
+	*-dec | vax-*)
+		os=-ultrix4.2
+		;;
+	m68*-apollo)
+		os=-domain
+		;;
+	i386-sun)
+		os=-sunos4.0.2
+		;;
+	m68000-sun)
+		os=-sunos3
+		# This also exists in the configure program, but was not the
+		# default.
+		# os=-sunos4
+		;;
+	m68*-cisco)
+		os=-aout
+		;;
+	mips*-cisco)
+		os=-elf
+		;;
+	mips*-*)
+		os=-elf
+		;;
+	*-tti)	# must be before sparc entry or we get the wrong os.
+		os=-sysv3
+		;;
+	sparc-* | *-sun)
+		os=-sunos4.1.1
+		;;
+	*-be)
+		os=-beos
+		;;
+	*-ibm)
+		os=-aix
+		;;
+	*-wec)
+		os=-proelf
+		;;
+	*-winbond)
+		os=-proelf
+		;;
+	*-oki)
+		os=-proelf
+		;;
+	*-hp)
+		os=-hpux
+		;;
+	*-hitachi)
+		os=-hiux
+		;;
+	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+		os=-sysv
+		;;
+	*-cbm)
+		os=-amigaos
+		;;
+	*-dg)
+		os=-dgux
+		;;
+	*-dolphin)
+		os=-sysv3
+		;;
+	m68k-ccur)
+		os=-rtu
+		;;
+	m88k-omron*)
+		os=-luna
+		;;
+	*-next )
+		os=-nextstep
+		;;
+	*-sequent)
+		os=-ptx
+		;;
+	*-crds)
+		os=-unos
+		;;
+	*-ns)
+		os=-genix
+		;;
+	i370-*)
+		os=-mvs
+		;;
+	*-next)
+		os=-nextstep3
+		;;
+        *-gould)
+		os=-sysv
+		;;
+        *-highlevel)
+		os=-bsd
+		;;
+	*-encore)
+		os=-bsd
+		;;
+        *-sgi)
+		os=-irix
+		;;
+        *-siemens)
+		os=-sysv4
+		;;
+	*-masscomp)
+		os=-rtu
+		;;
+	f30[01]-fujitsu | f700-fujitsu)
+		os=-uxpv
+		;;
+	*-rom68k)
+		os=-coff
+		;;
+	*-*bug)
+		os=-coff
+		;;
+	*-apple)
+		os=-macos
+		;;
+	*-atari*)
+		os=-mint
+		;;
+	*)
+		os=-none
+		;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+	*-unknown)
+		case $os in
+			-riscix*)
+				vendor=acorn
+				;;
+			-sunos*)
+				vendor=sun
+				;;
+			-aix*)
+				vendor=ibm
+				;;
+			-beos*)
+				vendor=be
+				;;
+			-hpux*)
+				vendor=hp
+				;;
+			-mpeix*)
+				vendor=hp
+				;;
+			-hiux*)
+				vendor=hitachi
+				;;
+			-unos*)
+				vendor=crds
+				;;
+			-dgux*)
+				vendor=dg
+				;;
+			-luna*)
+				vendor=omron
+				;;
+			-genix*)
+				vendor=ns
+				;;
+			-mvs* | -opened*)
+				vendor=ibm
+				;;
+			-ptx*)
+				vendor=sequent
+				;;
+			-vxsim* | -vxworks*)
+				vendor=wrs
+				;;
+			-aux*)
+				vendor=apple
+				;;
+			-hms*)
+				vendor=hitachi
+				;;
+			-mpw* | -macos*)
+				vendor=apple
+				;;
+			-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+				vendor=atari
+				;;
+			-vos*)
+				vendor=stratus
+				;;
+		esac
+		basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+		;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/connectors/webapp/support/formatfile.c b/connectors/webapp/support/formatfile.c
new file mode 100644
index 0000000..509d6a1
--- /dev/null
+++ b/connectors/webapp/support/formatfile.c
@@ -0,0 +1,58 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/* ------------------------------------------------------------------------- *
+ * Author Pier Fumagalli <pier@betaversion.org>
+ * Version $Id$
+ * ------------------------------------------------------------------------- */
+
+#include <stdio.h>
+
+int main(void) {
+    char buf[1024];
+    char *ret=NULL;
+    int x=0;
+    int y=0;
+    int k=0;
+    int s=0;
+    int l=0;
+
+    while ((ret=fgets(buf,1024,stdin))!=NULL) {
+        for (x=strlen(ret); x>=0; x--) {
+            if((ret[x]=='\0')||(ret[x]=='\t')||(ret[x]=='\n')||(ret[x]==' '))
+                ret[x]='\0';
+            else break;
+        }
+
+        k=0;
+        for (x=0; x<strlen(ret); x++) {
+            if (ret[x]=='\t') {
+                fprintf(stdout," ");
+                k++;
+                s=((((k/4)+1)*4)-k);
+                if (s==4) s=0;
+                for (y=0; y<s; y++) fprintf(stdout," ");
+                k+=s;
+            } else {
+                fprintf(stdout,"%c",ret[x]);
+                k++;
+            }
+        }
+        fprintf(stdout,"\n");
+        l++;
+        if (k>=80) fprintf(stderr,"line %4d (%4d) [%s]\n",l,k,ret);
+    }
+}
diff --git a/connectors/webapp/support/install.sh b/connectors/webapp/support/install.sh
new file mode 100755
index 0000000..9a8821f
--- /dev/null
+++ b/connectors/webapp/support/install.sh
@@ -0,0 +1,112 @@
+#!/bin/sh
+##
+##  install.sh -- install a program, script or datafile
+##
+##  Based on `install-sh' from the X Consortium's X11R5 distribution
+##  as of 89/12/18 which is freely available.
+##  Cleaned up for Apache's Autoconf-style Interface (APACI)
+##  by Ralf S. Engelschall <rse@apache.org>
+##
+#
+# This script falls under the Apache License.
+# See http://www.apache.org/docs/LICENSE
+
+
+#
+#   put in absolute paths if you don't have them in your path; 
+#   or use env. vars.
+#
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+
+#
+#   parse argument line
+#
+instcmd="$mvprog"
+chmodcmd=""
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+ext=""
+src=""
+dst=""
+while [ "x$1" != "x" ]; do
+    case $1 in
+        -c) instcmd="$cpprog"
+            shift; continue
+            ;;
+        -m) chmodcmd="$chmodprog $2"
+            shift; shift; continue
+            ;;
+        -o) chowncmd="$chownprog $2"
+            shift; shift; continue
+            ;;
+        -g) chgrpcmd="$chgrpprog $2"
+            shift; shift; continue
+            ;;
+        -s) stripcmd="$stripprog"
+            shift; continue
+            ;;
+        -S) stripcmd="$stripprog $2"
+            shift; shift; continue
+            ;;
+        -e) ext="$2"
+            shift; shift; continue
+            ;;
+        *)  if [ "x$src" = "x" ]; then
+                src=$1
+            else
+                dst=$1
+            fi
+            shift; continue
+            ;;
+    esac
+done
+if [ "x$src" = "x" ]; then
+     echo "install.sh: no input file specified"
+     exit 1
+fi
+if [ "x$dst" = "x" ]; then
+     echo "install.sh: no destination specified"
+     exit 1
+fi
+
+#
+#  If destination is a directory, append the input filename; if
+#  your system does not like double slashes in filenames, you may
+#  need to add some logic
+#
+if [ -d $dst ]; then
+    dst="$dst/`basename $src`"
+fi
+
+#  Add a possible extension (such as ".exe") to src and dst
+src="$src$ext"
+dst="$dst$ext"
+
+#  Make a temp file name in the proper directory.
+dstdir=`dirname $dst`
+dsttmp=$dstdir/#inst.$$#
+
+#  Move or copy the file name to the temp name
+$instcmd $src $dsttmp
+
+#  And set any options; do chmod last to preserve setuid bits
+if [ "x$chowncmd" != "x" ]; then $chowncmd $dsttmp; fi
+if [ "x$chgrpcmd" != "x" ]; then $chgrpcmd $dsttmp; fi
+if [ "x$stripcmd" != "x" ]; then $stripcmd $dsttmp; fi
+if [ "x$chmodcmd" != "x" ]; then $chmodcmd $dsttmp; fi
+
+#  Now rename the file to the real destination.
+$rmcmd $dst
+$mvcmd $dsttmp $dst
+
+exit 0
+
diff --git a/connectors/webapp/support/nightly.sh b/connectors/webapp/support/nightly.sh
new file mode 100755
index 0000000..1ec2547
--- /dev/null
+++ b/connectors/webapp/support/nightly.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+
+DATE=`date '+%Y%m%d'`
+BASE_DIR=/home/pier/WebApp
+PREV_DIR=$BASE_DIR/webapp-module-00000000
+CURR_REL=webapp-module-$DATE
+CURR_DIR=$BASE_DIR/$CURR_REL
+TEMP_DIR=$BASE_DIR/temp
+
+TAR_FILE=$BASE_DIR/webapp-module-$DATE.tar
+TGZ_FILE=$BASE_DIR/webapp-module-$DATE.tar.gz
+ZIP_FILE=$BASE_DIR/webapp-module-$DATE.zip
+
+
+echo "### Started at `date`"
+echo "### Updating CVS tree"
+cd $PREV_DIR
+cvs update -APd 2> /dev/null | tee $BASE_DIR/log.cvs
+cd $BASE_DIR
+TEMP=`cat $BASE_DIR/log.cvs`
+if test -z "$TEMP" ; then
+  echo "> No updates in CVS"
+  if test "$1" != "force" ; then
+    echo "> Exiting"
+    rm -f $BASE_DIR/log.cvs
+    exit 0
+  else
+    echo "> Forced rebuild"
+  fi
+fi
+rm -f $BASE_DIR/log.cvs
+
+
+echo ""
+echo "### Copying tree to new directory"
+cp -R $PREV_DIR $CURR_DIR
+
+
+echo ""
+echo "### Running buildconf script"
+cd $CURR_DIR
+./support/buildconf.sh 2>&1 | sed 's/^/> /g'
+cd $BASE_DIR
+
+
+echo ""
+echo "### Running configure"
+mkdir $TEMP_DIR
+cd $TEMP_DIR
+$CURR_DIR/configure \
+  --with-apxs=/opt/apache2/bin/apxs \
+  --with-ant=$BASE_DIR/ant/ant.sh \
+  --with-perl=/usr/bin/perl \
+  --enable-java=/opt/tomcat \
+  --enable-apidoc-c \
+  --enable-apidoc-java \
+  --enable-docs \
+    | sed 's/^/> /g'
+cd $BASE_DIR
+
+
+echo ""
+echo "### Building portable components"
+cd $TEMP_DIR
+make capi-build ant-build 2>&1 | sed 's/^/> /g'
+cd $BASE_DIR
+
+
+echo ""
+echo "### Copying portable components"
+set -x
+mv $TEMP_DIR/build/docs $CURR_DIR/documentation
+mv $TEMP_DIR/build/tomcat-warp.jar $CURR_DIR/tomcat-warp.jar
+set +x
+
+
+echo ""
+echo "### Rolling distribution files"
+set -x
+cd $BASE_DIR
+/usr/bin/tar -cf $TAR_FILE $CURR_REL
+gzip -9c $TAR_FILE > $TGZ_FILE
+rm -f $TAR_FILE
+zip -rpq9 $ZIP_FILE $CURR_REL
+set +x
diff --git a/connectors/webapp/support/scandoc.pl b/connectors/webapp/support/scandoc.pl
new file mode 100755
index 0000000..85b70a7
--- /dev/null
+++ b/connectors/webapp/support/scandoc.pl
@@ -0,0 +1,1342 @@
+#!/usr/bin/perl
+#
+# ScanDoc - Version 0.14,  A C/C++ Embedded Documentation Analyser
+# ----------------------------------------------------------------
+#
+# Distributed under the "Artistic License".  See the file 
+# "COPYING" that accompanies the ScanDoc distribution.
+#
+# See http://scandoc.sourceforge.net/ for more information and
+# complete documentation.
+#
+# (c) 1997 - 2000 Talin and others.
+
+require "ctime.pl";
+require "getopts.pl";
+
+# 1 = on (verbose); 0 = off 
+$debug = 0;
+
+# Get the current date
+$date = &ctime(time);
+
+# Set the default tab size
+$tabSize = 4;
+
+$minorVersion = 14;
+$majorVersion = 0;
+$scandocURL   = "http://scandoc.sourceforge.net/";
+
+# Set up default templates
+&Getopts( 'i:d:p:t:' );
+
+if ($#ARGV < 0) {
+  die "Usage: -i <doc-template> -p <output-path> -t<tabsize> -d<sym>=<value> [ <input-files> ... ]\n";
+}
+
+# Read the template
+if (!defined $opt_i) {
+  $opt_i = "default.pl";
+}
+&readTemplate( $opt_i );
+
+# Set the destination path.
+$destPath = "";
+$destPath = $opt_p if (defined($opt_p));
+
+# Set the tab size.
+$tabSize = $opt_t if (defined($opt_t));
+
+# Handle defines
+if ($opt_d) {
+  foreach $def (split( /,/, $opt_d )) {
+    if ($def =~ /\s*(\w*)\=(.*)/) {
+      $${1} = $2;
+    }
+    else {
+      $${1} = 1;
+    }
+  }
+}
+
+# For each input filename, parse it
+while ($srcfile = shift(@ARGV)) {
+
+  $linenumber = 0;
+  open( FILE, $srcfile ) || die "Can't open file $srcfile\n";
+  print STDERR "Reading \"$srcfile\"\n";
+  
+  $docTag = 'description';
+  $docEmpty = 1;
+  $packageName = '.general';
+  $author = '';
+  $version = '';
+  $class = 0;
+  $_ = '';
+  
+  while (&parseDeclaration( '' )) {}
+}
+
+# Collate subclasses and associate with class record.
+foreach $className (keys %subclasses) {
+  my $class = &classRecord( $className );
+  
+  if ($class) {
+    my @subs = ();
+    # print STDERR "$className ", join( ',', @{$subclasses{ $className }} ), "\n";
+    foreach $subName ($subclasses{ $className }) {
+      if (&classRecord( $subName )) {
+	push @subs, $subName;
+      }
+      $class->{ 'subs' } = @subs;
+    }
+  }
+}
+
+# Turn packages into objects. Special case for "default" package.
+foreach $pkg (keys %packages)
+{
+  # print STDERR $pkg, "\n";
+  bless $packages{ $pkg }, PackageRecord;
+  if ($pkg eq '.general') {
+    $packages{ $pkg }{ 'name' } = "General";
+  }
+  else {
+    $packages{ $pkg }{ 'name' } = $pkg;
+  }
+  # print STDERR $packages{ $pkg }->Classes(), "\n";
+}
+
+# Execute template file
+# print STDERR $docTemplate; # For debugging
+eval $docTemplate;
+print STDERR $@;
+
+exit;
+
+# ======================= Subroutines ================================
+
+# Read a line of input, and remove blank lines and preprocessor directives.
+sub rdln {
+  my ($skip_next_line) = 0;
+  if (defined ($_)) {
+    my ($previous_line) = $_;
+    while ( (/^(\s*|\#.*)$/ || $skip_next_line ) && ($_ = <FILE>)) {
+      if ($previous_line =~ m/\\\s*/) { $skip_next_line = 1; }
+      else { $skip_next_line = 0; }
+      $previous_line = $_;
+      $linenumber++; 
+      if ($debug) { print STDERR "(0:$srcfile) $linenumber.\n"; } 
+    }
+  }
+}
+
+# Don't skip "#"
+sub rdln2 {
+  if (defined ($_)) {
+    while (/^(\s*)$/ && ($_ = <FILE>)) {$linenumber++; if ($debug) { print STDERR "(0:$srcfile) $linenumber.\n"; } }
+  }
+}
+
+# Remove comments from current line
+sub removeComment {
+  s|//.*||;
+}
+
+# parsing functions
+sub matchKW		{ &rdln; return (s/^\s*($_[0])//, $1) if defined ($_); return (0, 0); }
+#sub matchStruct		{ &rdln; return (s/^\s*(struct|class)//, $1) if defined ($_); return (0, 0); }
+#sub matchPermission	{ &rdln; return (s/^\s*(public|private|protected)// && $1) if defined ($_); return (0,0); }
+sub matchID		{ &rdln; return (s/^\s*([A-Za-z_]\w*)//, $1) if defined ($_); return (0,0); }
+sub matchColon		{ &rdln; return (s/^\s*\://) if defined ($_); return 0; }
+sub matchComma		{ &rdln; return (s/^\s*\,//) if defined ($_); return 0; }
+sub matchSemi		{ &rdln; return (s/^\s*\;//) if defined ($_); return 0; }
+sub matchRBracket	{ &rdln; return (s/^\s*\{//) if defined ($_); return 0; }
+sub matchLBracket	{ &rdln; return (s/^\s*\}//) if defined ($_); return 0; }
+sub matchRParen		{ &rdln; return (s/^\s*\(//) if defined ($_); return 0; }
+sub matchLParen		{ &rdln; return (s/^\s*\)//) if defined ($_); return 0; }
+sub matchRAngle		{ &rdln; return (s/^\s*\<//) if defined ($_); return 0; }
+sub matchLAngle		{ &rdln; return (s/^\s*\>//) if defined ($_); return 0; }
+sub matchDecl           { &rdln; return (s/^(\s*[\s\w\*\[\]\~\&\n\:]+)//, $1) if defined ($_); return (0, 0); }
+sub matchOper		{ &rdln; return (s/^\s*([\~\&\^\>\<\=\!\%\*\+\-\/\|\w]*)// && $1) if defined ($_); return 0; }
+sub matchFuncOper	{ &rdln; return (s/^\s*(\(\))// && $1) if defined ($_); return 0; }
+sub matchAny		{ &rdln; return (s/^\s*(\S+)//, $1) if defined ($_); return (0, 0); }
+sub matchChar		{ &rdln; return (s/^(.)//, $1) if defined ($_); return (0, 0); }
+sub matchChar2	        { &rdln2; return (s/^(.)//, $1) if defined ($_); return (0, 0); }
+sub matchString 	{ &rdln; return (s/^\"(([^\\\"]|(\\.)))*\"//, $1) if defined ($_); return (0, 0); }
+
+# Skip to next semicolon
+sub skipToSemi {
+  
+  while (!&matchSemi) {
+    
+    &rdln;
+    s|//.*||;			# Eat comments
+      if (&matchLBracket) {
+	&skipBody;
+	next;
+      }
+    last if !s/^\s*([^\s\{\;]+)//;
+    # print STDERR "$1 ";
+  }
+}
+
+# Skip function body
+sub skipBody {
+  local( $nest );
+  
+  $nest = 1;
+  
+  for (;;) {
+    if (&matchRBracket) { $nest++; }
+    elsif (&matchLBracket) {
+      $nest--;
+      last if !$nest;
+    }
+    else { 
+      last if ((($valid,) = &matchKW( "[^\{\}]")) && !$valid);
+    }
+  }
+}
+
+# Skip a string. (multiline)
+sub skipString {
+  local( $char, $lastchar);
+  $lastchar = "\"";
+  
+  for (;;) {
+    ($valid, $char) = &matchChar2;
+    if (($char eq "\"") && ($lastchar ne "\\")) { last; }
+    if ($lastchar eq "\\") { $lastchar = " "; }
+    else { $lastchar = $char; }
+  }
+}
+
+
+# Skip everything in parenthesis.
+sub skipParenBody {
+  local( $nest );
+  
+  $nest = 1;
+  
+  for (;;) {
+    if (&matchRParen) { $nest++; }
+    elsif (&matchLParen) {
+      $nest--;
+      last if !$nest;
+    }
+    else { 
+      last if ((($valid,) = &matchKW( "[^\(\)]")) && !$valid);
+    }
+  }
+}
+
+# Parse (*name) syntax
+sub parseParenPointer {
+
+  $parenPointerFunction = "";
+
+  if (s/^(\s*\(\s*\*)//) {
+    $decl .= $1;
+    $nest = 1;
+    
+    for (;;) {
+      # Preserve spaces, eliminate in-line comments
+      &removeComment;
+      while (s/^(\s+)//) { $decl .= $1; &rdln; }
+      
+      if (&matchRParen) { $nest++; $decl .= "("; }
+      elsif (&matchLParen) {
+	$decl .= ")";
+	$nest--;
+	last if !$nest;
+      }
+      elsif ((($valid, $d) = &matchKW( "[^\(\)]*")) && $valid) { $decl .= $d; }
+      else { last; }
+    }
+    
+    # Just in case there are array braces afterwards.
+    while ((($valid, $d) = &matchDecl) && $valid) { $decl .= $d; }
+    $parenPointerFunction = $decl;
+    $parenPointerFunction =~ s/^\s+//;	# Remove whitespace from beginning
+    $parenPointerFunction =~ s/\s+$//;	# Remove whitespace from end
+  }
+}
+
+# Parse template arguments
+sub matchAngleArgs {
+  
+  if (&matchRAngle) {
+    local ($args, $nest);
+    
+    $args = "&lt;";
+    $nest = 1;
+    
+    for (;;) {
+      if (&matchRAngle) { $nest++; $args .= "&lt;"; }
+      elsif (&matchLAngle) {
+	$nest--;
+	$args .= "&gt;";
+	last if !$nest;
+      }
+      elsif ((($valid, $d) = &matchChar) && $valid) { $args .= $d; }
+      else { last; }
+    }
+    return $args;
+  }
+  else { return ''; }
+}
+
+# convert tabs to spaces
+sub expandTabs {
+  local	($text) = @_;
+  local 	($n);
+  
+  while (($n = index($text,"\t")) >= 0) {
+    substr($text, $n, 1) = " " x ($tabSize-($n % $tabSize));
+  }
+  
+  return $text;
+}
+
+# Process a line of text from a "special" comment
+sub handleCommentLine {
+  local ($_) = @_;
+  
+  if ($docEmpty) {
+    # Eliminate blank lines at the head of the doc.
+    return if (/^\s*$/);
+  }
+  
+  # First, expand tabs.
+  $_ = &expandTabs( $_ );
+	
+  # Remove gratuitous \s*\s  (james)
+  s/(^|\n)\s*\*\s/$1/g;
+  
+  # If it's one of the standard tags
+  if (s/^\s*\@(see|package|version|author|param|return|result|exception|keywords|deffunc|defvar|heading|todo)\s*//) {
+    my $tag = $1;
+    $tag = 'return' if ($tag eq 'result');
+    
+    # for param and exception, split the param name and the text
+    # seperate them with tabs.
+    if ($tag eq "param" || $tag eq "exception") {
+      s/^\s*(\w+)\s*(.*)/\t$1\t$2/;
+    }
+    elsif ($tag eq "heading") {
+      # 'heading' is processed by the template, if at all.
+      $_ = "\@heading\t$_";
+      $tag = "description";
+    }
+    elsif ($tag eq 'todo') {
+      if ($todolist{ $srcfile } ne '') {
+	$todolist{ $srcfile } .= "\n";
+      }
+    }
+    
+    # If it's @deffunc or @defvar
+    if ($tag =~ /def(.*)/) {
+      
+      $type = $1;
+      
+      # @deffunc and @defvar force a comment to be written out as if there was a
+      # declaration.
+      # Designed for use with macros and other constructs I can't parse.
+      
+      if (/(\S+)\s+(.*)$/) {
+	$name = $1;
+	$decl = $2;
+	$dbname = &uniqueName( "$baseScope$name" );
+	
+	my $entry = { 'type'    => $type,
+		      'name'    => $name,
+		      'longname'=> $name,
+		      'fullname'=> "$name$decl",
+		      'scopename'=>"$baseScope$name",
+		      'uname'   => $dbname,
+		      'decl'    => $decl,
+		      'package' => $packageName };
+
+        bless $entry, MemberRecord;
+
+	if ($class) {
+	  $entry->{ 'class' } = "$context";
+	  $class->{ 'members' }{ $dbname } = $entry;
+	} 
+	else {
+	  $packages{ $packageName }{ 'globals' }{ $dbname } = $entry;
+	}
+	$docTag = 'description';
+	&dumpComments( $entry );
+	return;
+      }
+    }
+    elsif ($tag eq 'package') {
+      s/^\s*//;
+      s/\s*$//;
+      $packageName = $_;
+      $docTag = 'description';
+      return;
+    }
+    elsif ($tag eq 'author') {
+      $author = $_;
+      $docTag = 'description';
+      return;
+    }
+    elsif ($tag eq 'version') {
+      $version = $_;
+      $docTag = 'description';
+      return;
+    }
+    
+    $docTag = $tag;
+  }
+  elsif (/^\s*@\w+/) {
+    # any other line that begins with an @ should be inserted into the main
+    # description for later expansion.
+    $docTag = 'description';
+  }
+  
+  # "To-do" lists are handled specially, and not associated with a class.
+  if ($docTag eq 'todo') {
+    $todolist{ $srcfile } .= $_;
+    return;
+  }
+  
+  # Append to current doc tag, regardless of whether it's a new line
+  # or a continuation. Also mark this doc as non-empty.
+  $docTags{ $docTag } .= $_;
+  $docEmpty = 0;
+  
+  # @see doesn't persist.
+  if ($docTag eq 'see') { $docTag = 'description'; }
+  
+  # print STDERR ":$_";
+}
+
+# Clear doc tag information at end of class or file
+sub clearComments {
+  
+  $docTag = 'description';
+  $docEmpty = 1;
+  %docTags = ();
+}
+
+# Add doc tag information to current documented item
+sub dumpComments {
+  local ($hashref) = @_;
+  
+  if ($docEmpty == 0) {
+    
+    if ($author ne  '') { $hashref->{ 'author'  } = $author;  }
+    if ($version ne '') { $hashref->{ 'version' } = $version; }
+    $hashref->{ 'sourcefile' } = $srcfile;
+    
+    # Store the tags for this documentation into the global doc symbol table
+    foreach $key (keys %docTags) {
+      my $data = $docTags{ $key };
+
+      $data =~ s/\s*$//;
+      
+      $hashref->{ $key } = $data;
+    }
+  }
+  
+  &clearComments();
+}
+
+# Generate a unique name from the given name.
+sub uniqueName {
+  local ($name) = @_;
+  
+  # Duplicate doc entries need to be distinguished, so give them a different label.
+  while ($docs{ $name }) {
+    if ($name =~ /-(\d+)$/) {
+      $name = $` . "-" . ($1 + 1);
+    }
+    else { $name .= "-2"; }
+  }
+  
+  $docs{ $name } = 1;
+  return $name;
+}
+
+# Get the current class record.
+sub classRecord {
+  local ($className) = @_;
+  local ($pkg) = $classToPackage{ $className };
+  
+  if ($pkg) {
+    return $packages{ $pkg }{ 'classes' }{ $className };
+  }
+  return 0;
+}
+
+# Parse a declaration in the file
+sub parseDeclaration {
+  
+  local ($context) = @_;
+  local ($baseScope) = '';
+  local ($decl);
+  my ($token);
+  
+  if ($context) { $baseScope = $context . "::"; }
+  
+  &rdln;
+
+  if (!defined ($_)) { return 0; }
+  
+  if (s|^\s*//\*\s+||) {
+    # Special C++ comment
+    &handleCommentLine( $' );
+    $_ = ''; &rdln;
+  }	
+  elsif (s|^\s*//||) { 
+    # Ordinary C++ comment
+    $_ = '';
+    &rdln;
+  }
+  elsif (s|^\s*\/\*\*\s+||) {
+    # Special C comments
+    
+    s/\={3,}|\-{3,}|\*{3,}//;			# Eliminate banner strips
+    $text = '';
+    $docTag = 'description';
+    
+    # Special comment
+    while (!/\*\//) { &handleCommentLine( $_ ); $text .= $_; $_ = <FILE>; $linenumber++; if ($debug) { print STDERR "(1) $linenumber\n."; }}
+    s/\={3,}|\-{3,}|\*{3,}//;			# Eliminate banner strips
+    /\*\//;
+    &handleCommentLine( $` );
+    $text.= $`; $_ = $';
+  }
+  elsif (s|^\s*\/\*||) {
+    # Ordinary C comment
+    $text = "";
+    
+    while (!/\*\//) { $text .= $_; $_ = <FILE>; $linenumber++; if ($debug) { print STDERR "(2) $linenumber\n."; }}
+    /\*\//;
+    $text.= $`; $_ = $';
+  }
+  elsif ((($valid, $tag) = &matchKW( "template")) && $valid) {
+    # Template definition
+    $args = &matchAngleArgs;
+    &rdln;
+    
+    ##$tmplParams = $args; JAMES
+    $result = &parseDeclaration( $context );
+    ##$tmplParams = ''; JAMES
+    return $result;
+  }
+  elsif ((($valid, $tag) = &matchKW("class|struct")) && $valid) {
+    # Class or structure definition
+    local ($className,$class);
+    
+    if ((($valid, $className) = &matchID) && $valid) {
+      
+      return 1 if (&matchSemi);		# Only a struct tag
+      
+      # A class instance
+      if ((($valid,)=&matchID) && $valid) {
+	&matchSemi;
+	return 1;
+      }
+      
+      my $fullName = "$baseScope$className"; ##$tmplParams"; JAMES
+      # print STDERR "CLASS $fullName\n";
+      
+      my @bases = ();
+      
+      if (&matchColon) {
+	
+	for (;;) {
+	  my $p;
+	  &matchKW( "virtual" );
+	  $perm = "private";
+	  if ((($valid, $p) = &matchKW( "public|private|protected" )) && $valid) { $perm = $p; }
+	  &matchKW( "virtual" );
+	  
+	  last if !(  (($valid, $base) = &matchID) && $valid  );
+	  
+	  push @bases, $base;
+	  push @{ $subclasses{ $base } }, $fullName;
+	  # print STDERR " : $perm $base\n";
+	  last if !&matchComma;
+	}
+      }
+      
+      #	print STDERR "\n";
+      # print STDERR "parsing class $fullName\n";
+
+      if ($docEmpty == 0) {
+	$class = { 'type'    => $tag,
+		   'name'    => $fullName,
+		   'longname'=> "$tag $className",
+		   'fullname'=> "$tag $className",
+		   'scopename'=> "$tag $fullName",
+		   'uname'   => $fullName,
+		   'bases'   => \@bases,
+		   'package' => $packageName,
+		   'members' => {} };
+	
+	# print STDERR "$className: @bases\n";
+	
+	bless $class, ClassRecord;
+	
+	print STDERR "   parsing class $fullName\n";
+	# $classToPackage{ $className } = $packageName;
+	$classToPackage{ $fullName } = $packageName;
+	# $classList{ $className } = $class;
+	$classList{ $fullName } = $class;
+	$packages{ $packageName }{ 'classes' }{ $fullName } = $class;
+	&dumpComments( $class );
+      }
+      
+      if (&matchRBracket) {
+	local ($perm) = ("private");
+	
+	while (!&matchLBracket) {
+	  my $p;
+	  if ((($valid, $p) = &matchKW( "public\:|private\:|protected\:" )) && $valid) {
+	    $perm = $p;
+	  }
+	  else {
+	    &parseDeclaration( $fullName )
+	      || die "Unmatched brace! line = $linenumber\n";
+	  }
+	}
+	
+	&matchSemi;
+      }
+      
+      &clearComments;
+    }
+  }
+  elsif ( ((($valid,)=&matchKW( "enum")) && $valid) || ((($valid,)=&matchKW( "typedef" )) && $valid)) {
+    &skipToSemi;
+  }
+  elsif ((($valid,)=&matchKW( "friend\s*class" )) && $valid) {
+    &skipToSemi;
+  }
+  elsif ((($valid, $token) = &matchKW("extern\\s*\\\"C\\\"")) && $valid) {
+    &matchRBracket;
+    while (!&matchLBracket) {
+      &parseDeclaration( '' ) || die "Unmatched brace! line = $linenumber\n";
+    }
+    &matchSemi;
+  }
+  # elsif ($kw = &matchID) {
+  #   $type = "$kw ";
+  #
+  #   if ($kw =~/virtual|static|const|volatile/) {
+  #	$type .= &typ;
+  #   }
+  # }
+  elsif ((($valid, $decl) = &matchDecl) && $valid) {
+    my ($instanceClass) = "";
+    
+    # print STDERR "DECLARATION=$decl, REST=$_, baseScope=$baseScope\n";
+
+    return 1 if ($decl =~ /^\s*$/);
+
+    if (!($class)) {
+      if ($decl =~ s/(\S*\s*)(\S+)\:\:(\S+)\s*$/$1$3/) {
+        $instanceClass = $2;
+      }
+    }
+
+    # Eliminate in-line comments
+    &removeComment;
+    
+    # Check for multi-line declaration
+    while ((($valid, $d) = &matchDecl) && $valid) { $decl .= $d; }
+    
+    # Handle template args, but don't let operator overloading confuse us!
+    $tempArgs = '';
+    if (!($decl =~ /\boperator\b/) && ($tempArgs = &matchAngleArgs)) {
+      $tempArgs = $decl . $tempArgs;
+      $decl = '';
+      while ((($valid, $d) = &matchDecl) && $valid) { $decl .= $d; }
+    }
+    
+    # Look for (*name) syntax
+    &parseParenPointer;
+    
+    # Special handling for operator... syntax
+    $oper = "";
+    if ($decl =~ s/\boperator\b(.*)/operator/) {
+      $oper = $1;
+      $oper .= &matchOper;
+      # If, after all that there's no opers, then try a () operator
+      if (!($oper =~ /\S/)) { $oper .= &matchFuncOper; }
+    }
+
+    ($type,$mod,$decl) = $decl =~ /([\s\w]*)([\s\*\&]+\s?)(\~?\w+(\[.*\])*)/;
+    
+    if ($parenPointerFunction) {
+      $decl=$parenPointerFunction;
+    }
+    
+    $type = $tempArgs . $type;
+    $decl .= $oper;
+    
+    if ($mod =~ /\s/) { $type .= $mod; $mod = ""; }
+    
+    for (;;) {
+      
+      # print STDERR "Looping: $type/$mod/$decl\n";
+      
+      if (&matchRParen) {
+	$nest = 1;
+	$args = "";
+	
+	for (;;) {
+	  # print STDERR "Argloop $_\n";
+	  
+	  # Process argument lists.
+	  
+	  # Preserve spaces, eliminate in-line comments
+	  # REM: Change this to save inline comments and automatically
+	  # generate @param clauses
+	  s|//.*||;
+	  while (s/^(\s+)//) { $args .= " "; &rdln; }
+	  
+	  if (&matchRParen) { $nest++; $args .= "("; }
+	  elsif (&matchLParen) {
+	    $nest--;
+	    last if !$nest;
+	    $args .= ")";
+	  }
+	  elsif ((($valid, $d) = &matchKW( "[\,\=\.\:\-]" )) && $valid) { $args .= $d; }
+	  elsif ((($valid, $d) = &matchDecl) && $valid) { $args .= $d; }
+	  elsif ((($valid, $d) = &matchAngleArgs) && $valid) { $args .= $d; }
+	  elsif ((($valid, $d) = &matchString) && $valid) { $args .= "\"$d\""; }
+	  else { last; }
+	}
+				
+	# print STDERR "$type$mod$baseScope$decl($args);\n";
+	
+	&matchKW( "const" );
+	
+	# Search for any text within the name field
+	# if ($docTag && $decl =~ /\W*(~?\w*).*/)
+	if ($docEmpty == 0) {
+	  $type =~ s/^\s+//;
+	  $mod  =~ s/\&/\&amp;/g;
+	  $args =~ s/\&/\&amp;/g;
+	  $args =~ s/\s+/ /g;
+	  $dbname = &uniqueName( "$baseScope$decl" );
+	  
+	  my $entry = { 'type'    => 'func',
+			'name'    => $decl,
+			'longname'=> "$decl()",
+			'fullname'=> "$type$mod$decl($args)",
+			'scopename'=>"$type$mod$baseScope$decl($args)",
+			'uname'   => $dbname,
+			'decl'    => "$type$mod$decl($args)",
+			'package' => $packageName };
+	  
+	  bless $entry, MemberRecord;
+	  
+	  if ($class) {
+	    $entry->{ 'class' } = "$context";
+	    $class->{ 'members' }{ $dbname } = $entry;
+	  }
+	  elsif ($instanceClass) {
+	    $class = &classRecord ($instanceClass);
+	    if (!($class)) {
+	      print STDERR "WARNING: Skipping \"$instanceClass\:\:$decl\".  Class \"$instanceClass\" not declared ($linenumber).\n";
+	    } else {
+	      $entry->{ 'class' } = "$instanceClass";
+	      $class->{ 'members' }{ $dbname } = $entry;
+	      $class = 0;
+	    }
+	  }
+	  else {
+	    $packages{ $packageName }{ 'globals' }{ $dbname } = $entry;
+	  }
+	  &dumpComments( $entry );
+	}
+	else { &clearComments; }
+	
+	s|//.*||;
+	
+	# Constructor super-call syntax
+	if (&matchColon) {
+	  
+	  # Skip over it.
+	  for (;;) {
+	    &rdln;
+	    last if /^\s*(\{|\;)/;
+	    last if !((($valid,)=&matchAny) && $valid);
+	  }
+	}
+	
+	last if &matchSemi;
+	if (&matchRBracket) { &skipBody; last; }
+	last if !&matchComma;
+	last if !((($valid, $decl) = &matchDecl) && $valid);
+	
+	# Look for (*name) syntax
+	&parseParenPointer;
+	
+	$decl =~ s/^\s*//;
+	$oper = "";
+	if ($decl =~ /\boperator\b/) {
+	  $decl =~ s/\boperator\b(.*)/operator/;
+	  $oper = $1 . &matchOper;
+	}
+	($mod,$d) = $decl =~ /^\s*([\*\&]*)\s*(\~?\w+(\[.*\])*)/;
+	$decl .= $oper;
+	$decl = $d if $d ne "";
+      }
+      else {
+	s|//.*||;
+	
+	$final = 0;
+	
+	if ((($valid,)=&matchKW( "\=" )) && $valid) {
+	  for (;;) {
+	    
+	    if (&matchRBracket) {
+	      &skipBody;
+	      $final = 1;
+	      last;
+	    }
+	    
+	    if (&matchSemi) {
+	      $final = 1;
+	      last;
+	    }
+	    
+	    # var = new ... (...)
+	    if ((($valid,)=&matchKW("new")) && $valid) {
+	      &matchKW("[A-Za-z_0-9 ]*");
+	      if (&matchRParen) {
+	        &skipParenBody;
+	      }
+	    }
+	    
+	    # var = (.....) ...
+	    if (&matchRParen) {
+	      &skipParenBody;
+	    }
+	    
+	    # var = ... * ...
+	    &matchKW ("[\/\*\-\+]*");
+	    
+	    # var = "..."
+	    if ((($valid,) = &matchKW ("[\"]")) && $valid) {
+	      &skipString;
+	    }
+	    #&matchString;
+	    
+	    last if /^\s*,/;
+	    #last if !((($valid,)=&matchAny) && $valid);
+	    last if !((($valid,)=&matchKW("[A-Za-z_0-9 \-]*")) && $valid);
+	    if (&matchSemi) {
+	        $final = 1;
+	        last;
+	    }
+	  }
+	}
+	
+	s|//.*||;
+	
+	# void ~*&foo[];
+	# void foo[];
+	# void far*foo[];
+	# print STDERR "Decl: $type$mod$baseScope$decl;\n";
+
+	# Search for any text within the name field
+	if ($docEmpty == 0 && ($decl =~ /\W*(~?\w*).*/))
+	  {
+	    $mod  =~ s/\&/\&amp;/g;
+	    $name = $decl;
+	    
+	    $dbname = &uniqueName( "$baseScope$1" );
+	    
+	    my $entry = { 'type'     => 'var',
+			  'name'     => $1,
+			  'longname' => "$name",
+			  'fullname' => "$type$mod$decl",
+			  'scopename'=> "$baseScope$type$mod$decl",
+			  'uname'    => $dbname,
+			  'decl'     => "$type$mod$decl",
+			  'package'  => $packageName };
+	    
+	    bless $entry, MemberRecord;
+	    
+	    if ($class) {
+	      $entry->{ 'class' } = "$context";
+	      $class->{ 'members' }{ $dbname } = $entry;
+	    }
+	    else {
+	      $packages{ $packageName }{ 'globals' }{ $dbname } = $entry;
+	    }
+	    &dumpComments( $entry );
+	  }
+	else { &clearComments; }
+	
+	last if $final;
+	last if &matchSemi;
+	last if !&matchComma;
+	last if !((($valid, $decl) = &matchDecl) && $valid);
+	
+	# Look for (*name) syntax
+	&parseParenPointer;
+	
+	$decl =~ s/^\s*//;
+	($mod,$d) = $decl =~ /^\s*([\*\&]*)(\~?\w+(\[.*\])*)/;
+	$decl = $d if $d ne "";
+      }
+    }
+  }
+  elsif ($context ne "" && /^\s*\}/) {
+    # print STDERR "Popping!\n";
+    return 1;
+  }
+  elsif (&matchRBracket) {
+    &skipBody;
+  }
+  elsif ((($valid, $token) = &matchAny) && $valid) {
+    # Comment in for debugging
+    # print STDERR "token: $token \n";
+  }
+  else { return 0; }
+  
+  return 1;
+}
+
+# read a file into a string ( filename, default-value )
+sub readFile {
+  local ( $filename, $result ) = @_;
+  
+  if ($filename && open( FILE, $filename )) {
+    $result = "";
+    while (<FILE>) { $result .= $_; }
+    close( FILE );
+  }
+  return $result;
+}
+
+# Read the entire document template and translate into PERL code.
+sub readTemplate {
+  local ( $filename ) = @_;
+  $docTemplate = '';
+  $indent = '';
+  $literal = 1;  # We're in literal mode.
+  
+  if (!-e $filename) {
+    if (-e "./templates/$filename") { $filename = "./templates/$filename"; }
+    elsif (-e "../templates/$filename") { $filename = "../templates/$filename"; }
+    else { die "Could not find template '$filename'.\n"; }
+  }
+  
+  open( FILE, $filename ) || die "Error opening '$filename'.\n";
+  while (<FILE>) {
+    last if (/END/);
+    
+    # if we found a code entry.
+    for (;;) {
+      &expandTabs( $_ );
+      if ($literal) {
+	# Check for beginning of code block.
+	if (s/^(.*)\<\<//) {
+	  $line = $1; 
+	  if (substr( $line, 0, length( $indent ) ) eq $indent) {
+	    substr( $line, 0, length( $indent ) ) = '';
+	  }
+	  
+	  if ($line ne '') {
+	    $line =~ s/\"/\\\"/g;
+	    $line =~ s/\$\((\w+)\.(\w+)\)/\" \. \$$1->$2() \. \"/g;
+	    $docTemplate .= "${indent}print\"$line\";";
+	  }
+	  # else { $docTemplate .= "\n"; }
+	  $literal = 0;
+	}
+	else {
+	  if (substr( $_, 0, length( $indent ) ) eq $indent) {
+	    substr( $_, 0, length( $indent ) ) = "";
+	  }
+	  chop;
+	  s/\"/\\\"/g;
+	  s/\$\((\w+)\.(\w+)\)/\" \. \$$1->$2() \. \"/g;
+	  $_ = $indent . "print \"" . $_ . "\\n\";\n";
+	  last;
+	}
+      }
+      else {
+	# Check for beginning of literal block.
+	if (s/^(\s*)\>\>//) {
+	  $indent = $1;
+	  $literal = 1;
+	}
+	elsif (s/^(\s*)(.*)\>\>//) {
+	  $docTemplate .= "$indent$2";
+	  $literal = 1;
+	}
+	else {
+	  last;
+	}
+      }
+    }
+    
+    $docTemplate .= $_;
+  }
+  close( FILE );
+  # print $docTemplate;
+}
+
+# Functions intended to be called from doc template file.
+
+# Open a new output file
+sub file {
+  my $mfile = $_[ 0 ];
+  
+  open( STDOUT, ">$destPath$mfile" ) || die "Error writing to '$mfile'\n";
+}
+
+# return list of package objects
+sub packages {
+  my ($p, @r);
+  @r = ();
+  
+  foreach $p (sort keys %packages) {
+    push @r, $packages{ $p };
+  }
+  return @r;
+}
+
+# return list of source files which have to-do lists
+sub todolistFiles {
+  my ($p, @r);
+  @r = ();
+  
+  foreach $p (sort keys %todolist) {
+    push @r, $p;
+  }
+  return @r;
+}
+
+# return list of tab-delimited to-do-list texts.
+sub todolistEntries {
+  local $_ = $todolist{ $_[0] };
+  s/^\s+//;				# Remove whitespace from beginning
+  s/\s+$/\n/;				# Remove whitespace from end
+  return split( /\n/, $_ );
+}
+
+# Convert package name to URL.
+sub packageURL {
+  my $p = $_[0];
+  
+  if ($p eq 'General') { $p = '.general'; }
+  if ($p eq '') { $p = '.general'; }
+  
+  if (ref $packages{ $p }) {
+    return $packages{ $p }->url();
+  }
+  return 0;
+}
+
+# Get the see-also list for an object
+sub seealsoList {
+  my $self = shift;
+  my ($see, $name, $url, $p, @r);
+  @r = ();
+  
+  if (defined ($self->{ 'see' })) {
+    foreach $_ (split(/\n/,$self->{ 'see' })) {
+      
+      if (/^\<a\s+href/) { # if already an HREF.
+	$name = $_;
+	$url = 0;
+      }
+      elsif (/([^\#]*)\#(.*)/) { # If a package name is present
+	$url = &packageURL( $1 ) . '#' . $2;
+	$name = $2;
+      }
+      else {
+	$name = $_;
+	$url = "#$_";
+	
+	# This doesn't appear to do anything - so I commented it.  (james)
+	# Look up the package in the index and use it to construct the html filename.
+	#if (/^([^\:]*)\:\:(.*)/) {
+	#  $className = ($1 eq '') ? '' : $classToPackage{ $1 };
+	#  $p = $packageToFile{ $className };
+	#  if ($p ne '') {
+	#    $url = &packageURL( $1 ) . '#' . $_;
+	#  }
+	#}
+      }
+      
+      $url =~ s/^\:*//;		# Remove leading colons from name
+      $url =~ s/::/-/g;		# Replace :: with dash
+      
+      my $entry = { 'name' => $name,
+		    'url'  => $url };
+      
+      bless $entry, DocReference;
+      
+      push @r, $entry;
+    }
+  }
+  return @r;
+}
+
+# Class for parsed package
+package PackageRecord;
+
+sub classes {
+  my $self = shift;
+  my $classes = $self->{ 'classes' };
+  return map $classes->{ $_ }, (sort keys %$classes);
+}
+
+sub globals {
+  my $self = shift;
+  my $globals = $self->{ 'globals' };
+  return map $globals->{ $_ }, (sort keys %$globals);
+}
+
+sub globalvars {
+  my $self = shift;
+  my $globals = $self->{ 'globals' };
+  my ($p, @r);
+  @r = ();
+  
+  foreach $p (sort keys %$globals) {
+    my $m = $globals->{ $p };
+    if ($m->{ 'type' } ne 'func') { push @r, $m; }
+  }
+  return @r;
+}
+
+sub globalfuncs {
+  my $self = shift;
+  my $globals = $self->{ 'globals' };
+  my ($p, @r);
+  @r = ();
+  
+  foreach $p (sort keys %$globals) {
+    my $m = $globals->{ $p };
+    if ($m->{ 'type' } eq 'func') { push @r, $m; }
+  }
+  return @r;
+}
+
+sub name {
+  my $self = shift;
+  return $self->{ 'name' };
+}
+
+sub url {
+  my $self = shift;
+  return "default-pkg.html" if ($self->{ 'name' } eq '.general');
+  return $self->{ 'name' } . '.html';
+}
+
+sub anchor {
+  my $self = shift;
+  my $url = $self->{ 'name' };
+  return $url;
+}
+
+# Class for parsed class
+package ClassRecord;
+
+sub keywords    { return ${$_[0]}{ 'keywords' }; }
+sub author      { return ${$_[0]}{ 'author' }; }
+sub version     { return ${$_[0]}{ 'version' }; }
+sub name        { return ${$_[0]}{ 'name' }; }
+sub longname    { return ${$_[0]}{ 'longname' }; }
+sub fullname    { return ${$_[0]}{ 'fullname' }; }
+sub scopename   { return ${$_[0]}{ 'scopename' }; }
+sub sourcefile  { return ${$_[0]}{ 'sourcefile' }; }
+#sub description { return &::processDescription( ${$_[0]}{ 'description' } ); }
+sub description { return ${$_[0]}{ 'description' }; }
+sub seealso     { &::seealsoList( $_[0] ); }
+
+sub url {
+  my $self = shift;
+  return 0 unless $self->{ 'package' };
+  my $pname = ::packageURL( $self->{ 'package' } );
+  my $url = $self->{ 'uname' };
+  $url =~ s/::/-/g;
+  return "$pname#$url";
+}
+
+sub anchor {
+  my $self = shift;
+  my $url = $self->{ 'uname' };
+  $url =~ s/::/-/g;
+  return $url;
+}
+
+sub members {
+  my $self = shift;
+  my $members = $self->{ 'members' };
+  my ($p, @r);
+  @r = ();
+  
+  foreach $p (sort keys %$members) {
+    push @r, $members->{ $p };
+  }
+  return @r;
+}
+
+sub membervars {
+  my $self = shift;
+  my $members = $self->{ 'members' };
+  my ($p, @r);
+  @r = ();
+  
+  foreach $p (sort keys %$members) {
+    my $m = $members->{ $p };
+    if ($m->{ 'type' } ne 'func') { push @r, $m; }
+  }
+  return @r;
+}
+
+sub memberfuncs {
+  my $self = shift;
+  my $members = $self->{ 'members' };
+  my ($p, @r);
+  @r = ();
+  
+  foreach $p (sort keys %$members) {
+    my $m = $members->{ $p };
+    if ($m->{ 'type' } eq 'func') { push @r, $m; }
+  }
+  return @r;
+}
+
+sub baseclasses {
+  my $self = shift;
+  my $bases = $self->{ 'bases' };
+  my ($p, $class, @r);
+  @r = ();
+  
+  foreach $p (@$bases) {
+    
+    unless ($class = $::classList{ $p }) {
+      # It's one we don't know about, so just make something up
+      $class = { 'name'    => $p,
+		 'longname'=> "class $p",
+		 'fullname'=> "class $p",
+		 'scopename'=>"class $p",
+		 'uname'   => $p,
+		 'members' => {} };
+      
+      if ($::classToPackage{ $p }) {
+	$class->{ 'package' } = $::classToPackage{ $p };
+      }
+      
+      bless $class, ClassRecord;
+    }
+    push @r, $class;
+  }
+  return @r;
+}
+
+sub subclasses {
+  my $self = shift;
+  my $subs;
+  my ($p, $class, @r);
+  @r = ();
+  
+  if (defined ($self->{ 'subs' })) {
+    $subs = $self->{ 'subs' };
+    foreach $p (sort @$subs) {
+      $class = $::classList{ $p };
+      push @r, $class;
+    }
+  }
+  return @r;
+}
+
+# Class for parsed class member or global
+package MemberRecord;
+
+sub type         { return ${$_[0]}{ 'type' }; }
+sub keywords     { return ${$_[0]}{ 'keywords' }; }
+sub author       { return ${$_[0]}{ 'author' }; }
+sub version      { return ${$_[0]}{ 'version' }; }
+sub name         { return ${$_[0]}{ 'name' }; }
+sub longname     { return ${$_[0]}{ 'longname' }; }
+sub fullname     { return ${$_[0]}{ 'fullname' }; }
+sub scopename    { return ${$_[0]}{ 'scopename' }; }
+sub returnValue  { return ${$_[0]}{ 'return' }; }
+sub sourcefile   { return ${$_[0]}{ 'sourcefile' }; }
+sub description  { return ${$_[0]}{ 'description' }; }
+sub seealso      { &::seealsoList( $_[0] ); }
+
+sub url {
+  my $self = shift;
+  return 0 unless $self->{ 'package' };
+  my $pname = ::packageURL( $self->{ 'package' } );
+  my $url = $self->{ 'uname' };
+  $url =~ s/::/-/g;
+  return "$pname#$url";
+}
+
+sub anchor {
+  my $self = shift;
+  my $url = $self->{ 'uname' };
+  $url =~ s/::/-/g;
+  $url;
+}
+
+sub params {
+  my $self = shift;
+  my $params = $self->{ 'param' };
+  my @r;
+  @r = ();
+  
+  return 0 unless ($params);
+  
+  my @paramList = split( /\t/, $params );
+  
+  for ($i = 1; $i < $#paramList; $i += 2) {
+    my $entry = { 'name'        => $paramList[ $i ],
+		  'description' => $paramList[ $i + 1 ] };
+    
+    bless $entry, ArgRecord;
+    
+    push @r, $entry;
+  }
+  return @r;
+}
+
+sub exceptions {
+  my $self = shift;
+  my $params = $self->{ 'exception' };
+  my @r;
+  @r = ();
+  
+  return 0 unless ($params);
+  
+  my @paramList = split( /\t/, $params );
+  
+  for ($i = 1; $i < $#paramList; $i += 2) {
+    my $entry = { 'name'        => $paramList[ $i ],
+		  'description' => $paramList[ $i + 1 ] };
+    
+    bless $entry, ArgRecord;
+    
+    push @r, $entry;
+  }
+  return @r;
+}
+
+package ArgRecord;
+sub name        { return ${$_[0]}{ 'name' }; }
+sub description { return ${$_[0]}{ 'description' }; }
+
+package DocReference;
+sub name        { return ${$_[0]}{ 'name' }; }
+sub url         { return ${$_[0]}{ 'url' }; }
diff --git a/connectors/webapp/support/template.pl b/connectors/webapp/support/template.pl
new file mode 100644
index 0000000..7120a0b
--- /dev/null
+++ b/connectors/webapp/support/template.pl
@@ -0,0 +1,676 @@
+<<
+# Scandoc template file.
+#
+# This is an example set of templates that is designed to create several
+# different kinds of index files. It generates a "master index" which intended
+# for use with a frames browser; A "package index" which is the root page of
+# the index, and then "package files" containing documentation for all of the
+# classes within a single package.
+
+###############################################################################
+## Defaults for look and feel
+
+if ($project eq "") { $project = "WebApp Library"; }
+if ($copyright eq "") { $copyright = "2001, The Apache Software Foundation"; }
+if ($body_bgcolor eq "") { $body_bgcolor = '#ffffff'; }
+if ($body_text    eq "") { $body_text    = '#000000'; }
+if ($body_link    eq "") { $body_link    = '#0000ff'; }
+if ($body_vlink   eq "") { $body_vlink   = '#0000ff'; }
+if ($body_alink   eq "") { $body_alink   = '#0000ff'; }
+
+###############################################################################
+# Generate the frameset index                                                 #
+###############################################################################
+
+file "index.html";
+@p = packages();
+$_ = @p[0]->url;
+s/\s/_/g;
+y/[A-Z]/[a-z]/;
+>>
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; iso-8859-1">
+    <title>$project API Documentation</title>
+  </head>
+  <frameset cols="200,*">
+    <frameset rows="150,*">
+      <frame src="packages.html">
+      <frame src="pkg.$_" name="pkg">
+    </frameset>
+    <frame src="doc.$_" name="doc">
+  </frameset>
+</html>
+<<
+
+###############################################################################
+# Generate the packages list                                                  #
+###############################################################################
+
+file "packages.html";
+>>
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; iso-8859-1">
+    <title>$project - Packages List</title>
+  </head>
+  <body link="$body_link" vlink="$body_vlink" alink="$body_alink"
+        bgcolor="$body_bgcolor" text="$body_text">
+    <font size="+1" face="arial,helvetica,sans serif">
+      <nobr><b>$project packages:</b></nobr>
+    </font>
+    <table border="0" cellspacing="0" cellpadding="0">
+      <tr>
+        <td width="10">&nbsp;</td>
+        <td>&nbsp;</td>
+      </tr>
+<<
+
+foreach $p (packages()) {
+    $_ = $p->url;
+    s/\s/_/g;
+    y/[A-Z]/[a-z]/;
+    >>
+          <tr>
+            <td width="10">&nbsp;</td>
+            <td>
+              <font face="arial,helvetica,sans serif">
+                <nobr><a href="pkg.$_" target="pkg">$(p.name)</a></nobr>
+              </font>
+            </td>
+          </tr>
+    <<
+}
+
+>>
+    </table>
+    <hr>
+    <font size="-2" face="arial,helvetica,sans serif">
+      <div align="center">
+        Copyright &copy; $copyright.<br>
+        All Rights Reserved.<br>
+      </div>
+    </font>
+  </body>
+</html>
+<<
+
+###############################################################################
+# Generate the packages table of content for each package                     #
+###############################################################################
+
+foreach $p (packages()) {
+    $_ = $p->url;
+    s/\s/_/g;
+    y/[A-Z]/[a-z]/;
+    file "pkg.$_";
+    >>
+    <html>
+      <head>
+        <meta http-equiv="Content-Type" content="text/html; iso-8859-1">
+        <title>$project - Packages List</title>
+      </head>
+
+      <body link="$body_link" vlink="$body_vlink" alink="$body_alink"
+            bgcolor="$body_bgcolor" text="$body_text">
+        <font size="+1" face="arial,helvetica,sans serif">
+          <nobr><b><a href="doc.$_" target="doc">$(p.name)</a> package:</b></nobr>
+        </font>
+        <table border="0" cellspacing="0" cellpadding="0">
+          <tr>
+            <td width="10">&nbsp;</td>
+            <td>&nbsp;</td>
+          </tr>
+    <<
+
+    # Generate a list of classes included in this package
+    if ($p->classes()) {
+        >>
+          <tr>
+            <td colspan="2">
+              <font face="arial,helvetica,sans serif">
+                <nobr>Classes:</nobr>
+              </font>
+            </td>
+          </tr>
+        <<
+        foreach $e ($p->classes()) {
+            $_ = $e->url;
+            s/\s/_/g;
+            y/[A-Z]/[a-z]/;
+            >>
+                  <tr>
+                    <td width="10">&nbsp;</td>
+                    <td>
+                      <font face="arial,helvetica,sans serif">
+                        <nobr><a href="doc.$_" target="doc">$(e.fullname)</a></nobr>
+                      </font>
+                    </td>
+                  </tr>
+            <<
+        }
+    } else {
+        >>
+              <tr>
+                <td colspan="2">
+                  <font face="arial,helvetica,sans serif">
+                    <nobr><i>No Classes defined.</i></nobr>
+                  </font>
+                </td>
+              </tr>
+        <<
+    }
+
+    # Generate a list of all global functions included in this package
+    >>
+      <tr><td colspan="2">&nbsp;</td></tr>
+    <<
+    if ($p->globalfuncs()) {
+        >>
+              <tr>
+                <td colspan="2">
+                  <font face="arial,helvetica,sans serif">
+                    <nobr>Global Functions:</nobr>
+                  </font>
+                </td>
+              </tr>
+        <<
+        foreach $e ($p->globalfuncs()) {
+            $_ = $e->url;
+            s/\s/_/g;
+            y/[A-Z]/[a-z]/;
+            >>
+                  <tr>
+                    <td width="10">&nbsp;</td>
+                    <td>
+                      <font face="arial,helvetica,sans serif">
+                        <nobr><a href="doc.$_" target="doc">$(e.fullname)</a></nobr>
+                      </font>
+                    </td>
+                  </tr>
+            <<
+        }
+    } else {
+        >>
+              <tr>
+                <td colspan="2">
+                  <font face="arial,helvetica,sans serif">
+                    <nobr><i>No Global Functions defined.</i></nobr>
+                  </font>
+                </td>
+              </tr>
+        <<
+    }
+
+    # Generate a list of all global variables included in this package
+    >>
+      <tr><td colspan="2">&nbsp;</td></tr>
+    <<
+    if ($p->globalvars()) {
+        >>
+              <tr>
+                <td colspan="2">
+                  <font face="arial,helvetica,sans serif">
+                    <nobr>Global Variables:</nobr>
+                  </font>
+                </td>
+              </tr>
+        <<
+        foreach $e ($p->globalvars()) {
+            $_ = $e->url;
+            s/\s/_/g;
+            y/[A-Z]/[a-z]/;
+            >>
+                  <tr>
+                    <td width="10">&nbsp;</td>
+                    <td>
+                      <font face="arial,helvetica,sans serif">
+                        <nobr><a href="doc.$_" target="doc">$(e.fullname)</a></nobr>
+                      </font>
+                    </td>
+                  </tr>
+            <<
+        }
+    } else {
+        >>
+              <tr>
+                <td colspan="2">
+                  <font face="arial,helvetica,sans serif">
+                    <nobr><i>No Global Variables defined.</i></nobr>
+                  </font>
+                </td>
+              </tr>
+        <<
+    }
+
+    # Copyright statement at the bottom
+    >>
+        </table>
+        <hr>
+        <font size="-2" face="arial,helvetica,sans serif">
+          <div align="center">
+            Copyright &copy; $copyright.<br>
+            All Rights Reserved.<br>
+          </div>
+        </font>
+      </body>
+    </html>
+    <<
+}
+
+###############################################################################
+# Generate the packages detailed description for each package                 #
+###############################################################################
+foreach $p (packages()) {
+    $_ = $p->url;
+    s/\s/_/g;
+    y/[A-Z]/[a-z]/;
+    file "doc.$_";
+    >>
+    <html>
+      <head>
+        <meta http-equiv="Content-Type" content="text/html; iso-8859-1">
+        <title>$project - Packages List</title>
+      </head>
+      <body link="$body_link" vlink="$body_vlink" alink="$body_alink"
+            bgcolor="$body_bgcolor" text="$body_text">
+        <table width="100%" cellspacing="0" cellpadding="2" border="1">
+          <tr>
+            <td bgcolor="ccccff">
+              <table border="0" width="100%" cellspacing="0" cellpadding="0">
+                <tr>
+                  <td bgcolor="ccccff" align="left">
+                    <font size="+1" face="arial,helvetica,sans serif">
+                      <b>$project</b>
+                    </font>
+                  </td>
+                  <td bgcolor="ccccff" align="right">
+                    <font size="+1" face="arial,helvetica,sans serif">
+                      <b>$(p.name) package</b>
+                    </font>
+                  </td>
+                </tr>
+              </table>
+            </td>
+          </tr>
+        </table>
+        <br>
+        <table width="100%" cellspacing="0" cellpadding="2" border="1">
+    <<
+
+    # Generate a TOC of all classes at the top of the page
+    if ($p-> classes()) {
+        >>
+              <tr>
+                <td bgcolor="eeeeff" align="left">
+                  <font face="arial,helvetica,sans serif">
+                    <b>Classes</b>
+                  </font>
+                </td>
+              </tr>
+        <<
+        foreach $e ($p->classes()) {
+            $_ = $e->url;
+            s/\s/_/g;
+            y/[A-Z]/[a-z]/;
+            >>
+                  <tr>
+                    <td>
+                      <dl>
+                        <dt>
+                          <font face="arial,helvetica,sans serif">
+                            <nobr><a href="doc.$_">$(e.fullname)</a></nobr>
+                          </font>
+                        </dt>
+            <<
+            if ($e->members()) {
+                foreach $m ($e->members()) {
+                    $_ = $m->url;
+                    s/\s/_/g;
+                    y/[A-Z]/[a-z]/;
+                    >>
+                                <dd>
+                                  <font size="-1" face="arial,helvetica,sans serif">
+                                    <nobr><a href="doc.$_">$(m.fullname)</a></nobr>
+                                  </font>
+                                </dd>
+                    <<
+                }
+            }
+            >>
+                      </dl>
+                    </td>
+                  </tr>
+            <<
+        }
+    }
+
+    # Continue with the global functions TOC
+    if ($p->globalfuncs()) {
+        >>
+            </table>
+            <br>
+            <table width="100%" cellspacing="0" cellpadding="2" border="1">
+              <tr>
+                <td bgcolor="eeeeff" align="left">
+                  <font face="arial,helvetica,sans serif">
+                    <b>Global Functions</b>
+                  </font>
+                </td>
+              </tr>
+        <<
+        foreach $e ($p->globalfuncs()) {
+            $_ = $e->url;
+            s/\s/_/g;
+            y/[A-Z]/[a-z]/;
+            >>
+                  <tr>
+                    <td>
+                      <font face="arial,helvetica,sans serif">
+                        <nobr><a href="doc.$_">$(e.fullname)</a></nobr>
+                      </font>
+                    </td>
+                  </tr>
+            <<
+        }
+    }
+
+    # And then finish with the global variables TOC
+    if ($p->globalvars()) {
+        >>
+            </table>
+            <br>
+            <table width="100%" cellspacing="0" cellpadding="2" border="1">
+              <tr>
+                <td bgcolor="eeeeff" align="left">
+                  <font face="arial,helvetica,sans serif">
+                    <b>Global Variables</b>
+                  </font>
+                </td>
+              </tr>
+        <<
+        foreach $e ($p->globalvars()) {
+            $_ = $e->url;
+            s/\s/_/g;
+            y/[A-Z]/[a-z]/;
+            >>
+                  <tr>
+                    <td>
+                      <font face="arial,helvetica,sans serif">
+                        <nobr><a href="doc.$_">$(e.fullname)</a></nobr>
+                      </font>
+                    </td>
+                  </tr>
+            <<
+        }
+    }
+
+    >>
+        </table>
+        <br>
+    <<
+
+    # Then generate the detail for each class in this package
+    foreach $e ($p->classes()) {
+        $_ = $e->url;
+        s/\s/_/g;
+        y/[A-Z]/[a-z]/;
+        >>
+            <table width="100%" cellspacing="0" cellpadding="2" border="1">
+              <tr>
+                <td bgcolor="ccccff" align="left">
+                  <font size="+1" face="arial,helvetica,sans serif">
+                    <a name="$(e.name)">
+                      <b>Class &quot;$(e.name)&quot; Detail:</b>
+                    </a>
+                  </font>
+                </td>
+              </tr>
+            </table>
+              <font size="+1" face="arial,helvetica,sans serif">
+                <b>$(e.name)</b>
+              </font>
+            <dl>
+              <dt><code>$(e.fullname) {</code></dt>
+        <<
+
+        # Generate a code-like representation of the class
+        if ($e->members()) {
+            foreach $m ($e->members()) {
+                >>
+                      <dd><code>$(m.fullname);</code></dd>
+                <<
+            }
+        }
+        >>
+              <dt><code>};</code></dt>
+            </dl>
+            <p>
+              <font face="arial,helvetica,sans serif">
+                $(e.description)
+              </font>
+            </p>
+        <<
+
+        # If we have functions, output them
+        if ($e->memberfuncs()) {
+            >>
+                <table width="100%" cellspacing="0" cellpadding="2" border="1">
+                  <tr>
+                    <td bgcolor="eeeeff" align="left">
+                      <font face="arial,helvetica,sans serif">
+                        <b>Class &quot;$(e.name)&quot; Functions:</b>
+                      </font>
+                    </td>
+                  </tr>
+                </table>
+            <<
+            foreach $m ($e->memberfuncs()) {
+                $_ = join("-",$e->name,$m->name);
+                s/\s/_/g;
+                y/[A-Z]/[a-z]/;
+                >>
+                    <a name="$_">
+                <<
+                &function($m);
+            }
+        }
+
+        # If we have variables, output them
+        if ($e->membervars()) {
+            >>
+                <table width="100%" cellspacing="0" cellpadding="2" border="1">
+                  <tr>
+                    <td bgcolor="eeeeff" align="left">
+                      <font face="arial,helvetica,sans serif">
+                        <b>Class &quot;$(e.name)&quot; Variables:</b>
+                      </font>
+                    </td>
+                  </tr>
+                </table>
+            <<
+            foreach $m ($e->membervars()) {
+                $_ = join("-",$e->name,$m->name);
+                s/\s/_/g;
+                y/[A-Z]/[a-z]/;
+                >>
+                    <a name="$_">
+                <<
+                &variable($m);
+            }
+        }
+    }
+
+    # Output detailed information for each global function
+    if ($p->globalfuncs()) {
+        >>
+            <table width="100%" cellspacing="0" cellpadding="2" border="1">
+              <tr>
+                <td bgcolor="ccccff" align="left">
+                  <font size="+1" face="arial,helvetica,sans serif">
+                    <b>Global Functions Detail:</b>
+                  </font>
+                </td>
+              </tr>
+            </table>
+        <<
+        foreach $e ($p->globalfuncs()) {
+            $_ = $e->name;
+            s/\s/_/g;
+            y/[A-Z]/[a-z]/;
+            >>
+                <a name="$_">
+            <<
+            &function($e);
+        }
+    }
+
+    # Then write detailed information for each global variable
+    if ($p->globalvars()) {
+        >>
+            <table width="100%" cellspacing="0" cellpadding="2" border="1">
+              <tr>
+                <td bgcolor="ccccff" align="left">
+                  <font size="+1" face="arial,helvetica,sans serif">
+                    <b>Global Variables Detail:</b>
+                  </font>
+                </td>
+              </tr>
+            </table>
+        <<
+        foreach $e ($p->globalvars()) {
+            $_ = $e->name;
+            s/\s/_/g;
+            y/[A-Z]/[a-z]/;
+            >>
+                <a name="$_">
+            <<
+            &variable($e);
+        }
+    }
+
+    >>
+        <font size="-2" face="arial,helvetica,sans serif">
+          <div align="center">
+            Copyright &copy; $copyright.<br>
+            All Rights Reserved.<br>
+            Generated with <a href="$scandocURL">ScanDoc
+            $majorVersion.$minorVersion</a> on $date
+          </div>
+        </font>
+      </body>
+    </html>
+    <<
+}
+
+# Write out the detailed description of a function
+sub function {
+    local ($m) = @_;
+
+    # Output the function name and description
+    >>
+        <font size="+1" face="arial,helvetica,sans serif">
+          <b>$(m.name)</b>
+        </font>
+        <dl>
+          <dt><code>$(m.fullname);</code></dt>
+          <dd>
+            <dl>
+              <dt>
+                <font face="arial,helvetica,sans serif">
+                  $(m.description)
+                </font>
+              </dt>
+    <<
+
+    # Process all parameters (one by one)
+    if ($m->params()) {
+        >>
+                  <dt>
+                    <font face="arial,helvetica,sans serif">
+                      <b>Parameters</b>
+                    </font>
+                  </dt>
+        <<
+        foreach $a ($m->params()) {
+            >>
+                      <dd>
+                        <code>$(a.name)</code> -
+                        <font face="arial,helvetica,sans serif">
+                          $(a.description)
+                        </font>
+                      </dd>
+            <<
+        }
+    }
+
+    # Check for a return value
+    if ($m->returnValue()) {
+        >>
+                  <dt>
+                    <font face="arial,helvetica,sans serif">
+                      <b>Return Value</b>
+                    </font>
+                  </dt>
+                  <dd>
+                    <font face="arial,helvetica,sans serif">
+                      $(m.returnValue)
+                    </font>
+                  </dd>
+        <<
+    }
+
+    # Dig in for exceptions
+    if ($m->exceptions()) {
+        >>
+                  <dt>
+                    <font face="arial,helvetica,sans serif">
+                      <b>Exceptions</b>
+                    </font>
+                  </dt>
+        <<
+        foreach $a ($m->exceptions()) {
+            >>
+                      <dd>
+                        <code>$(a.name)</code>
+                        <font face="arial,helvetica,sans serif">
+                          $(a.description)
+                        </font>
+                      </dd>
+            <<
+        }
+    }
+
+    # Close the list
+    >>
+            </dl>
+          </dd>
+        </dl>
+        <hr>
+    <<
+}
+
+# Write out the detailed description of a variable
+sub variable {
+    local ($m) = @_;
+
+    # Output the variable name and description
+    >>
+        <font size="+1" face="arial,helvetica,sans serif">
+          <b>$(m.name)</b>
+        </font>
+        <dl>
+          <dt><code>$(m.fullname);</code></dt>
+          <dd>
+            <dl>
+              <dt>
+                <font face="arial,helvetica,sans serif">
+                  $(m.description)
+                </font>
+              </dt>
+            </dl>
+          </dd>
+        </dl>
+        <hr>
+    <<
+}
diff --git a/connectors/webapp/support/wa_ant.m4 b/connectors/webapp/support/wa_ant.m4
new file mode 100644
index 0000000..cca9bbb
--- /dev/null
+++ b/connectors/webapp/support/wa_ant.m4
@@ -0,0 +1,95 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl Author Pier Fumagalli <pier@betaversion.org>
+dnl Version $Id$
+dnl --------------------------------------------------------------------------
+
+dnl --------------------------------------------------------------------------
+dnl WA_ANT
+dnl   Locate Apache ANT
+dnl   $1 => Environment variable where the ANT binary? name will be stored
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_ANT],
+  [
+    wa_ant_enabled=""
+    wa_ant_tempval=""
+    AC_MSG_CHECKING([if ant is enabled])
+    AC_ARG_WITH(
+      [ant],
+      [  --with-ant[[=ant]]        the Apache Ant tool to use],
+      [
+        case "${withval}" in
+        ""|"yes"|"YES"|"true"|"TRUE")
+          AC_MSG_RESULT([yes])
+          wa_ant_enabled="yes"
+          wa_ant_tempval=""
+          ;;
+        "no"|"NO"|"false"|"FALSE")
+          AC_MSG_RESULT([no])
+          wa_ant_enabled="no"
+          wa_ant_tempval=""
+          ;;
+        *)
+          AC_MSG_RESULT([yes (${withval})])
+          wa_ant_enabled="yes"
+          WA_PATH_PROG([wa_ant_tempval],[${withval}],[ant])
+          if test -z "${wa_ant_tempval}" ; then
+            AC_MSG_ERROR([${withval} is invalid])
+          fi
+          ;;
+        esac
+      ],[
+        AC_MSG_RESULT([guessing])
+      ])
+
+    if test "${wa_ant_enabled}" = "no" ; then
+      wa_ant_tempval=""
+    else
+      if test -z "${wa_ant_tempval}" ; then
+        WA_PATH_PROG([wa_ant_tempval],[ant],[ant])
+      fi
+      if test -z "${wa_ant_tempval}" ; then
+        WA_PATH_PROG([wa_ant_tempval],[ant.sh],[ant.sh])
+      fi
+      if test -z "${wa_ant_tempval}" ; then
+        if test "${wa_ant_enabled}" = "yes" ; then
+          AC_MSG_ERROR([apache ant cannot be found])
+          exit 1
+        fi
+      fi
+    fi
+
+    if test -n "${wa_ant_tempval}" ; then
+      AC_MSG_CHECKING([if ${wa_ant_tempval} is working])
+
+      wa_ant_enabled=`${wa_ant_tempval} -version`
+      wa_ant_enabled=`echo ${wa_ant_enabled} | sed 's/^Apache Ant/Ant/g'`
+      wa_ant_enabled=`echo ${wa_ant_enabled} | grep "^Ant version"`
+      wa_ant_enabled=`echo ${wa_ant_enabled} | cut -d\  -f3`
+      if test -z "${wa_ant_enabled}" ; then
+        WA_ERROR([ant misconfigured, reconfigure with --without-ant])
+      else
+        AC_MSG_RESULT([${wa_ant_enabled}])
+      fi
+    fi
+    
+    $1="${wa_ant_tempval}"
+    unset wa_ant_tempval
+    unset wa_ant_enabled
+  ])
diff --git a/connectors/webapp/support/wa_apr.m4 b/connectors/webapp/support/wa_apr.m4
new file mode 100644
index 0000000..3de765f
--- /dev/null
+++ b/connectors/webapp/support/wa_apr.m4
@@ -0,0 +1,128 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl Author Pier Fumagalli <pier@betaversion.org>
+dnl Version $Id$
+dnl --------------------------------------------------------------------------
+
+dnl --------------------------------------------------------------------------
+dnl WA_APR
+dnl   Locate the Apache APR source directory.
+dnl   $1 => Environment variable name where the APR directory will be stored
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_APR],
+  [
+    if test -z "${srcdir}" ; then
+      wa_apr_tempval="apr"
+    else
+      wa_apr_tempval="${srcdir}/apr"
+    fi
+    AC_MSG_CHECKING([for apr sources])
+    AC_ARG_WITH(
+      [apr],
+      [  --with-apr[[=apr]]        the Apache Portable Runtime library to use],
+      [
+        case "${withval}" in
+        ""|"yes"|"YES"|"true"|"TRUE")
+          ;;
+        "no"|"NO"|"false"|"FALSE")
+          WA_ERROR([apr library sources required for compilation])
+          ;;
+        *)
+          wa_apr_tempval="${withval}"
+          ;;
+        esac
+      ])
+    AC_MSG_RESULT([${wa_apr_tempval}])
+    WA_PATH_DIR($1,[${wa_apr_tempval}],[apr sources])
+    
+    unset wa_apr_tempval
+  ])
+
+dnl --------------------------------------------------------------------------
+dnl WA_APR_GET
+dnl   Retrieve a value from the configured APR source tree
+dnl   $1 => Environment variable name for the returned value
+dnl   $2 => APR sources directory as returned by WA_APR
+dnl   $3 => APR variable name (found in $2/apr-config)
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_APR_GET],
+  [
+    AC_MSG_CHECKING([for apr $3 variable])
+    if test ! -f "$2/apr-config" ; then
+      WA_ERROR([cannot find apr-config file in $2])
+    fi
+    wa_apr_get_tempval=`cat $2/apr-config | grep "^$3=" 2> /dev/null`
+    if test -z "${wa_apr_get_tempval}" ; then
+      WA_ERROR([value for $3 not specified in $2/apr-config])
+    fi
+    wa_apr_get_tempval=`echo ${wa_apr_get_tempval} | sed 's/^$3="//g'`
+    wa_apr_get_tempval=`echo ${wa_apr_get_tempval} | sed 's/"$//g'`
+    WA_APPEND([$1],[${wa_apr_get_tempval}])
+    AC_MSG_RESULT([${wa_apr_get_tempval}])
+    unset wa_apr_get_tempval
+  ])
+
+dnl --------------------------------------------------------------------------
+dnl WA_APR_LIB
+dnl   Retrieve the name of the library for -l$(APR_LIB)
+dnl   $1 => Environment variable name for the returned value
+dnl   $2 => APR sources directory as returned by WA_APR
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_APR_LIB],
+  [
+    AC_MSG_CHECKING([for apr APR_LIB])
+    if test ! -f "$2/apr-config" ; then
+      WA_ERROR([cannot find apr-config file in $2])
+    fi
+    wa_apr_get_tempval=`$2/apr-config --link-libtool 2> /dev/null`
+    if test -z "${wa_apr_get_tempval}" ; then
+      WA_ERROR([$2/apr-config --link-libtool failed])
+    fi
+    wa_apr_get_tempval=`basename ${wa_apr_get_tempval} |  sed 's/lib//g'`
+    wa_apr_get_tempval=`echo ${wa_apr_get_tempval} | sed 's/\.la//g'`
+    WA_APPEND([$1],[${wa_apr_get_tempval}])
+    AC_MSG_RESULT([${wa_apr_get_tempval}])
+    unset wa_apr_get_tempval
+  ])
+
+dnl --------------------------------------------------------------------------
+dnl WA_APR_LIBNAME
+dnl   Retrieve the complete name of the library.
+dnl   $1 => Environment variable name for the returned value
+dnl   $2 => APR sources directory as returned by WA_APR
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_APR_LIBNAME],
+  [
+    AC_MSG_CHECKING([for apr APR_LIBNAME])
+    if test ! -f "$2/apr-config" ; then
+      WA_ERROR([cannot find apr-config file in $2])
+    fi
+    wa_apr_get_tempval=`$2/apr-config --link-libtool 2> /dev/null`
+    if test -z "${wa_apr_get_tempval}" ; then
+      WA_ERROR([$2/apr-config --link-libtool failed])
+    fi
+    wa_apr_get_tempval=`basename ${wa_apr_get_tempval}`
+    WA_APPEND([$1],[${wa_apr_get_tempval}])
+    AC_MSG_RESULT([${wa_apr_get_tempval}])
+    unset wa_apr_get_tempval
+  ])
+
diff --git a/connectors/webapp/support/wa_apxs.m4 b/connectors/webapp/support/wa_apxs.m4
new file mode 100644
index 0000000..57c0518
--- /dev/null
+++ b/connectors/webapp/support/wa_apxs.m4
@@ -0,0 +1,128 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl Author Pier Fumagalli <pier@betaversion.org>
+dnl Version $Id$
+dnl --------------------------------------------------------------------------
+
+dnl --------------------------------------------------------------------------
+dnl WA_APXS
+dnl   Locate the APXS script
+dnl   $1 => Environment variable where the APXS script name will be stored
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_APXS],
+  [
+    wa_apxs_tempval="apxs"
+    AC_MSG_CHECKING([for apxs name])
+    AC_ARG_WITH(
+      [apxs],
+      [  --with-apxs[[=apxs]]      the Apache APXS utility to use],
+      [
+        case "${withval}" in
+        ""|"yes"|"YES"|"true"|"TRUE")
+          ;;
+        "no"|"NO"|"false"|"FALSE")
+          WA_ERROR([apxs required for compilation])
+          ;;
+        *)
+          wa_apxs_tempval="${withval}"
+          ;;
+        esac
+      ])
+    AC_MSG_RESULT([${wa_apxs_tempval}])
+    WA_PATH_PROG_FAIL([$1],[${wa_apxs_tempval}],[apxs])
+    unset wa_apxs_tempval
+  ])
+
+dnl --------------------------------------------------------------------------
+dnl WA_APXS_CHECK
+dnl   Check that APXS is actually workable, and return its version number
+dnl   $1 => Environment variable where the APXS version will be stored
+dnl   $2 => Name of the APXS script (as returned by WA_APXS)
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_APXS_CHECK],
+  [
+    wa_apxs_check_tempval=""
+    AC_MSG_CHECKING([for apxs version])
+    if grep "STANDARD_MODULE_STUFF" "$2" > /dev/null ; then
+      wa_apxs_check_tempval="1.3"
+    elif grep "STANDARD20_MODULE_STUFF" "$2" > /dev/null ; then
+      wa_apxs_check_tempval="2.0"
+    else
+      WA_ERROR([$2 invalid])
+    fi
+    AC_MSG_RESULT([$2 (${wa_apxs_check_tempval})])
+    $1=${wa_apxs_check_tempval}
+    unset wa_apxs_check_tempval
+    
+    AC_MSG_CHECKING([for apxs sanity])
+    $2 -q CC > /dev/null 2>&1
+    if test "$?" != "0" ; then
+      WA_ERROR([apxs is unworkable])
+    fi
+    if test -z "`$2 -q CC`" ; then
+      WA_ERROR([apxs cannot compile])
+    fi
+    AC_MSG_RESULT([ok])
+  ])
+
+dnl --------------------------------------------------------------------------
+dnl WA_APXS_GET
+dnl   Retrieve a value from APXS
+dnl   $1 => Environment variable where the APXS value will be stored
+dnl   $2 => Name of the APXS script (as returned by WA_APXS)
+dnl   $3 => Name of the APXS value to retrieve
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_APXS_GET],
+  [
+    AC_MSG_CHECKING([for apxs $3 variable])
+    wa_apxs_get_tempval=`"$2" -q "$3" 2> /dev/null`
+    if test "$?" != "0" ; then
+      WA_ERROR([cannot execute $2])
+    fi
+    AC_MSG_RESULT([${wa_apxs_get_tempval}])
+    WA_APPEND([$1],[${wa_apxs_get_tempval}])
+    unset wa_apxs_get_tempval
+  ])
+
+dnl --------------------------------------------------------------------------
+dnl WA_APXS_SERVER
+dnl   Retrieve the HTTP server version via APXS
+dnl   $1 => Environment variable where the info string will be stored
+dnl   $2 => Name of the APXS script (as returned by WA_APXS)
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_APXS_SERVER],
+  [
+    WA_APXS_GET([wa_apxs_server_sbindir],[$2],[SBINDIR])
+    WA_APXS_GET([wa_apxs_server_target],[$2],[TARGET])
+    wa_apxs_server_daemon="${wa_apxs_server_sbindir}/${wa_apxs_server_target}"
+    WA_PATH_PROG_FAIL([wa_apxs_server_daemon],[${wa_apxs_server_daemon}],[httpd])
+    AC_MSG_CHECKING([httpd version])
+    wa_apxs_server_info="`${wa_apxs_server_daemon} -v | head -1`"
+    wa_apxs_server_info="`echo ${wa_apxs_server_info} | cut -d: -f2`"
+    wa_apxs_server_info="`echo ${wa_apxs_server_info}`"
+    AC_MSG_RESULT([${wa_apxs_server_info}])
+    $1="${wa_apxs_server_info}"
+    unset wa_apxs_server_sbindir
+    unset wa_apxs_server_target
+    unset wa_apxs_server_daemon
+    unset wa_apxs_server_info
+  ])
diff --git a/connectors/webapp/support/wa_exec.m4 b/connectors/webapp/support/wa_exec.m4
new file mode 100644
index 0000000..9f37ad1
--- /dev/null
+++ b/connectors/webapp/support/wa_exec.m4
@@ -0,0 +1,87 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl Author Pier Fumagalli <pier@betaversion.org>
+dnl Version $Id$
+dnl --------------------------------------------------------------------------
+
+dnl --------------------------------------------------------------------------
+dnl WA_EXEC
+dnl   Execute a program filtering its output (pretty printing).
+dnl
+dnl   Parameters:
+dnl     $1 => name of the variable containing the return value (error code).
+dnl     $2 => name of the binary/script to invoke
+dnl     $3 => message used for pretty printing output
+dnl     $4 => the directory where the command must be executed
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_EXEC],
+  [
+    wa_exec_curdir="`pwd`"
+    if test -d "$4" ; then
+      cd "$4"
+    else
+      AC_MSG_ERROR([can't switch to directory $4]) 
+    fi
+
+    echo "  invoking \"$2\""
+    echo "  in directory \"$4\""
+    echo "-1" > retvalue.tmp
+
+    set $2
+    wa_exec_file=[$]1
+    if test ! -x "${wa_exec_file}" ; then
+      cd "${wa_exec_curdir}"
+      AC_MSG_ERROR([cannot find or execute \"${wa_exec_file}\" in \"$4\"])
+      exit 1
+    fi
+    unset wa_exec_file
+
+    {
+      $2
+      echo "wa_exec_retvalue $?"
+    } | {
+      wa_exec_ret=0
+      while true ; do
+        read wa_exec_first wa_exec_line
+        if test ! "$?" -eq "0" ; then
+          break
+        else
+          if test "${wa_exec_first}" = "wa_exec_retvalue" ; then
+            wa_exec_ret="${wa_exec_line}"
+          else
+            if test -n "${wa_exec_line}" ; then
+             echo "    $3: ${wa_exec_first} ${wa_exec_line}"
+            fi
+          fi
+        fi
+      done
+      echo "${wa_exec_ret}" > retvalue.tmp
+      unset wa_exec_first
+      unset wa_exec_line
+      unset wa_exec_ret
+    }
+
+    $1="`cat retvalue.tmp`"
+    rm -f retvalue.tmp
+    echo "  execution of \"$2\""
+    echo "  returned with value \"${$1}\""
+
+    cd "${wa_exec_curdir}"
+    unset wa_exec_curdir
+  ])
diff --git a/connectors/webapp/support/wa_perl.m4 b/connectors/webapp/support/wa_perl.m4
new file mode 100644
index 0000000..aa53677
--- /dev/null
+++ b/connectors/webapp/support/wa_perl.m4
@@ -0,0 +1,89 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl Author Pier Fumagalli <pier@betaversion.org>
+dnl Version $Id$
+dnl --------------------------------------------------------------------------
+
+dnl --------------------------------------------------------------------------
+dnl WA_APXS
+dnl   Locate the APXS script
+dnl   $1 => Environment variable where the APXS script name will be stored
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_PERL],
+  [
+    wa_perl_enabled=""
+    wa_perl_tempval=""
+    AC_MSG_CHECKING([if perl is enabled])
+    AC_ARG_WITH(
+      [perl],
+      [  --with-perl[[=perl]]      the Perl interpreter to use],
+      [
+        case "${withval}" in
+        ""|"yes"|"YES"|"true"|"TRUE")
+          AC_MSG_RESULT([yes])
+          wa_perl_enabled="yes"
+          wa_perl_tempval=""
+          ;;
+        "no"|"NO"|"false"|"FALSE")
+          AC_MSG_RESULT([no])
+          wa_perl_enabled="no"
+          wa_perl_tempval=""
+          ;;
+        *)
+          AC_MSG_RESULT([yes (${withval})])
+          wa_perl_enabled="yes"
+          WA_PATH_PROG([wa_perl_tempval],[${withval}],[perl])
+          if test -z "${wa_perl_tempval}" ; then
+            AC_MSG_ERROR([${withval} is invalid])
+          fi
+          ;;
+        esac
+      ],[
+        AC_MSG_RESULT([guessing])
+      ])
+
+    if test "${wa_perl_enabled}" = "no" ; then
+      wa_perl_tempval=""
+    else
+      if test -z "${wa_perl_tempval}" ; then
+        WA_PATH_PROG([wa_perl_tempval],[perl],[perl])
+      fi
+      if test -z "${wa_perl_tempval}" ; then
+        if test "${wa_perl_enabled}" = "yes" ; then
+          AC_MSG_ERROR([perl interpreter cannot be found])
+          exit 1
+        fi
+      fi
+    fi
+
+    if test -n "${wa_perl_tempval}" ; then
+      AC_MSG_CHECKING([if ${wa_perl_tempval} is working])
+
+      wa_perl_enabled=`echo 'printf "%vd\n", $^V;' | ${wa_perl_tempval}`
+      if test -z "${wa_perl_enabled}" ; then
+        WA_ERROR([perl misconfigured, reconfigure with --without-perl])
+      else
+        AC_MSG_RESULT([${wa_perl_enabled}])
+      fi
+    fi
+    
+    $1="${wa_perl_tempval}"
+    unset wa_perl_tempval
+    unset wa_perl_enabled
+  ])
diff --git a/connectors/webapp/support/wa_util.m4 b/connectors/webapp/support/wa_util.m4
new file mode 100644
index 0000000..060522b
--- /dev/null
+++ b/connectors/webapp/support/wa_util.m4
@@ -0,0 +1,179 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl Author Pier Fumagalli <pier@betaversion.org>
+dnl Version $Id$
+dnl --------------------------------------------------------------------------
+
+dnl --------------------------------------------------------------------------
+dnl WA_ERROR
+dnl   Dump an error message and make sure we actually exit.
+dnl   $1 => Message to dump for error.
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_ERROR],
+  [
+    AC_MSG_RESULT([error])
+    AC_MSG_ERROR([$1])
+    exit 1
+  ])
+
+dnl --------------------------------------------------------------------------
+dnl WA_HEADER
+dnl   Dump an extra header to the standard output
+dnl   $1 => Message of the header to dump.
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_HEADER],
+  [
+    AC_MSG_RESULT([])
+    AC_MSG_RESULT([$1])
+  ])
+
+dnl --------------------------------------------------------------------------
+dnl WA_VARIABLE
+dnl   Initialize a substituted (global) variable with a zero-length string.
+dnl   $1 => The environment variable name.
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_VARIABLE],
+  [
+    $1=""
+    AC_SUBST([$1])
+  ])
+
+dnl --------------------------------------------------------------------------
+dnl WA_APPEND
+dnl   Append the extra value to the variable specified if and only if the
+dnl   value is not already in there.
+dnl   $1 => The environment variable name.
+dnl   $2 => The extra value
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_APPEND],
+  [
+    wa_append_tempval="`echo $2`"
+    if test -n "${wa_append_tempval}" ; then
+      if test -z "${$1}" ; then
+        $1="${wa_append_tempval}"
+      else 
+        wa_append_found=""
+        for wa_append_current in ${$1} ; do
+          if test "${wa_append_current}" = "${wa_append_tempval}" ; then
+            wa_append_found="yes"
+          fi
+        done
+        if test -z "${wa_append_found}" ; then
+          $1="${$1} ${wa_append_tempval}"
+        fi
+        unset wa_append_found
+        unset wa_append_current
+      fi
+    fi 
+    unset wa_append_tempval
+  ])
+
+dnl --------------------------------------------------------------------------
+dnl WA_PATH_PROG
+dnl   Resolve the FULL path name of an executable.
+dnl   $1 => The variable where the full path name will be stored.
+dnl   $2 => The executable to resolve.
+dnl   $3 => The description of what we're trying to locate.
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_PATH_PROG],
+  [
+    wa_path_prog_tempval="`echo $2`"
+    if test -x "${wa_path_prog_tempval}" ; then
+      wa_path_prog_tempdir=`dirname "${wa_path_prog_tempval}"`
+      wa_path_prog_tempfil=`basename "${wa_path_prog_tempval}"`
+      WA_PATH_DIR([wa_path_prog_tempdir],[${wa_path_prog_tempdir}],[$3])
+      AC_MSG_CHECKING([for $3])
+      $1="${wa_path_prog_tempdir}/${wa_path_prog_tempfil}"
+      AC_MSG_RESULT([${$1}])
+    else
+      AC_PATH_PROG($1,[${wa_path_prog_tempval}])
+    fi
+  ])
+
+
+dnl --------------------------------------------------------------------------
+dnl WA_PATH_PROG_FAIL
+dnl   Resolve the FULL path name of an executable and fail if not found.
+dnl   $1 => The variable where the full path name will be stored.
+dnl   $2 => The executable to resolve.
+dnl   $3 => The description of what we're trying to locate.
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_PATH_PROG_FAIL],
+  [
+    WA_PATH_PROG([$1],[$2],[$3])
+    AC_MSG_CHECKING([for $3 availability])
+    if test -z "${$1}" ; then
+      AC_MSG_ERROR([cannot find $3 "${wa_apxs_tempval}"])
+      exit 1
+    fi
+    AC_MSG_RESULT([${$1}])
+  ])
+
+
+dnl --------------------------------------------------------------------------
+dnl WA_PATH_DIR
+dnl   Resolve the FULL path name of a directory.
+dnl   $1 => The variable where the full path name will be stored.
+dnl   $2 => The path to resolve.
+dnl   $3 => The description of what we're trying to locate.
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_PATH_DIR],
+  [
+    AC_MSG_CHECKING([for $3 directory path])
+    wa_path_dir_tempval="`echo $2`"
+    if test -d "${wa_path_dir_tempval}" ; then
+      wa_path_dir_curdir="`pwd`"
+      cd "${wa_path_dir_tempval}"
+      wa_path_dir_newdir="`pwd`"
+      $1="${wa_path_dir_newdir}"
+      AC_MSG_RESULT([${wa_path_dir_newdir}])
+      cd "${wa_path_dir_curdir}"
+      unset wa_path_dir_curdir
+      unset wa_path_dir_newdir
+    else
+      WA_ERROR([directory ${wa_path_dir_tempval} not found])
+    fi
+    unset wa_path_dir_tempval
+  ])
+
+dnl --------------------------------------------------------------------------
+dnl WA_HELP
+dnl   Do some autoconf magic.
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_HELP],
+  [
+    ECHO_N="${ECHO_N} + "
+    m4_divert_once(
+      [PARSE_ARGS],
+      [
+        if test "$ac_init_help" = "long" ; then
+          ac_init_help="modified"
+          echo "Configuration of AC_PACKAGE_STRING:"
+          echo ""
+          echo "Usage: [$]0 [[OPTION]]"
+        fi
+      ])
+  ])
diff --git a/connectors/webapp/support/wa_version.m4 b/connectors/webapp/support/wa_version.m4
new file mode 100644
index 0000000..71d4cb7
--- /dev/null
+++ b/connectors/webapp/support/wa_version.m4
@@ -0,0 +1,56 @@
+dnl
+dnl Copyright 1999-2004 The Apache Software Foundation
+dnl
+dnl Licensed under the Apache License, Version 2.0 (the "License");
+dnl you may not use this file except in compliance with the License.
+dnl You may obtain a copy of the License at
+dnl
+dnl     http://www.apache.org/licenses/LICENSE-2.0
+dnl
+dnl Unless required by applicable law or agreed to in writing, software
+dnl distributed under the License is distributed on an "AS IS" BASIS,
+dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+dnl See the License for the specific language governing permissions and
+dnl limitations under the License.
+dnl
+
+dnl --------------------------------------------------------------------------
+dnl Author Pier Fumagalli <pier@betaversion.org>
+dnl Version $Id$
+dnl --------------------------------------------------------------------------
+
+dnl --------------------------------------------------------------------------
+dnl WA_VERSION
+dnl   Retrieve the version of the WebApp module
+dnl   $1 => The variable name where the version number will be stored.
+dnl   $2 => A working C compiler.
+dnl --------------------------------------------------------------------------
+AC_DEFUN(
+  [WA_VERSION],
+  [
+    cat > ${TGT_DIR}/wa_version.c << EOF
+#include "${SRC_DIR}/include/wa_version.h"
+#include "stdio.h"
+
+int main(void) [ {
+  printf(WA_VERSION "\n");
+  exit(0);
+} ]
+EOF
+    AC_MSG_CHECKING([for webapp version])
+    $2 ${TGT_DIR}/wa_version.c -o ${TGT_DIR}/wa_version
+    if test "$?" != "0" ; then
+      AC_MSG_ERROR([compiler didn't run successfully])
+    fi
+    $1=`${TGT_DIR}/wa_version`
+    if test "$?" != "0" ; then
+      AC_MSG_ERROR([cannot execute wa_version.o])
+    fi
+    if test -z "${$1}" ; then
+      AC_MSG_ERROR([empty version number])
+    fi
+    rm -f "${TGT_DIR}/wa_version"
+    rm -f "${TGT_DIR}/wa_version.c"
+    AC_MSG_RESULT([${$1}])
+    
+  ])